我有一个Pandas Dataframe。我使用groupBy
(在1列上)+ apply
组合向数据框添加新列。 apply使用参数调用自定义函数。完整的电话看起来像这样:
df = df.groupby('id').apply(lambda x: customFunction(x,'searchString'))
自定义函数的工作方式如下:根据if
else
条件,新列填充1
或0
。然后返回该组。有点概括,自定义函数如下所示:
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
中输出一些调试文本,我会发现每个组的实际迭代相当快,但最后需要花费更多秒(比迭代本身更长),直到{ {1}}实际上已完成。我认为这与重新索引或重新分配新列中的新数据有关。
我现在的问题是:
groupBy
+ groupBy
需要这么长时间?为什么实际迭代的部分已经完成,需要这么长时间?我已经阅读了返回组,因为它需要很长时间,但我认为在我的情况下这是必要的,因为我在apply
中明确生成了新数据,这需要返回数据。
答案 0 :(得分:2)
df.groupby(...).apply(...)
没有完全矢量化,因为它是一个for .. loop
,它会将指定的函数应用于每个组(在你的情况下,它将被执行1499 + 1次)。
See Notes in the docs describing why Pandas apply will call func twice for the first group:
在当前实现中,在第一个上应用调用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
答案 1 :(得分:2)
这是另一个没有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)