Pandas分组累加求和

151

我想在我的Pandas数据帧中添加一个累计总和列,以便:

name day no cumulative_sum
Jack Monday 10 10
Jack Tuesday 20 30
Jack Tuesday 10 40
Jack Wednesday 50 90
Jill Monday 40 40
Jill Wednesday 110 150

变成:

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))的组合,但都没有成功。


要使用一行代码创建两列,请使用此答案 - cottontail
8个回答

146

这应该可以搞定,需要使用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

1
用暴力方法达到结果,希望在pandas中这很简单。 - MGLondon

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

这个方法可以,但是你需要小心处理“day”列的顺序。举个例子,如果“day”按照字母顺序排列,“no_csum”可能无法反映你实际需要的信息。 - ovpira

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

df.groupby(['name'])['no'].cumsum()也可以正常工作。 - undefined

13

9

与其使用 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() 实际上只是将两列移动到多级索引中。
  • as_index=False 表示你无需在之后调用 reset_index。

2

data.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

1

从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


0

如果你想写一行代码(也许你想将方法传递到管道中),你可以先将groupby方法的as_index参数设置为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()
    )
)

res


我们怎样才能确保“cumsum”按照“day”的顺序执行? - undefined
@JigidiSarnath 如果你想要按照日期顺序执行cumsum操作,你需要在cumsum之前对groupby结果按照日期进行排序。请参考这篇帖子中的方法来对数据进行排序。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接