4. 异常值突跳的滑动窗口检测与标记
突跳检测的核心:用滑动窗口建立“局部正常基线”
在线监测序列中的突跳(spike)表现为单点或极短时段内数值突然偏离邻近水平,随后又快速回落。它与零点漂移(缓慢基线偏移)、量程超限(持续饱和)不同,关键特征是局部性:异常只影响很短的一段,前后数据仍处于正常波动范围。因此检测思路不是拿全序列均值做比较,而是用滑动窗口在每一时刻构造一个“局部参考值”,再判断当前点偏离该参考值是否超过阈值。
本例采用滑动中位数 + MAD(中位数绝对偏差)作为窗口统计量。相比均值与标准差,中位数和 MAD 对窗口内混入的异常点不敏感,不会被突跳本身抬高基线或撑大阈值,从而避免“异常点掩盖自己”。判定规则为:
对时刻 i,取窗口 W = [i-k, i+k](不含 i 本身,或含 i 但用稳健统计)
ref_i = median(W)
mad_i = median(|x - ref_i| for x in W)
score_i = |x_i - ref_i| / (1.4826 * mad_i + eps)
若 score_i > T(如 T=3.5),标记 x_i 为突跳
其中 1.4826 是使 MAD 在正态分布下与标准差一致的比例因子,eps 防止零方差时除零。窗口长度 2k+1 直接决定“局部”的范围:窗口太短,正常波动容易被误判;窗口太长,突跳会稀释在大量正常点中,score 被压低而漏检。
可运行的检测与标记实现
下面代码用纯 Python 实现窗口检测,并对比三种窗口长度。数据为教学示例:在平稳基线上叠加小幅噪声,再人为注入 4 个突跳点(含 1 个负向突跳),用于观察检出效果。
import statistics as st
def sliding_spike_detect(series, k, threshold=3.5, eps=1e-9):
n = len(series)
flags = [False] * n
scores = [0.0] * n
for i in range(n):
lo, hi = max(0, i - k), min(n, i + k + 1)
window = [series[j] for j in range(lo, hi) if j != i]
if len(window) < 3:
continue
ref = st.median(window)
mad = st.median([abs(x - ref) for x in window])
scale = 1.4826 * mad + eps
scores[i] = abs(series[i] - ref) / scale
flags[i] = scores[i] > threshold
return flags, scores
# 教学示例序列:基线 50,噪声 ±0.6,注入突跳
base = [50.0, 50.4, 49.7, 50.2, 50.1, 49.9, 50.3, 50.0,
50.2, 49.8, 50.1, 50.3, 49.9, 50.0, 50.2, 49.8,
50.1, 50.0, 49.9, 50.3, 50.1, 49.7, 50.2, 50.0]
base[6] = 58.5 # 正向突跳
base[13] = 41.0 # 负向突跳
base[18] = 56.2 # 正向突跳
base[22] = 44.8 # 负向突跳
for k in (2, 4, 7):
flags, scores = sliding_spike_detect(base, k)
hit = [i for i, f in enumerate(flags) if f]
print(f"k={k}, 窗口={2*k+1}, 检出点={hit}, 数量={len(hit)}")
预期输出(示例数据可直接推出):k=2 时窗口仅 5 点,噪声波动被放大,可能出现误报;k=4 时窗口 9 点,4 个注入突跳应全部检出且误报少;k=7 时窗口 15 点,突跳在窗口中占比降低,score 被稀释,部分幅度较小的突跳可能漏检。实际运行结果以本地执行为准,这里给出的是基于规则的分析预期。
窗口长度对检出效果的影响
窗口长度不是越大越好,也不是越小越灵敏,它需要在灵敏度与稳健性之间取平衡。判断依据应同时看两个指标:
- 召回率:注入的突跳中被标记的比例,反映漏检程度。
- 误报数:正常点被标记的数量,反映对噪声的敏感程度。
实践中可先用已知注入点的历史片段做一次“回放标定”:固定阈值 T,扫描 k 的取值,选召回率高且误报可接受的最小窗口。若序列采样间隔为 1 min,突跳通常持续 1~2 个点,窗口取 7~11 点(k=3~5)较常见;若采样间隔为 1 h,突跳可能持续数小时,窗口应相应加长。
标记结果与人工复核要点
检测输出的是“候选突跳点”,不应直接删除或替换。正确做法是先标记、后复核:在数据表中增加 spike_flag 与 score 两列,保留原始值,供后续插补或剔除时追溯。复核时重点看三类情况:
- 连续多点被标记:若相邻 3 个以上点同时超阈值,更可能是真实事件(如短时排放峰值)或传感器故障,而非单点突跳,应转人工判断。
- 正负交替突跳:同一时段正负方向反复跳变,常见于接触不良或电磁干扰,需结合设备状态排查,而不是简单标记了事。
- 阈值边界点:score 在 3.0~3.5 之间的点建议保留观察,不强行标记,避免把正常波动误判为异常。
scale = max(1.4826·MAD, 0.5)),或改用固定绝对阈值兜底。