Pandas 分组累计求和

154 投票
8 回答
202095 浏览
提问于 2025-04-17 23:55

我想在我的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.groupbydf.agg(lambda x: cumsum(x)),但都没有成功。

8 个回答

9

你可以用 df.set_index(['name', 'day']).groupby(level=0, as_index=False).cumsum() 来替代 df.groupby(by=['name','day']).sum().groupby(level=[0]).cumsum()(见上面)。

  • df.groupby(by=['name','day']).sum() 实际上只是把这两列变成了一个多重索引(MultiIndex)。
  • as_index=False 的意思是你不需要在之后再调用 reset_index。
13

你应该使用

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

在这里输入图片描述

60

这个在 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
83

对@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
148

这样做就可以了,需要用到 groupby() 两次:

df.groupby(['name', 'day']).sum() \
  .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() \
         .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

撰写回答