Python 带累积条件的分组变换

Python 带累积条件的分组变换,python,pandas,pandas-groupby,Python,Pandas,Pandas Groupby,我有一个包含许多产品id和iso_代码的大表:总共200万行。因此,答案(如果可能)还应考虑内存问题,我有16 GB内存。 我想看看每一个(id,iso_代码)组合在购买日期之前返回的物品数量在行中(如此累积),但有一个陷阱: 我只想计算退货日期早于我看到的购买日期的以前销售中发生的退货。 我添加了列项目\u返回作为示例:这是应该计算的列。 想法如下: 在销售时,我只能计算已经发生的退货,而不能计算将来将发生的退货。 我尝试了df.groupby(['id',iso_code']).tran

我有一个包含许多产品id和iso_代码的大表:总共200万行。因此,答案(如果可能)还应考虑内存问题,我有16 GB内存。

我想看看每一个(id,iso_代码)组合在购买日期之前返回的物品数量行中(如此累积),但有一个陷阱:
我只想计算退货日期早于我看到的购买日期的以前销售中发生的退货。

我添加了列项目\u返回作为示例:这是应该计算的列。

想法如下:
在销售时,我只能计算已经发生的退货,而不能计算将来将发生的退货。

我尝试了
df.groupby(['id',iso_code']).transform(np.cumsum)
.transform(lambda x:仅计算在购买日期之前发生的退货)
,但在应用这些特殊条件的情况下,我无法想出如何进行
.groupby.transform(np.cumsum)

对于购买的物品也有类似的问题,我只计算比购买日期短的天数内的累计物品

希望你能帮助我

结果表示例:

+-------+------+------------+----------+------------+---------------+----------------+------------------+
|   row |   id | iso_code   |   return | buy_date   | return_date   |   items_bought |   items_returned |
|-------+------+------------+----------+------------+---------------+----------------+------------------|
|     0 |  177 | DE         |        1 | 2019-05-16 | 2019-05-24    |              0 |                0 |
|     1 |  177 | DE         |        1 | 2019-05-29 | 2019-06-03    |              1 |                1 |
|     2 |  177 | DE         |        1 | 2019-10-27 | 2019-11-06    |              2 |                2 |
|     3 |  177 | DE         |        0 | 2019-11-06 | None          |              3 |                2 |
|     4 |  177 | DE         |        1 | 2019-11-18 | 2019-11-28    |              4 |                3 |
|     5 |  177 | DE         |        1 | 2019-11-21 | 2019-12-11    |              5 |                3 |
|     6 |  177 | DE         |        1 | 2019-11-25 | 2019-12-06    |              6 |                3 |
|     7 |  177 | DE         |        0 | 2019-11-30 | None          |              7 |                4 |
|     8 |  177 | DE         |        1 | 2020-04-30 | 2020-05-27    |              8 |                6 |
|     9 |  177 | DE         |        1 | 2020-04-30 | 2020-09-18    |              8 |                6 |
+-------+------+------------+----------+------------+---------------+----------------+------------------+
示例代码:

+-------+------+------------+----------+------------+---------------+----------------+------------------+
|   row |   id | iso_code   |   return | buy_date   | return_date   |   items_bought |   items_returned |
|-------+------+------------+----------+------------+---------------+----------------+------------------|
|     0 |  177 | DE         |        1 | 2019-05-16 | 2019-05-24    |              0 |                0 |
|     1 |  177 | DE         |        1 | 2019-05-29 | 2019-06-03    |              1 |                1 |
|     2 |  177 | DE         |        1 | 2019-10-27 | 2019-11-06    |              2 |                2 |
|     3 |  177 | DE         |        0 | 2019-11-06 | None          |              3 |                2 |
|     4 |  177 | DE         |        1 | 2019-11-18 | 2019-11-28    |              4 |                3 |
|     5 |  177 | DE         |        1 | 2019-11-21 | 2019-12-11    |              5 |                3 |
|     6 |  177 | DE         |        1 | 2019-11-25 | 2019-12-06    |              6 |                3 |
|     7 |  177 | DE         |        0 | 2019-11-30 | None          |              7 |                4 |
|     8 |  177 | DE         |        1 | 2020-04-30 | 2020-05-27    |              8 |                6 |
|     9 |  177 | DE         |        1 | 2020-04-30 | 2020-09-18    |              8 |                6 |
+-------+------+------------+----------+------------+---------------+----------------+------------------+
将熊猫作为pd导入
从io导入StringIO
df_text=“”
行id iso_代码退货购买日期退货日期
0177 DE 1 2019-05-16 2019-05-24
1177 DE 1199-05-29 2019-06-03
2 177 DE 1 2019-10-27 2019-11-06
3177 DE 0 2019-11-06无
4177 DE 1 2019-11-18 2019-11-28
5177 DE 1 2019-11-21 2019-12-11
6177 DE 1 2019-11-25 2019-12-06
7 177 DE 0 2019-11-30无
8177 DE 112020-04-302020-05-27
9 177 DE 1 2020-04-30 2020-09-18
"""
df=pd.read\u csv(StringIO(df\u text),sep='\t',index\u col=0)
df['items_bunded']=[0,1,2,3,4,5,6,7,8,8]
df['items_returned']=[0,1,2,2,3,3,3,4,6]

这似乎需要交叉合并:

(df[['id','iso_code', 'buy_date']].reset_index()
   .merge(df[['id','iso_code', 'return','return_date','buy_date']], on=['id','iso_code'])
   .assign(items_returned=lambda x: x['return_date'].lt(x['buy_date_x'])*x['return'],
           items_bought=lambda x: x['buy_date_y'].lt(x['buy_date_x']))
   .groupby('row')[['items_bought','items_returned']].sum()
)
输出:

     items_bought  items_returned
row                              
0               0               0
1               1               1
2               2               2
3               3               2
4               4               3
5               5               3
6               6               3
7               7               4
8               8               6
9               8               6

更新对于较大的数据,由于内存需求,交叉合并并不理想。然后我们可以执行一个
groupby()
,这样我们只合并较小的组:

def myfunc(df):
    return (df[['id','iso_code', 'buy_date']].reset_index()
   .merge(df[['id','iso_code', 'return','return_date','buy_date']], on=['id','iso_code'])
   .assign(items_returned=lambda x: x['return_date'].lt(x['buy_date_x'])*x['return'],
           items_bought=lambda x: x['buy_date_y'].lt(x['buy_date_x']))
   .groupby('row')[['items_bought','items_returned']].sum()
)

df.groupby(['id','iso_code']).apply(myfunc).reset_index(level=[0,1], drop=True)
您将获得相同的输出:

     items_bought  items_returned
row                              
0               0               0
1               1               1
2               2               2
3               3               2
4               4               3
5               5               3
6               6               3
7               7               4
8               8               6
9               8               6

嗨,广,谢谢你优雅的解决方案。这适用于较小的数据,但当将其应用于我的200万行数据帧时,我会遇到内存问题:无法为具有形状(316038559)和数据类型对象的数组分配3.69 GiB。您好,谢谢您的更新!我发现.apply()解决方案太慢,需要很长时间才能完成。因此,我现在使用您的第一个解决方案:因为我在云中工作,我只是将我的虚拟机切换到一个具有更多内存(32GB)的虚拟机,然后它就可以工作了。我喜欢你在回答中使用方法链接:)