
本文介绍如何在 pandas 中为按类别分组的数据计算“滞后累积和”——即每个组内当前行的累积值不包含本行数据,而是截至前一行的累计总和,适用于特征工程中构建时序依赖型特征。
本文介绍如何在 pandas 中为按类别分组的数据计算“滞后累积和”——即每个组内当前行的累积值不包含本行数据,而是截至前一行的累计总和,适用于特征工程中构建时序依赖型特征。
在机器学习建模(尤其是体育赛事预测、用户行为分析等时序场景)中,常需构造“截至上一事件的累计统计量”作为特征。例如:预测某球队主场得分时,应使用其历史主场得分总和(不含当前场次),而非包含当前场次的即时累计值——否则会引入未来信息,导致数据泄露。
Pandas 提供了灵活的分组与窗口操作能力,但直接链式调用 .shift().cumsum() 容易出错(如问题中所示),因为 shift 作用于原始序列,而 cumsum 若在全局或错误层级执行,将破坏分组逻辑。正确做法是:先按组计算累积和,再对结果整体偏移一行。
✅ 正确实现方式
使用 groupby().transform() 配合匿名函数,确保每组独立计算并保持原始索引对齐:
data['cumulative'] = (
data.groupby('home_id')['home_score']
.transform(lambda x: x.cumsum().shift(1))
.fillna(0)
.astype(int)
)
- groupby('home_id'):按球队 ID 分组;
- transform(...):保证输出与原 DataFrame 等长,且自动对齐;
- lambda x: x.cumsum().shift(1):对每组 home_score 先累加,再整体下移一行(首行变 NaN);
- .fillna(0).astype(int):将首行 NaN 替换为 0,并转为整型,符合业务语义(无历史记录时累计值为 0)。
⚠️ 常见误区与注意事项
- ❌ 错误写法:data.groupby('home_id')['home_score'].shift().cumsum()
→ shift() 在分组后作用于每组首尾,但 cumsum() 会在整个 shift 后的 Series 上全局累加,破坏分组边界。 - ✅ 必须保证 cumsum() 和 shift() 在同一分组内完成:transform 内部的 lambda 函数天然满足此要求。
- ? 时间顺序至关重要:示例数据已按 game_date 排序。若原始数据未排序,请务必先执行 data.sort_values(['home_id', 'game_date'], inplace=True),否则累积逻辑将失效。
- ? 扩展应用:该模式可推广至任意滞后阶数(如 shift(2) 表示截至前两场)、其他聚合(如 cummean())、或多字段组合(如 cumsum() + rolling(3).mean())。
通过这一简洁可靠的模式,你可安全地为任意分组时序特征生成无数据泄露的滞后累积特征,显著提升模型泛化能力与工程健壮性。











