熊猫Groupby累积总和


93

我想向我的Pandas数据框添加一个累加和列,以便:

name | day       | no
-----|-----------|----
Jack | Monday    | 10
Jack | Tuesday   | 20
Jack | Tuesday   | 10
Jack | Wednesday | 50
Jill | Monday    | 40
Jill | Wednesday | 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))无济于事。


您确定要在工作日内汇总吗?那样就失去了索引,而且如果有多个星期,则累加的总和就没有意义了。dmitry-andreev和@vjayky的答案将按天顺序计算每个名称的累积量。想一想,如果还有日期列,该如何扩展?可以在分组和汇总之前对条目进行排序。
Elias Hasle

Answers:


89

这应该做到,需要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

3
谢谢你的回答。我确实有一些疑问:1.您能解释一下“ level = [0]”是什么意思吗?2.同样,正如您所看到的,您之前在数据框中已有行号,并且在进行累加总和后这些行号就会消失。有办法让他们回来吗?
user3694373

5
1),索引号必须走,因为总和来自多个行,例如第二个数字40,是10 + 20 + 10,它应该获得哪个索引值?1 2 3 因此,让我们继续使用nameand dayas multiIndex,这样做更有意义(如果需要,reset_index()可以获取int索引)。2),level=[0]方法groupby是按的第1级操作MultiIndex,即列name
CT朱

谢谢CT。我后来明白了,并尝试使用reset_index()解决我的问题。感谢您的详细解释!
2014年

4
有一个细微的错误:第一个groupby()默认值是对键进行排序,因此,如果在输入数据集的底部添加一个Jack-Thursday行,则会得到意外的结果。而且由于groupby()可以与级别名称一起使用,所以我发现df.groupby(['name', 'day'], sort=False).sum().groupby(by='name').cumsum().reset_index()隐秘性降低了。
Nickolay

您如何重命名列?
林志强

47

这适用于熊猫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确实很有帮助。我尝试使用转换,但是在cumsum()上不能很好地发挥作用。
zerovector

2
请注意,这个答案(似乎等同于由@vjayky简单的解决方案)不通过汇总nameday通过计算累积和之前name(注:有2行杰克+在周二的结果)。这使它比朱CT的答案更简单。
Nickolay

39

修改@Dmitry的答案。这比较简单,可以在熊猫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

2
如果您不需要问题中要求的两步聚合,这似乎是最简单的解决方案。
Nickolay

我唯一不喜欢的部分是它将int dtype转换为float。
克里斯·法尔

这应该是小组部分的总和的可接受答案。@ChrisFarr从我的熊猫1.0.3开始,它似乎不再转换为浮动。
路易·杨

7

除了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() 实际上只是将两列都移至MultiIndex
  • as_index=False 意味着您以后不需要调用reset_index

感谢您发布此信息,它帮助我了解了这里的情况!请注意,groupby().sum()只是移动两列,将多指标-它也总结了杰克+周二两个值。并且as_index=False在这种情况下似乎没有任何作用,因为索引已经在之前设置了groupby。并且由于groupby().cumsum()从数据框的列中破坏了名称/日期,因此您必须将结果数字列添加到原始数据框(如建议的vjayky和Dmitry),或者将名称/日期移动到索引,然后再设置reset_index。
Nickolay

7

你应该使用

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

在此处输入图片说明


3
这将计算全局运行总计,而不是分别为每个组计算单独的总和。因此,给Jill-Monday分配了一个130的值(90,作为所有Jack值的总和+ 40,即Jill-Monday的值)。
Nickolay '18年

@Nickolay刚刚添加了另一个答案,让我知道它是否有效
sushmit

我不确定是否按照我的示例行3计算全局运行总值
sshmit

为什么在这里使用lambda x:x.cumsum()而不是pandas.series.cumsum()?
金华王

0

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.