Pandas 分组累计和
- 2025-02-27 09:06:00
- admin 原创
- 67
问题描述:
我想向我的 Pandas 数据框添加一个累积总和列,以便:
姓名 | 天 | 不 |
---|---|---|
杰克 | 周一 | 10 |
杰克 | 周二 | 20 |
杰克 | 周二 | 10 |
杰克 | 周三 | 50 |
吉尔 | 周一 | 40 |
吉尔 | 周三 | 110 |
变成:
Jack | Monday | 10 | 10
Jack | Tuesday | 30 | 40
Jack | Wednesday | 50 | 90
Jill | Monday | 40 | 40
Jill | Wednesday | 110 | 150
我尝试了各种组合,df.groupby
但df.agg(lambda x: cumsum(x))
都无济于事。
解决方案 1:
这应该可以做到,需要groupby()
两次:
df.groupby(['name', 'day']).sum() \n .groupby(level=0).cumsum().reset_index()
解释:
print(df)
name day no
0 Jack Monday 10
1 Jack Tuesday 20
2 Jack Tuesday 10
3 Jack Wednesday 50
4 Jill Monday 40
5 Jill Wednesday 110
# sum per name/day
print( df.groupby(['name', 'day']).sum() )
no
name day
Jack Monday 10
Tuesday 30
Wednesday 50
Jill Monday 40
Wednesday 110
# cumulative sum per name/day
print( df.groupby(['name', 'day']).sum() \n .groupby(level=0).cumsum() )
no
name day
Jack Monday 10
Tuesday 40
Wednesday 90
Jill Monday 40
Wednesday 150
第一个总和产生的数据框由'name'
和 索引'day'
。您可以通过打印来查看它
df.groupby(['name', 'day']).sum().index
计算累积和时,您需要通过 来计算'name'
,对应第一个索引(级别 0)。
最后,用来reset_index
重复名字。
df.groupby(['name', 'day']).sum().groupby(level=0).cumsum().reset_index()
name day no
0 Jack Monday 10
1 Jack Tuesday 40
2 Jack Wednesday 90
3 Jill Monday 40
4 Jill Wednesday 150
解决方案 2:
修改@Dmitry的答案。这更简单,并且在pandas 0.19.0中有效:
print(df)
name day no
0 Jack Monday 10
1 Jack Tuesday 20
2 Jack Tuesday 10
3 Jack Wednesday 50
4 Jill Monday 40
5 Jill Wednesday 110
df['no_csum'] = df.groupby(['name'])['no'].cumsum()
print(df)
name day no no_csum
0 Jack Monday 10 10
1 Jack Tuesday 20 30
2 Jack Tuesday 10 40
3 Jack Wednesday 50 90
4 Jill Monday 40 40
5 Jill Wednesday 110 150
解决方案 3:
这在 pandas 0.16.2 中有效
In[23]: print df
name day no
0 Jack Monday 10
1 Jack Tuesday 20
2 Jack Tuesday 10
3 Jack Wednesday 50
4 Jill Monday 40
5 Jill Wednesday 110
In[24]: df['no_cumulative'] = df.groupby(['name'])['no'].apply(lambda x: x.cumsum())
In[25]: print df
name day no no_cumulative
0 Jack Monday 10 10
1 Jack Tuesday 20 30
2 Jack Tuesday 10 40
3 Jack Wednesday 50 90
4 Jill Monday 40 40
5 Jill Wednesday 110 150
解决方案 4:
你应该使用
df['cum_no'] = df.no.cumsum()
http://pandas.pydata.org/pandas-docs/version/0.19.2/ generated/pandas.DataFrame.cumsum.html
另一种方法
import pandas as pd
df = pd.DataFrame({'C1' : ['a','a','a','b','b'],
'C2' : [1,2,3,4,5]})
df['cumsum'] = df.groupby(by=['C1'])['C2'].transform(lambda x: x.cumsum())
df
解决方案 5:
除了df.groupby(by=['name','day']).sum().groupby(level=[0]).cumsum()
(见上文)你也可以这样做df.set_index(['name', 'day']).groupby(level=0, as_index=False).cumsum()
df.groupby(by=['name','day']).sum()
实际上只是将两列移动到 MultiIndexas_index=False
意味着你之后不需要调用 reset_index
解决方案 6:
数据.csv:
name,day,no
Jack,Monday,10
Jack,Tuesday,20
Jack,Tuesday,10
Jack,Wednesday,50
Jill,Monday,40
Jill,Wednesday,110
代码:
import numpy as np
import pandas as pd
df = pd.read_csv('data.csv')
print(df)
df = df.groupby(['name', 'day'])['no'].sum().reset_index()
print(df)
df['cumsum'] = df.groupby(['name'])['no'].apply(lambda x: x.cumsum())
print(df)
输出:
name day no
0 Jack Monday 10
1 Jack Tuesday 20
2 Jack Tuesday 10
3 Jack Wednesday 50
4 Jill Monday 40
5 Jill Wednesday 110
name day no
0 Jack Monday 10
1 Jack Tuesday 30
2 Jack Wednesday 50
3 Jill Monday 40
4 Jill Wednesday 110
name day no cumsum
0 Jack Monday 10 10
1 Jack Tuesday 30 40
2 Jack Wednesday 50 90
3 Jill Monday 40 40
4 Jill Wednesday 110 150
解决方案 7:
从 1.0 版本开始,pandas 获得了用于窗口函数的新 API。
具体来说,之前取得的成果
df.groupby(['name'])['no'].apply(lambda x: x.cumsum())
或者
df.set_index(['name', 'day']).groupby(level=0, as_index=False).cumsum()
现在变成
df.groupby(['name'])['no'].expanding().sum()
我发现所有与窗口相关的函数比 groupby+level 操作更直观
虽然学习使用 groupby 对于一般用途很有用。
请参阅文档:
https://pandas.pydata.org/docs/user_guide/window.html
解决方案 8:
如果您想编写一行代码(也许您想将方法传递到管道中),您可以首先将方法as_index
的参数设置groupby
为 False 以从聚合步骤返回一个数据框并用于assign()
为其分配一个新列(每个人的累计总和)。
这些链式方法返回一个新的数据框,因此您需要将其分配给一个变量(例如agg_df
)以便以后使用它。
agg_df = (
# aggregate df by name and day
df.groupby(['name','day'], as_index=False)['no'].sum()
.assign(
# assign the cumulative sum of each name as a new column
cumulative_sum=lambda x: x.groupby('name')['no'].cumsum()
)
)
扫码咨询,免费领取项目管理大礼包!