【问题标题】:Pandas groupby cumulative sumPandas groupby 累计总和
【发布时间】:2014-05-04 06:07:36
【问题描述】:

我想在我的 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 的答案是计算每个名称的天数上的 cumsum。想想如果还有一个日期列,如何扩展它,可以在分组和聚合之前对条目进行排序。

标签: python pandas pandas-groupby


【解决方案1】:

这个应该可以了,需要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. 你能解释一下'level = [0]' 是什么意思吗? 2. 此外,如您所见,您之前的数据框中有行号,一旦您进行累积总和,这些行号就会消失。有没有办法让他们回来?
  • 1),索引号必须去,因为cumsums来自多行,比如第二个数字40是10+20+10,应该取哪个索引值? 1、2 还是 3?所以,让我们继续使用nameday 作为multiIndex,这样更有意义(如果需要,reset_index() 可以获得int 索引)。 2)、level=[0]的意思是groupby是由MultiIndex的第一级操作,即name列。
  • 感谢 CT。后来我明白了,并尝试使用 reset_index() 来解决我的问题。感谢您的详细解释!
  • 有一个微妙的错误:第一个groupby() 默认对键进行排序,所以如果你在输入数据集的底部添加一个 Jack-Thursday 行,你会得到意想不到的结果。由于groupby() 可以使用级别名称,我发现df.groupby(['name', 'day'], sort=False).sum().groupby(by='name').cumsum().reset_index() 不那么神秘。
  • 如何重命名列?
【解决方案2】:

这适用于熊猫 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() 并没有很好地配合。
  • 请注意,这个答案(似乎等同于simpler solution by @vjayky)在通过name 计算累积和之前不会按nameday 聚合(注意:Jack 有 2 行+星期二的结果)。这就是它比the answer by CT Zhu 更简单的原因。
【解决方案3】:

你应该使用

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

【讨论】:

  • 这会计算全局运行总计,而不是单独为每个组单独计算总和。所以 Jill-Monday 被分配了一个值 130(90,作为所有 Jack 值的总和,+40,Jill-Monday 的值)。
  • @Nickolay 刚刚添加了另一个答案,让我知道它是否有效
  • 我不确定它是否根据我的示例计算全局运行总计,第 3 行的值为 4
  • 为什么我在这里使用 lambda x: x.cumsum() 而不是 pandas.series.cumsum()?
【解决方案4】:

而不是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() 并不是只是将两列都移动到 MultiIndex —— 它还总结了 Jack+Tuesday 的两个值。而as_index=False 在这种情况下似乎没有任何效果,因为索引已经设置在groupby 之前。并且由于groupby().cumsum() 从数据框的列中删除名称/日期,因此您必须将生成的数字列添加到原始数据框(如 vjayky 和 ​​Dmitry 建议的那样),或者将名称/日期移动到索引,然后再重置索引。
【解决方案5】:

修改@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

【讨论】:

  • 如果您按照问题的要求don't need the two-step aggregation,这似乎是最简单的解决方案。
  • 我唯一不喜欢的部分是它将我的 int dtype 转换为浮点数。
  • 这应该是集体部分中 cumsum 的公认答案。 @ChrisFarr 从 pandas 1.0.3 开始,它似乎不再转换为浮动了。
【解决方案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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-30
    • 2017-08-01
    • 2018-01-21
    • 1970-01-01
    • 2020-06-27
    相关资源
    最近更新 更多