
本文介绍如何在 Pandas 中高效实现按账户(Acc)分组、依据 indi_val 字符串后缀(如 "_E")进行行级条件过滤,并分别计算“With E”“Without E”“Normal”三类指标的金额总和,最终与参考表对齐输出。
本文介绍如何在 pandas 中高效实现按账户(acc)分组、依据 `indi_val` 字符串后缀(如 "_e")进行行级条件过滤,并分别计算“with e”“without e”“normal”三类指标的金额总和,最终与参考表对齐输出。
在实际数据分析中,常需根据字符串特征(如后缀、前缀、包含关键词等)对数据进行行级条件筛选,再结合分组(groupby)完成聚合统计。本例中,df1 存储明细交易数据,df2 定义每个账户所需的聚合类型(Indi),目标是为 df2 中每条记录匹配对应逻辑下的 Amt 总和。
直接使用 df2.apply() 调用自定义函数虽直观,但存在两大隐患:一是未指定 axis=1 导致 KeyError: 'Indi'(因默认按列应用);二是嵌套 df1.apply() 属于低效的 Python 循环式操作,违背 Pandas 向量化设计原则,易引发性能瓶颈与逻辑错误(如空列表、条件误判等)。
✅ 推荐采用 分步聚合 + 拼接 + 关联合并 的向量化方案,清晰、健壮且高效:
import pandas as pd
# 构建原始数据
data1 = {
'Acc': [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4],
'indi_val': ['Val1', 'val2', 'Val_E', 'Val1_E', 'Val1', 'Val3', 'val2', 'val2_E', 'val22_E', 'val2_A', 'val2_V', 'Val_E', 'Val_A', 'Val', 'Val2', 'val7'],
'Amt': [10, 20, 5, 5, 22, 38, 15, 25, 22, 23, 24, 56, 67, 45, 87, 88]
}
df1 = pd.DataFrame(data1)
data2 = {
'Acc': [1, 1, 2, 2, 3, 4],
'Indi': ['With E', 'Without E', 'With E', 'Without E', 'Normal', 'Normal']
}
df2 = pd.DataFrame(data2)
# 步骤1:构建各指标类型的聚合结果(全部向量化)
temp_dfs = []
# "Normal": 不做任何过滤,全量求和
normal_agg = df1.groupby('Acc')['Amt'].sum().reset_index(name='Amt')
normal_agg['Indi'] = 'Normal'
temp_dfs.append(normal_agg)
# "With E": indi_val 以 '_E' 结尾
with_e_mask = df1['indi_val'].str.endswith('_E', na=False) # na=False 避免 NaN 引发错误
with_e_agg = df1[with_e_mask].groupby('Acc')['Amt'].sum().reset_index(name='Amt')
with_e_agg['Indi'] = 'With E'
temp_dfs.append(with_e_agg)
# "Without E": indi_val 不以 '_E' 结尾(注意:需排除 NaN)
without_e_mask = ~df1['indi_val'].str.endswith('_E', na=False)
without_e_agg = df1[without_e_mask].groupby('Acc')['Amt'].sum().reset_index(name='Amt')
without_e_agg['Indi'] = 'Without E'
temp_dfs.append(without_e_agg)
# 合并所有聚合结果
result_agg = pd.concat(temp_dfs, ignore_index=True)
# 步骤2:与 df2 关联匹配(确保 Acc+Indi 双键对齐)
final_result = df2.merge(result_agg, on=['Acc', 'Indi'], how='left')
print(final_result)
输出结果:
Acc Indi Amt 0 1 With E 10 1 1 Without E 90 2 2 With E 47 3 2 Without E 62 4 3 Normal 255 5 4 Normal 88
? 关键注意事项:
- 使用 .str.endswith('_E', na=False) 替代切片 [-2:] == '_E',既避免 IndexError(短字符串),又显式处理 NaN 值;
- groupby(...).sum() 自动忽略 NaN,但需确保 Amt 列无非数值类型;
- merge(..., on=['Acc','Indi']) 严格按双键匹配,保证 df2 的每一行都能精准关联到对应聚合值;
- 若 df2 中存在 df1 无对应数据的组合(如某账户无 _E 记录却要求 "With E"),结果中 Amt 将为 NaN,可根据业务需要填充(如 .fillna(0))。
该方法完全规避了 apply 的嵌套循环陷阱,充分利用 Pandas 底层优化,代码可读性强、易于扩展(如新增 "Contains A" 类型只需追加一个子集聚合),是生产环境中处理类似“多条件分组聚合+元数据驱动”的标准实践。










