python - 如何在Python中结合groupby、rolling和多列创建

标签 python pandas janitor

我有一个问题,可以使用 R 中的 dplyr 轻松解决,但似乎无法在 Python 中找到简单的方法。 我有一个 df,其中 id(=customerid)、s(=store)、m(=month) 和 ttl(=total buy) 作为列。 我想计算 id+s 上的多个新列 - 例如最近 3 个月的购买量和最低购买量。

示例(最后两列是新列):

id  s         m   ttl ttl_3 min_id_s
1   A   1/1/2020    7   nan 3
1   A   2/1/2020    3   nan 3
1   A   3/1/2020    7   17  3
1   A   4/1/2020    6   16  3
1   A   5/1/2020    7   20  3
1   A   6/1/2020    7   20  3
1   B   1/1/2020    6   nan 6
1   B   2/1/2020    10  nan 6
1   B   3/1/2020    8   24  6
1   B   4/1/2020    8   26  6
1   B   5/1/2020    10  26  6
1   B   6/1/2020    8   26  6
2   A   1/1/2020    4   nan 1
2   A   2/1/2020    3   15  1
2   A   3/1/2020    10  17  1
2   A   4/1/2020    6   19  1
2   A   5/1/2020    4   20  1
2   A   6/1/2020    1   11  1

我尝试过以下方法:

grp = df.groupby(['id','s'])
df = df.assign(ttl_3 = grp['ttl'].apply(lambda x: x.rolling(window=3)).sum(), min_id_s = grp['ttl'].min())

我收到以下错误:

Cannot access callable attribute 'assign' of 'DataFrameGroupBy' objects, try using the 'apply' method

我知道它可以在不分配的情况下解决,但随后我必须为每个新列添加一行,并且由于我需要大量这样的行,所以我正在寻找解决方法。

我还使用 pyjanitor 研究了 add_columns,但它似乎不适用于 groupby。

在 R 中,以下代码解决了该问题,并且在 mutate 中我可以继续添加列:

df = df %>% group_by(id, s) %>% mutate(ttl_3 = runner(ttl, k=3, f=sum), min_id_s = min(ttl))

最佳答案

Python 语法和 pandas 的逻辑几乎相同

t = '''
id  s         m   ttl 
1   A   1/1/2020    7 
1   A   2/1/2020    3 
1   A   3/1/2020    7 
1   A   4/1/2020    6 
1   A   5/1/2020    7 
1   A   6/1/2020    7 
1   B   1/1/2020    6 
1   B   2/1/2020    10
1   B   3/1/2020    8 
1   B   4/1/2020    8 
1   B   5/1/2020    10
1   B   6/1/2020    8 
2   A   1/1/2020    4 
2   A   2/1/2020    3 
2   A   3/1/2020    10
2   A   4/1/2020    6 
2   A   5/1/2020    4 
2   A   6/1/2020    1 '''

import pandas as pd
import io

df = pd.read_csv(io.StringIO(t), sep='\s+')

尝试尽可能接近 dplyr。
ids 分组并计算新列。可以使用 agg 方法计算多个滚动列。
您可以使用分配,但您还必须为每个聚合编写一行。

group = df.groupby(['id','s'])['ttl']
df['ttl_3'] = group.rolling(3).sum().reset_index(level=(0,1), drop=True)
df['min_id_s'] = group.transform('min')
#df = df.assign(
#         ttl_3 = group.rolling(3).sum().reset_index(level=(0,1), drop=True),
#         min_id_s = group.transform('min'))
df

输出:

    id  s         m  ttl  ttl_3  min_id_s
0    1  A  1/1/2020    7    NaN         3
1    1  A  2/1/2020    3    NaN         3
2    1  A  3/1/2020    7   17.0         3
3    1  A  4/1/2020    6   16.0         3
4    1  A  5/1/2020    7   20.0         3
5    1  A  6/1/2020    7   20.0         3
6    1  B  1/1/2020    6    NaN         6
7    1  B  2/1/2020   10    NaN         6
8    1  B  3/1/2020    8   24.0         6
9    1  B  4/1/2020    8   26.0         6
10   1  B  5/1/2020   10   26.0         6
11   1  B  6/1/2020    8   26.0         6
12   2  A  1/1/2020    4    NaN         1
13   2  A  2/1/2020    3    NaN         1
14   2  A  3/1/2020   10   17.0         1
15   2  A  4/1/2020    6   19.0         1
16   2  A  5/1/2020    4   20.0         1
17   2  A  6/1/2020    1   11.0         1

