You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Groupby的sum与cumsum差异:返回DataFrame/Series的原因及解决方法

Pandas GroupBy:sum与cumsum行为差异及多聚合操作解决方案

问题背景

测试数据:

import pandas as pd
import numpy as np

dict1 = {'A': {0: 'A0', 1: 'A0', 2: 'A0', 3: 'A0', 4: 'A1', 5: 'A1', 6: 'A1', 7: 'A1', 8: 'A1', 9: 'A1'}, 
         'B': {0: 'B0', 1: 'B1', 2: 'B2', 3: 'B3', 4: 'B4', 5: 'B5', 6: 'B6', 7: 'B7', 8: 'B8', 9: 'B9'}, 
         'C': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}, 
         'D': {0: 10, 1: 11, 2: 12, 3: 13, 4: 14, 5: 15, 6: 16, 7: 17, 8: 18, 9: 19}, 
         'E': {0: 'E0', 1: 'E1', 2: 'E0', 3: 'E1', 4: 'E0', 5: 'E1', 6: 'E0', 7: 'E1', 8: 'E0', 9: 'E1'}}

df2 = pd.DataFrame(dict1)

1. sum与cumsum的行为差异

执行分组求和:

df2.groupby(['A','E'])['D'].sum()

返回带分组索引的聚合结果:

A   E 
A0  E0    22
    E1    24
A1  E0    48
    E1    51
Name: D, dtype: int64

执行分组累计求和:

df2.groupby(['A','E'])['D'].cumsum()

仅返回保留原索引的行级结果:

0    10
1    11
2    22
3    24
4    14
5    15
6    30
7    32
8    48
9    51
Name: D, dtype: int64

2. 多列混合聚合问题

尝试一次聚合中同时执行累计求和与自定义聚合:

df2.groupby(['A','E']).agg({'D':'cumsum','C':lambda x: 4*np.sum(x)})

结果出现大量NaN:

D     C
0     10.0   NaN
1     11.0   NaN
2     22.0   NaN
3     24.0   NaN
4     14.0   NaN
5     15.0   NaN
6     30.0   NaN
7     32.0   NaN
8     48.0   NaN
9     51.0   NaN
(A0, E0) NaN  8.0
(A0, E1) NaN 16.0
(A1, E0) NaN 72.0
(A1, E1) NaN 84.0

解答

一、sum与cumsum行为差异的原因

  • sum是聚合(Aggregation)操作:将每个分组的所有数据汇总为单个值,输出行数等于分组数量,索引为分组键组合。
  • cumsum是转换(Transformation)操作:为原数据的每一行返回对应分组内的累计值,保留原索引,输出行数与原数据一致,核心是在不改变数据结构行数的前提下完成分组内逐行计算。

二、让cumsum返回包含分组信息的DataFrame

无需手动赋值的简洁方式:

# 通过apply返回带分组键的DataFrame
result_df = df2.groupby(['A','E'])['D'].apply(lambda x: x.cumsum()).reset_index()

输出结果:

A   E  D
0  A0  E0 10
1  A0  E0 22
2  A0  E1 11
3  A0  E1 24
4  A1  E0 14
5  A1  E0 30
6  A1  E0 48
7  A1  E1 15
8  A1  E1 32
9  A1  E1 51

三、多列混合聚合的解决方案

问题根源:聚合操作返回分组级结果,转换操作返回行级结果,两者索引不匹配导致NaN。以下是两种无需单独处理的实现方式:

方案1:先转换后聚合再合并

# 计算行级累计求和
df2['D_cumsum'] = df2.groupby(['A','E'])['D'].cumsum()
# 计算分组级聚合结果
agg_df = df2.groupby(['A','E'])['C'].agg(lambda x: 4*np.sum(x)).reset_index(name='C_agg')
# 合并结果
final_df = pd.merge(df2[['A','E','D_cumsum']], agg_df, on=['A','E'], how='left')

最终输出:

A   E  D_cumsum  C_agg
0  A0  E0        10      8
1  A0  E1        11     16
2  A0  E0        22      8
3  A0  E1        24     16
4  A1  E0        14     72
5  A1  E1        15     84
6  A1  E0        30     72
7  A1  E1        32     84
8  A1  E0        48     72
9  A1  E1        51     84

方案2:自定义分组处理函数

通过groupby.apply在每个分组内同时完成转换与聚合:

def process_group(group):
    group['D_cumsum'] = group['D'].cumsum()
    group['C_agg'] = 4 * group['C'].sum()
    return group

final_df = df2.groupby(['A','E'], group_keys=False).apply(process_group)

此方法直接返回包含所有计算列的完整DataFrame,无需额外合并步骤。


内容的提问来源于stack exchange,提问作者Rajib Lochan Sarkar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:40:17