本文介绍如何在 pandas 中基于分组数据,利用滚动窗口与反向传播技巧,高效标记长度 ≥3 且平均值 ≥80 的连续高置信度(c_p)序列,避免 o(n³) 暴力遍历。
本文介绍如何在 pandas 中基于分组数据,利用滚动窗口与反向传播技巧,高效标记长度 ≥3 且平均值 ≥80 的连续高置信度(c_p)序列,避免 o(n³) 暴力遍历。
在船舶停泊状态识别等时序分析任务中,常需判断某列数值(如置信度 C_p)是否存在长度至少为 N、均值不低于阈值的连续子序列,并将该子序列内所有元素标记为有效(如 is_parked = 1)。直接使用 Python 循环嵌套实现虽直观,但时间复杂度高达 O(n³),难以应对大规模轨迹数据。
Pandas 提供了更优雅、向量化且高性能的解决方案:结合 groupby、rolling 与逻辑传播机制。核心思路分为三步:
- 按 Group 分组处理,确保序列判断独立于不同船舶或航段;
- 对每组 C_p 应用长度为 N=3 的滚动均值,并用 .gt(threshold) 生成布尔掩码(均值 ≥ 80 → True);
- 关键技巧:反转序列后做滚动最大值(.rolling(N).max()),将每个满足条件的窗口右端点的 True 向左“扩散”覆盖整个窗口长度,再反转回原顺序(代码中通过 [::-1] 两次操作隐式完成逻辑对齐)。
以下是完整可运行示例:
import pandas as pd
# 构造示例数据
df = pd.DataFrame({
'Index': [1, 2, 3, 4, 5, 6, 7, 8, 9],
'Group': [1, 1, 1, 1, 1, 1, 3, 3, 3],
'C_p': [27, 85, 83, 78, 66, 47, 82, 80, 66]
})
N = 3
threshold = 80
df['is_parked'] = (
df.groupby('Group')['C_p']
.transform(lambda s:
s.rolling(N).mean().gt(threshold)[::-1]
.rolling(N, min_periods=1).max()
.astype(int)
)
)
print(df)输出结果与预期一致:
Index Group C_p is_parked 0 1 1 27 0 1 2 1 85 1 2 3 1 83 1 3 4 1 78 1 4 5 1 66 0 5 6 1 47 0 6 7 3 82 0 7 8 3 80 0 8 9 3 66 0
✅ 为什么有效?
- s.rolling(3).mean() 计算每组内长度为 3 的滑动窗口均值(前两行因不足 3 个元素返回 NaN);
- .gt(80) 将均值 ≥ 80 的位置转为 True,其余为 False;
- [::-1] 反转后,再用 .rolling(3).max() 实现“向左填充”:只要窗口内任一 True,整个窗口即标记为 True;
- 最终 .astype(int) 转为 0/1 整数,完美匹配业务需求。
⚠️ 注意事项:
- 该方法假设同一 Group 内数据已按时间/空间顺序排列(如索引递增),否则需先排序;
- 若存在 NaN 值,rolling(...).mean() 默认跳过,但建议提前清洗或设置 min_periods 参数控制容错性;
- 对超长序列(如万级点位),此方案仍保持 O(n) 时间复杂度,远优于原始三重循环。
综上,无需切换至 NumPy 或纯 Python 循环——Pandas 原生的 rolling + groupby.transform 组合,即可简洁、高效、可读地解决此类“带长度约束的滑动均值标记”问题。

















