
本文介绍在 pandas 中识别数值连续段(streak),并基于前一段的值条件性修改当前段——例如将紧随“1段”之后的“2段”整体替换为 1,适用于序列化数据清洗与规则化处理。
本文介绍在 pandas 中识别数值连续段(streak),并基于前一段的值条件性修改当前段——例如将紧随“1段”之后的“2段”整体替换为 1,适用于序列化数据清洗与规则化处理。
在时间序列、状态日志或分组行为分析中,常需按“连续相同值段”(即 streak)进行上下文感知的转换。本例目标明确:若某段全为 2,且其直接前驱段全为 1,则将该 2 段所有元素统一替换为 1;其余值(如 -1、孤立 2 等)保持不变。
核心难点在于:不能仅比较相邻行(如 df.a.shift(1) == 1 and df.a == 2),因为这只能捕获段首的切换点,无法将整个后续 2 段全部标记。正确解法是按“段”传播前一段的代表值,再进行向量化判断。
✅ 推荐解法:利用 shift + ffill 实现段级上下文传播
import pandas as pd
df = pd.DataFrame({'a': [1, 1, 1, 2, 2, 2, 2, 2, -1, -1, 2, 2, 2]})
# 步骤1:获取上一行的值(用于检测段切换)
s = df['a'].shift()
# 步骤2:仅在值变化处保留前一行值,其余置为 NaN → 标记每个新段的起始点
# 然后用 ffill 向下填充,使整段共享其前一段的值
prev_group = s.where(df['a'] != s).ffill()
# 步骤3:构造两个布尔掩码
m1 = prev_group == 1 # 当前位置所属的「前一段」是否为 1
m2 = df['a'] == 2 # 当前位置值是否为 2
# 步骤4:同时满足时,赋值为 1
df.loc[m1 & m2, 'a'] = 1
print(df)
输出结果完全符合预期:
a 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 -1 9 -1 10 2 11 2 12 2
? 关键逻辑解析
- s.where(df['a'] != s):仅当 a[i] != a[i-1](即进入新段)时保留 s[i](即 a[i-1]),否则设为 NaN;
- .ffill():将每个新段起始处记录的前一段值,向下广播至整个当前段,实现“段级记忆”;
- 因此,从索引 3 到 7 的 2 段,其 prev_group 值均为 1.0(来自索引 2 处的 1),从而被完整匹配并替换。
⚠️ 注意事项
- 该方法不依赖显式循环或 groupby,纯向量化,性能优异;
- 首行因无前驱段,prev_group.iloc[0] 为 NaN,自然不满足 == 1,安全;
- 若需扩展规则(如“3 段接在 2 段后则变 2”),只需调整 m1 和 m2 的条件组合;
- 对含 NaN 的原始列需预处理(如 fillna()),避免 ffill 行为异常。
此模式可泛化为任意“基于前一段特征修改当前段”的场景,是 Pandas 序列规则处理的高阶实践范式。











