Python 熊猫数据帧加入groupby加速

Python 熊猫数据帧加入groupby加速,python,performance,pandas,dataframe,Python,Performance,Pandas,Dataframe,我正在根据其他列的分组向数据帧添加一些列。我进行了一些分组、计数,最后将结果连接回原始数据帧 完整的数据包括1M行,我第一次尝试了20k行的方法,效果很好。对于客户添加到订单中的每个项目,数据都有一个条目 以下是一个示例数据: import numpy as np import pandas as pd data = np.matrix([[101,201,301],[101,201,302],[101,201,303],[101,202,301],[101,202,302],[101,203,

我正在根据其他列的分组向数据帧添加一些列。我进行了一些分组、计数,最后将结果连接回原始数据帧

完整的数据包括1M行,我第一次尝试了20k行的方法,效果很好。对于客户添加到订单中的每个项目,数据都有一个条目

以下是一个示例数据:

import numpy as np
import pandas as pd
data = np.matrix([[101,201,301],[101,201,302],[101,201,303],[101,202,301],[101,202,302],[101,203,301]])
df = pd.DataFrame(data, columns=['customer_id', 'order_id','item_id'])
df['total_nitems_user_lifetime'] = df.join(df.groupby('customer_id').count()\
      ['order_id'],on='customer_id',rsuffix="_x")['order_id_x']
df['nitems_in_order'] = df.join(df.groupby('order_id').count()\
   ['customer_id'],on='order_id',rsuffix="_x")['customer_id_x']
对于上述样本数据,所需输出为:

|客户id |订单id |商品id |总设备|用户|寿命|订单中的设备| | 101 | 201 | 301 | 6 | 3 | 101 | 201 | 302 | 6 | 3 | 101 | 201 | 303 | 6 | 3 | 101 | 202 | 301 | 6 | 2 | 101 | 202 | 302 | 6 | 2 | 101 | 203 | 301 | 6 | 1 即使有1M行,代码的运行速度也相对较快:

df['total_nitems_user_lifetime'] = df.join(df.groupby('customer_id').count()\
          ['order_id'],on='customer_id',rsuffix="_x")['order_id_x']
但类似的连接需要相当长的时间〜几个小时:

df['nitems_in_order'] = df.join(df.groupby('order_id').count()\
       ['customer_id'],on='order_id',rsuffix="_x")['customer_id_x']

我希望有一种更聪明的方法来获得相同的总价值。我理解为什么在第二种情况下需要很长时间,因为组的数量增加了很多。谢谢你

好的,我可以看到你想要实现的目标,在这个样本大小上,它的速度快了2倍多,而且我认为它的扩展性也可能更好,基本上不是将groupby的结果加入/合并回原始df,只需调用transform:

有趣的是,当我在600000行df上尝试此操作时:

In [34]:

%timeit df['total_nitems_user_lifetime'] = df.groupby('customer_id')['order_id'].transform('count')
%timeit df['nitems_in_order'] = df.groupby('order_id')['customer_id'].transform('count')
10 loops, best of 3: 160 ms per loop
1 loops, best of 3: 231 ms per loop
In [36]:

%timeit df['total_nitems_user_lifetime'] = df.join(df.groupby('customer_id').count()\
      ['order_id'],on='customer_id',rsuffix="_x")['order_id_x']
%timeit df['nitems_in_order'] = df.join(df.groupby('order_id').count()\
   ['customer_id'],on='order_id',rsuffix="_x")['customer_id_x']
10 loops, best of 3: 208 ms per loop
10 loops, best of 3: 215 ms per loop
我的第一种方法大约快25%,但实际上比你的方法慢,我认为值得在你的真实数据上尝试一下,看看它是否能提高速度

如果我们合并列创建,使其位于一行上:

In [40]:

%timeit df['total_nitems_user_lifetime'], df['nitems_in_order'] = df.groupby('customer_id')['order_id'].transform('count'),  df.groupby('order_id')['customer_id'].transform('count')
1 loops, best of 3: 425 ms per loop
In [42]:

%timeit df['total_nitems_user_lifetime'], df['nitems_in_order'] = df.join(df.groupby('customer_id').count()\
      ['order_id'],on='customer_id',rsuffix="_x")['order_id_x'] , df.join(df.groupby('order_id').count()\
   ['customer_id'],on='order_id',rsuffix="_x")['customer_id_x']
1 loops, best of 3: 447 ms per loop

我们可以看到,我的组合代码略快于您的代码,因此这样做不会节省太多时间,通常您可以应用多个聚合函数,以便返回多个列,但这里的问题是,您按不同的列分组,因此我们必须执行两个昂贵的groupby操作。

原始方法,有1M行:

df['nitems_in_order'] = df.join(df.groupby('order_id').count()\
                       ['customer_id'],on='order_id',rsuffix="_x")['customer_id_x']
time:  0:00:02.422288
@EdChum提出的转变建议:

df['nitems_in_order'] = df.groupby('order_id')['customer_id'].transform('count')
time: 0:00:04.713601
使用groupby,然后选择一列,然后计数,转换回dataframe,最后加入。结果:更快:

df = df.join(df.groupby(['order_id'])['order_id'].count().to_frame('nitems_in_order'),on='order_id')
time: 0:00:0.406383

谢谢

你能发布示例数据和预期输出吗?目前我只能猜测join操作看起来没有必要,但是groupby是一个昂贵的操作。谢谢你@EdChum我编辑了这篇文章来添加示例数据和代码。谢谢Ed,我尝试了转换的想法,事实上需要更长的时间。。。当我玩的时候,我找到了一种让它跑得更快的方法。。。我会把答案贴在下面。非常感谢您的帮助,它给了我一些好的想法。@ab3是的,请发布您的答案并记住接受它,很高兴我能提供一些新的想法
df = df.join(df.groupby(['order_id'])['order_id'].count().to_frame('nitems_in_order'),on='order_id')
time: 0:00:0.406383