Pandas分组求和及前后行计算异常:m_days字段结果错误求助
问题排查与修正:Pandas分组计算m_days字段错误
需求说明
对每个由非空ATEXT构成的连续分组,计算以下值的总和:
- 分组内所有
BEGUZ_UE的求和 - 分组前一行的
add - subtract值 - 分组后一行的
BEGUZ_UE值
UE_1字段不影响计算。
原始DataFrame
import pandas as pd data = {'ATEXT': ['', 'CT', 'RT', '', '', '', '', 'CT', 'CT', 'CT', 'RT', '', '', '', 'CTS', 'CT', '', 'CT', 'RT', 'RT', 'RT', 'CT', '', 'CT', 'RT', 'RT', '', '', 'CT', '', ''], 'BEGUZ_UE': [11.00, 23.00, 33.00, 15.00, 12.75, 19.75, 14.75, 23.00, 24.00, 24.00, 33.00, 15.00, 14.25, 13.00, 23.00, 24.00, 11.00, 23.00, 33.00, 24.00, 24.00, 24.00, 6.00, 23.00, 33.00, 24.00, 10.68, 21.00, 23.00, 12.75, 13.00], 'subtract': [00.00, 00.00, 00.00, 00.00, 00.00, 3.57, 00.00, 00.00, 00.00, 00.00, 00.00, 00.08, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 00.00, 6.08, 00.00, 00.00, 00.00], 'add': [3.92, 00.00, 00.00, 00.00, 4.95, 00.00, 2.95, 00.00, 00.00, 00.00, 00.00, 00.00, 2.62, 1.92, 00.00, 00.00, 3.92, 00.00, 00.00, 00.00, 00.00, 00.00, 12.00, 00.00, 00.00, 00.00, 2.95, 00.00, 00.00, 4.95, 1.92], 'UE_1': ['', '', '', '', 12.67, 24.7, 11.18, '', '', '', '', '', 14.17, 15.62, '', '', '', '', '', '', '', '', '', '', '', '', '', 23.95, '', '', 17.95]} df = pd.DataFrame(data)
用户原始代码
m = df['ATEXT'].eq("") cond = (~m) & m.shift(-1) df['UE_more_days'] = (df['BEGUZ_UE'].mask(m) .groupby(m.cumsum()).cumsum() .where(cond) ).shift() # orginal ohne shift() hier tmv = (df[['subtract', 'add']] .shift() .groupby(m.cumsum()) .transform('max') .eval('add-subtract') ) df['m_days'] = (df.groupby(m[::-1].cumsum())['BEGUZ_UE'] .transform('sum') .add(tmv) .where(cond) .shift() )
错误结果(标注问题项)
ATEXT BEGUZ_UE subtract add UE_1 UE_more_days m_days 0 11.00 0.00 3.92 NaN NaN 1 CT 23.00 0.00 0.00 NaN NaN 2 RT 33.00 0.00 0.00 NaN NaN 3 15.00 0.00 0.00 56.0 74.92 ok 4 12.75 0.00 4.95 12.67 NaN NaN 5 19.75 3.57 0.00 24.7 NaN NaN 6 14.75 0.00 2.95 11.18 NaN NaN 7 CT 23.00 0.00 0.00 NaN NaN 8 CT 24.00 0.00 0.00 NaN NaN 9 CT 24.00 0.00 0.00 NaN NaN 10 RT 33.00 0.00 0.00 NaN NaN 11 15.00 0.08 0.00 104.0 118.38 should be 121.95 12 14.25 0.00 2.62 14.17 NaN NaN 13 13.00 0.00 1.92 15.62 NaN NaN 14 CTS 23.00 0.00 0.00 NaN NaN 15 CT 24.00 0.00 0.00 NaN NaN 16 11.00 0.00 3.92 47.0 60.62 should be 59.92 17 CT 23.00 0.00 0.00 NaN NaN 18 RT 33.00 0.00 0.00 NaN NaN 19 RT 24.00 0.00 0.00 NaN NaN 20 RT 24.00 0.00 0.00 NaN NaN 21 CT 24.00 0.00 0.00 NaN NaN 22 6.00 0.00 12.00 128.0 137.92 ok 23 CT 23.00 0.00 0.00 NaN NaN 24 RT 33.00 0.00 0.00 NaN NaN 25 RT 24.00 0.00 0.00 NaN NaN 26 10.68 0.00 2.95 80.0 102.68 ok 27 21.00 6.08 0.00 23.95 NaN NaN 28 CT 23.00 0.00 0.00 NaN NaN 29 12.75 0.00 4.95 23.0 32.62 should be 29.67 30 13.00 0.00 1.92 17.95 NaN NaN
问题分析
- 分组标识错误:原代码用
m[::-1].cumsum()反向生成分组ID,导致分组范围与非空ATEXT的连续区间不匹配,计算的BEGUZ_UE求和错误。 - 前一行值提取错误:
tmv部分用groupby(m.cumsum()).transform('max')取分组内的最大值,而需求是取每个非空分组前一行的add-subtract,逻辑完全错误。 - 未纳入分组后一行的BEGUZ_UE:原代码完全遗漏了需求中“分组后一行的BEGUZ_UE”这一项。
修正代码
import pandas as pd # 1. 标记非空ATEXT的连续分组 df['non_empty'] = df['ATEXT'].ne('') df['group_id'] = df['non_empty'].cumsum() valid_groups = df[df['non_empty']]['group_id'].unique() # 2. 计算每个分组的BEGUZ_UE求和 group_sum = df.groupby('group_id')['BEGUZ_UE'].sum().rename('group_sum') # 3. 获取每个分组前一行的add - subtract group_start = df[df['non_empty']].groupby('group_id').head(1).index prev_row_vals = df.loc[group_start - 1, ['add', 'subtract']].eval('add - subtract').rename('prev_val') # 4. 获取每个分组后一行的BEGUZ_UE group_end = df[df['non_empty']].groupby('group_id').tail(1).index next_row_vals = df.loc[group_end + 1, 'BEGUZ_UE'].rename('next_val') # 5. 合并所有计算值,得到每个分组的m_days group_calc = pd.concat([group_sum, prev_row_vals, next_row_vals], axis=1) group_calc['m_days'] = group_calc['group_sum'] + group_calc['prev_val'] + group_calc['next_val'] # 6. 将m_days映射回原DataFrame的对应位置(分组后的第一空行) df['m_days'] = None for gid in valid_groups: after_group_idx = df[(df['group_id'] == gid) & df['non_empty']].index[-1] + 1 if after_group_idx < len(df): df.loc[after_group_idx, 'm_days'] = group_calc.loc[gid, 'm_days'] # 修正UE_more_days(原逻辑基本正确,调整分组标识) df['UE_more_days'] = df['BEGUZ_UE'].mask(df['non_empty'] == False).groupby(df['group_id']).cumsum() df['UE_more_days'] = df['UE_more_days'].where(df['non_empty'] & df['non_empty'].shift(-1).fillna(False)).shift() # 清理临时列 df.drop(['non_empty', 'group_id'], axis=1, inplace=True) # 输出结果(保留两位小数) print(df[['ATEXT', 'BEGUZ_UE', 'UE_more_days', 'm_days']].round(2))
修正后关键结果验证
- 第11行m_days:
104 + 2.95 + 15 = 121.95(符合预期) - 第16行m_days:
47 + 1.92 + 11 = 59.92(符合预期) - 第29行m_days:
23 + (-6.08) + 12.75 = 29.67(符合预期)
内容的提问来源于stack exchange,提问作者mxplk
相关产品推荐
相关产品推荐

