根据输入频率生成大量句子

时间:2018-10-11 14:40:55

标签: python pandas dataframe

我的目标是根据输入的频率生成句子。例如,我有这样的输入:

>>> df = pd.DataFrame({"s":["a", "a", "b", "b", "c", "c"], "m":[["x", "y"], ["x", "z"], ["y", "w", "z"], ["y"], ["z"], ["z"]]})
>>> df.set_index("s")
>>> df
           m
s
a     [x, y]
a     [x, z]
b  [y, w, z]
b        [y]
c        [z]
c        [z]

我想要一个函数gen_sentence(s),该函数采用s并根据m列中字母的频率生成随机的非空句子。因此,gen_sentence("a")应该生成所有包含x的句子,其中50%应该包含y和50%z

我的直觉是将DataFrame转换为频率的DataFrame,因此对于这样的示例:

     w  x    y    z
s
a  0.0  1  0.5  0.5
b  0.5  0  1.0  0.5
c  0.0  0  0.0  1.0

然后在给定s的情况下为每列滚动一个随机数:

def gen_sentence(fdf, s):
    return fdf.columns[np.random.random(len(fdf.columns)) < fdf.loc[s]]

但是,我不知道如何在频率DataFrame中转换DataFrame。

解决方案可能是使用df.agg["s"],但是我对聚合应用什么功能?

实际上,数据集非常大,有超过一百万行,m中约有500个不同的单词,而s约有100个不同的值,并且频率表将稀疏:大多数{{1}对于s中的大多数单词,}的频率为零。此外,我需要至少生成几十万个句子,因此我试图找到一种实现可以尽快生成一个句子的实现。另外,该解决方案不必使用Panda的解决方案,我只是认为大多数功能的矢量化实现是最快的解决方案。


因此,简而言之,首先,如何将DataFrame转换为频率DataFrame,其次,是否有一种更快的生成句子的方法?


我已经测试了我的实现,看它是否足够快并且是否不错:一个频率为500行100列的DataFrame可以在我的机器上约1.2秒内生成5000个句子。

如果您想针对我的方法测试自己的方法,这是我的测试:

m

2 个答案:

答案 0 :(得分:1)

要转换为频率数据帧,请尝试以下方法(不是最佳解决方案,但它可以工作):

for letter in ['x', 'y', 'w', 'z']:
    df.loc[:, letter] = df.m.apply(lambda x: x.count(letter))

df = df.drop(['m'], axis=1)

df_1 = df.groupby('s').agg(lambda x: sum(x)).reset_index()

print(df_1)

输出:

   s  x  y  w  z
0  a  2  1  0  1
1  b  0  2  1  1
2  c  0  0  0  2

另一种选择(使用forstack的{​​{1}}循环):

pivot_table

输出:

import numpy as np
df_1 = (df.m.apply(pd.Series).stack().to_frame('m')).reset_index().set_index('level_0')['m']
df_1 = pd.concat([df['s'], df_1], axis=1).reset_index()[['s', 'm']]
df_1.insert(1, 'freq', 1)
df_1 = pd.pivot_table(df_1, values='freq', index='s', columns='m', aggfunc=np.sum).fillna(0)
df_1 = df_1.div(df_1.max(axis=1), axis=0)
df_1.columns.name=None

print(df_1)

答案 1 :(得分:0)

在阿拉(Alla Tarighati)的帮助下,我现在有了问题的第一部分解决方案:

letters = set(x for l in df["m"] for x in l)
for letter in letters:
    df.loc[:, letter] = df.m.apply(lambda x: letter in x)

df = df.drop(["m"], axis=1)

gdf = df.groupby("s")
fdf = gdf.agg(lambda x: sum(x))
fdf = fdf.divide(gdf.size(), axis="index")
print(fdf)

输出:

     y    x    z    w
s                    
a  0.5  1.0  0.5  0.0
b  1.0  0.0  0.5  0.5
c  0.0  0.0  1.0  0.0

请注意,在第三行中,我将lambda函数更改为letter in x,以使句子中的重复字母不会被重复计数。

和Alla Tarighati一样,这不是一个非常快的解决方案,因此欢迎进行改进!