【问题标题】:Pandas groupby transform cumulative with conditions熊猫 groupby 变换累积条件
【发布时间】:2020-10-15 13:51:14
【问题描述】:

我有一个包含许多产品 ID 和 iso_codes 的大表:总共 200 万行。所以答案应该(如果可能的话)也考虑到内存问题,我有 16 GB 内存。

我想查看对于每个 (id, iso_code) 组合行中的 buy_date 之前返回的商品数量是多少(如此累积),但是有一个陷阱
我只想计算return_date在我正在查看的buy_date之前的先前销售发生的退货。

我添加了 column items_returned 作为示例:这是应该计算的列。

思路如下:
在销售的那一刻,我只能计算已经发生的退货,而不是将来会发生的退货。

我尝试了df.groupby(['id', 'iso_code']).transform(np.cumsum).transform(lambda x: only count returns that happened before my buy_date) 的组合,但不知道如何在应用这些特殊条件的情况下执行.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 |
+-------+------+------------+----------+------------+---------------+----------------+------------------+

示例代码:

import pandas as pd
from io import StringIO

df_text = """
row id  iso_code    return  buy_date    return_date
0   177 DE  1   2019-05-16  2019-05-24
1   177 DE  1   2019-05-29  2019-06-03
2   177 DE  1   2019-10-27  2019-11-06
3   177 DE  0   2019-11-06  None
4   177 DE  1   2019-11-18  2019-11-28
5   177 DE  1   2019-11-21  2019-12-11
6   177 DE  1   2019-11-25  2019-12-06
7   177 DE  0   2019-11-30  None
8   177 DE  1   2020-04-30  2020-05-27
9   177 DE  1   2020-04-30  2020-09-18
"""

df = pd.read_csv(StringIO(df_text), sep='\t', index_col=0)

df['items_bought'] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 8]
df['items_returned'] = [0, 1, 2, 2, 3, 3, 3, 4, 6, 6]

【问题讨论】:

    标签: python pandas pandas-groupby


    【解决方案1】:

    这似乎需要交叉合并:

    (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
    

    【讨论】:

    • 嗨,Quang,感谢您提供优雅的解决方案。这适用于较小的数据,但是在将其应用于我的 200 万行数据帧时出现内存问题:无法为形状为 (3, 165038559) 和数据类型对象的数组分配 3.69 GiB。
    • 嗨广,谢谢你的更新!我发现 .apply() 解决方案太慢了,需要很长时间才能完成。所以我现在使用您的第一个解决方案:因为我在云中工作,所以我只是将我的虚拟机切换到具有更多内存(32GB)的虚拟机,然后它就可以工作了。我喜欢你在答案中使用方法链:)
    猜你喜欢
    • 1970-01-01
    • 2019-07-26
    • 2021-11-01
    • 1970-01-01
    • 2017-05-15
    • 2019-01-09
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多