繁体   English   中英

熊猫数据框groupby + Apply +新列很慢

[英]Pandas dataframe groupby + apply + new column is slow

我有一个熊猫数据框。 我使用groupBy (在1列上)+ apply组合以将新列添加到数据框。 Apply调用带有参数的自定义函数。 完整的调用如下所示:

df = df.groupby('id').apply(lambda x: customFunction(x,'searchString'))

自定义函数的工作方式如下:根据if else条件,新列将填充10 然后返回该组。 有点概括,自定义函数如下所示:

def customFunction(group,searchString):
    #print(group.iloc[[0]]['id'].values[0])
    if len(group[(group['name'] == searchString)) > 0:
        group['newColumn'] = 1
    else:
        group['newColumn'] = 0
    return group

我的问题是,即使我不处理太多数据,该脚本也会运行相对较长的时间。 这些是我的数据的统计数据:数据框具有3130行和49列。 groupBy生成1499个单独的组。

如果我在customFunction输出了一些调试文本, customFunction观察到通过每个组进行的实际迭代速度相当快,但是最后,要花费更长时间(比迭代本身更长的时间),直到groupBy实际完成为止。 我认为这与从新列重新索引或重新分配新数据有关。

我的问题是:

  • 为什么groupBy + apply需要这么长时间? 为什么实际迭代已经完成的部分要花这么长时间?
  • 如何避免这个瓶颈? 如何改善我的代码(见上文)以更快地执行?
  • 更笼统地说:如何最有效地实施“ 按特定列分组,然后根据条件添加新列 ”的模式? 也许一种方法是创建单独的数据结构,而无需返回组。 然后,在一个单独的步骤中,可以将新计算的数据结构与原始数据帧结合在一起。 但是,我不确定这是否会更好。

我应该避免读返回组,因为它会花费很长时间,但是我认为这是必须的,因为我在我的customFunction显式生成了新数据,这就需要返回数据。

df.groupby(...).apply(...)尚未完全向量化,因为在df.groupby(...).apply(...)它是for .. loop ,它将对每个组应用指定的功能(在您的情况下,它将执行1499 + 1次)。

请参阅说明中为什么要应用熊猫的文档中的注释 ,将为第一组调用func两次

在当前的实现中,在第一个组上两次应用func调用,以确定它可以采用快速还是慢速代码路径。 如果func有副作用,这可能导致意外的行为,因为它们将对第一组生效两次。

建议首先使用向量化函数,如果无法使用.apply()作为最后的解决方案,则寻求解决方案。

IIUC可以使用以下矢量化方法:

In [43]: df
Out[43]:
   id name
0   1  aaa
1   1  bbb
2   1  aaa
3   2  ccc
4   2  bbb
5   2  ccc
6   3  aaa

In [44]: searchString = 'aaa'

In [45]: df['newColumn'] = df.groupby('id')['name'] \
                             .transform(lambda x: x.eq(searchString).any().astype(int))

In [46]: df
Out[46]:
   id name  newColumn
0   1  aaa          1
1   1  bbb          1
2   1  aaa          1
3   2  ccc          0
4   2  bbb          0
5   2  ccc          0
6   3  aaa          1

70.000行DF的计时

In [56]: df = pd.concat([df] * 10**4, ignore_index=True)

In [57]: df.shape
Out[57]: (70000, 2)

In [58]: %timeit df.groupby('id').apply(lambda x: customFunction(x,searchString))
10 loops, best of 3: 92.4 ms per loop

In [59]: %timeit df.groupby('id')['name'].transform(lambda x: x.eq(searchString).any().astype(int))
10 loops, best of 3: 53.5 ms per loop

这是没有groupby另一种更有效的解决方案(针对此特定情况)

>> searchString = 'searchString'
>> df = pd.DataFrame({'id': np.random.choice(1000, 1000000)})
>> df['name'] = random_names  # 1000000 random strings of len 10
>> df.loc[np.random.choice(1000000, 1000, replace=False), 'name'] = searchString
>>
>> def solution_0(x):
>>   x = x.groupby('id').apply(lambda g: customFunction(g, searchString))
>>
>> def solution_1(x):
>>   x['newColumn'] = x.groupby('id')['name'].transform(lambda g: g.eq(searchString).any().astype(int))
>>
>> def solution_2(x):
>>   x['newColumn'] = 0
>>   x.loc[x['id'].isin(x.loc[x['name'] == searchString, 'id']), 'newColumn'] = 1
>> 
>> %timeit solution_0(df)
3.4 s ± 125 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>> %timeit solution_1(df)
1.47 s ± 56.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>> %timeit solution_2(df)
129 ms ± 4.33 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM