本文介绍一种高效、向量化的方法,利用 groupby + cumsum 实现类似 excel 中“条件重置累加”的逻辑:当参考列值为 0 时清零,否则对当前值与上一行新列值求和。
本文介绍一种高效、向量化的方法,利用 groupby + cumsum 实现类似 excel 中“条件重置累加”的逻辑:当参考列值为 0 时清零,否则对当前值与上一行新列值求和。
在数据分析中,常需构造一类“带重置的累积和”列(如库存连续补货量、会话持续时间、信号积分等),其规则看似依赖递归(即新列 B[i] = A[i] + B[i−1],但 B[i−1] 尚未生成),容易误以为必须用 for 循环或 apply 逐行计算。实际上,Pandas 提供了完全向量化、高性能的解决方案——关键在于将问题转化为按逻辑分组后组内累加。
核心思路:将“重置点”转为分组标识
观察目标行为:每当 Column A 出现 0,Column B 就清零并重新开始累加。这意味着每个 0 都标志着一个新“累加段”的开始。我们可以:
- 识别所有重置位置:column_a == 0 生成布尔序列;
- 生成分组编号:对布尔序列调用 .cumsum(),使每个 True(即 0 值)及其之后的元素属于同一组,且每遇到新 0 就进入下一组;
- 组内累加:使用 .groupby(...).cumsum() 对每组内的 A 值独立累加。
该方法完全避免循环和显式引用自身列的前序值,充分利用 Pandas 的向量化能力,性能远超 iterrows() 或 shift().fillna(0) 拼接条件判断。
完整实现代码
import pandas as pd
# 示例数据
df = pd.DataFrame({"A": [0, 0, 1, 2, 3, 0, 0, 1, 4]})
# ✅ 向量化生成 Column B(推荐方案)
streak_indicator = df["A"] == 0
df["B"] = df["A"].groupby(streak_indicator.cumsum()).cumsum()
print(df)输出结果:
A B 0 0 0 1 0 0 2 1 1 3 2 3 4 3 6 5 0 0 6 0 0 7 1 1 8 4 5
✅ 验证逻辑:
- 行 0–1:A=0 → streak_indicator=True,cumsum() 得 [1,2,...],故第 0 行属组 1、第 1 行属组 2 → 各自组内 cumsum 为 0;
- 行 2–4:A=[1,2,3] 属组 3 → cumsum 得 [1,3,6];
- 行 5–6:A=0 → 新组(4 和 5),值均为 0;
- 行 7–8:A=[1,4] 属组 6 → cumsum 得 [1,5]。
注意事项与进阶提示
- 边界安全:此方法天然支持首行(B[0] 正确为 0 当 A[0]==0),无需特殊处理;
- 非零重置值? 若需在 A==X(X≠0)时重置,只需将 df["A"] == 0 替换为 df["A"] == X;
- 浮点/缺失值处理:若 A 含 NaN,先用 fillna(0) 或 dropna() 预处理,否则 ==0 判断可能失效;
- 性能对比:对百万级数据,向量化方案比 for 循环快 100+ 倍,比 apply(lambda x: ...) 快 10+ 倍;
- 替代思路(不推荐):虽可用 df["B"] = df["A"].where(df["A"] != 0, 0).cumsum(),但这无法重置——它只是全局累加后置零,不符合需求。
总结
本例展示了如何将“状态依赖型”列生成问题,通过巧妙构造分组键(cumsum of reset condition),转化为标准的分组聚合操作。这不仅是解决累加重置的通用范式,也适用于计数连续非零长度、标记会话 ID、检测信号上升沿等典型“streaks and islands”场景。掌握这一模式,可显著提升 Pandas 代码的简洁性、可读性与执行效率。