使用agg聚合多列以进行滚动

group = df.groupby(['id','s'])['ttl']
df[['ttl_3_sum','ttl_3_mean']] = group.rolling(3).agg(['sum','mean']).reset_index(level=(0,1), drop=True)

输出:

    id  s         m  ttl  ttl_3_sum  ttl_3_mean
0    1  A  1/1/2020    7        NaN         NaN
1    1  A  2/1/2020    3        NaN         NaN
2    1  A  3/1/2020    7       17.0    5.666667
3    1  A  4/1/2020    6       16.0    5.333333
4    1  A  5/1/2020    7       20.0    6.666667
5    1  A  6/1/2020    7       20.0    6.666667
6    1  B  1/1/2020    6        NaN         NaN
7    1  B  2/1/2020   10        NaN         NaN
8    1  B  3/1/2020    8       24.0    8.000000
9    1  B  4/1/2020    8       26.0    8.666667
10   1  B  5/1/2020   10       26.0    8.666667
11   1  B  6/1/2020    8       26.0    8.666667
12   2  A  1/1/2020    4        NaN         NaN
13   2  A  2/1/2020    3        NaN         NaN
14   2  A  3/1/2020   10       17.0    5.666667
15   2  A  4/1/2020    6       19.0    6.333333
16   2  A  5/1/2020    4       20.0    6.666667
17   2  A  6/1/2020    1       11.0    3.666667

使用agg从多个列聚合多个列以进行滚动。列必须是数字。据我所知,transform不支持多重聚合。

为滚动聚合生成随机数值列

import numpy as np
df['ttl2'] = np.random.rand(len(df))

groupby 没有选择要从多列聚合的列。例如使用自定义函数

group = df.groupby(['id','s'])
df[['ttl_3_sum', 'ttl2_lambda']] = (group.rolling(3)
          .agg({'ttl':'sum', 'ttl2': lambda x: x.sum()/x.min()})
          .reset_index(level=(0,1), drop=True))
df

输出:

    id  s         m  ttl      ttl2  ttl_3_sum  ttl2_lambda
0    1  A  1/1/2020    7  0.032482        NaN          NaN
1    1  A  2/1/2020    3  0.998115        NaN          NaN
2    1  A  3/1/2020    7  0.689431       17.0    52.953016
3    1  A  4/1/2020    6  0.897444       16.0     3.749456
4    1  A  5/1/2020    7  0.484360       20.0     4.276231
5    1  A  6/1/2020    7  0.971768       20.0     4.859138
6    1  B  1/1/2020    6  0.238363        NaN          NaN
7    1  B  2/1/2020   10  0.740311        NaN          NaN
8    1  B  3/1/2020    8  0.641598       24.0     6.797507
9    1  B  4/1/2020    8  0.984911       26.0     3.688945
10   1  B  5/1/2020   10  0.043379       26.0    38.495141
11   1  B  6/1/2020    8  0.700253       26.0    39.847293
12   2  A  1/1/2020    4  0.437082        NaN          NaN
13   2  A  2/1/2020    3  0.465313        NaN          NaN
14   2  A  3/1/2020   10  0.698976       17.0     3.663777
15   2  A  4/1/2020    6  0.430087       19.0     3.707103
16   2  A  5/1/2020    4  0.949536       20.0     4.832976
17   2  A  6/1/2020    1  0.904986       11.0     5.311974

关于python - 如何在Python中结合groupby、rolling和多列创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64302315/

相关文章:

r - 将 Excel 数字转换为日期

python - 如何矢量化(利用 pandas/numpy)而不是使用嵌套的 for 循环

python - Matplotlib:带有日期和数值的散点图

python - 使用 pandas 和 numpy Python 格式化数据表日期时间值

r - 带 tabyl() 的累积交叉表

r - 使用带有 tidyverse 和 janitor 的 base::function 在 R 中创建多个交叉表

Python 解码 AMF 响应

python - 初始的sys.path从哪里来

python - 在 Python 中显示 SQLite 数据库中的表

python - 替换数据框中的值,行名等于列名