统计方法一:Z-score 与 3σ 原则 – 原理、实现、适用条件
异常检测最朴素的思路是什么?
说白了,就是看哪个数据点「离群」了。离群有多远?多远算异常?Z-score 和 3σ 原则 就是回答这两个问题的入门级统计工具。我自己做异常检测项目时,80% 的场景先拿它扫一遍,快速过滤明显离群点,再上复杂模型。别小看它,简单不等于没用。
1. Z-score 的本质:把距离标准化
Z-score 干的事很直接:告诉你这一个数据点距离均值有几个标准差。
公式我写过无数次了:
Z = (x - μ) / σ
其中 μ 是均值,σ 是标准差。
Z-score 为 2 代表该点比均值高两个标准差,-1.5 代表比均值低 1.5 个标准差。数值本身没量纲,不同尺度的特征可以直接比较 Z 值大小。
2. 3σ 原则:正态分布下的经验法则
3σ 原则基于正态分布的性质:
- 约 68% 的数据落在 μ ± 1σ 内
- 约 95% 落在 μ ± 2σ 内
- 约 99.7% 落在 μ ± 3σ 内
你想想看,如果一个点离均值超过 3 个标准差,它属于正常分布的概率只有 0.3%。所以 3σ 原则一刀切:超过 3σ 就算异常。
| 范围 | 概率覆盖 | 通常处理 |
|---|---|---|
| |Z| < 2 | 约 95% | 正常点,保留 |
| 2 ≤ |Z| < 3 | 约 4.7% | 可疑,需要关注 |
| |Z| ≥ 3 | 约 0.3% | 异常候选,标记或剔除 |
我建议你记住这个表格,面试常问,实际调参也经常拿它做起点。
3. 实战:Python 实现与两个坑
代码很简短,但有两个坑我踩过,先说清楚。
import numpy as np
import pandas as pd
def zscore_detect(data, threshold=3):
mean = np.mean(data)
std = np.std(data, ddof=0) # 总体标准差
z_scores = (data - mean) / std
return np.abs(z_scores) > threshold
如果你用样本标准差(ddof=1),分母会更小,Z-score 会更大,更容易把点判定为异常。我建议明确数据是总体还是样本,保持一致。我犯过一个低级错误,在公司数据上用了 ddof=0,结果线上数据分布微调,误报直接翻倍。
坑二:均值和标准差对异常值很敏感
一个极端离群点会把均值拉偏,把标准差拉大,导致其他本来正常的点变得「不明显」。我踩过最狠的坑是:一批数据里有一个极离谱的离群点,用 Z-score 检测它自己反而低于阈值。
4. Z-score 的变体与适用前提
工作中我用过两种 Z-score 变体:
- 标准 Z-score:上面代码那种,依赖均值与标准差。
- 稳健 Z-score:用中位数代替均值,用 MAD 代替标准差,对异常点更鲁棒。
三个适用前提,少一个就别用:
- 数据近似正态分布——偏态很严重时,3σ 会漏报或误报。
- 无严重离群点污染——否则均值被拉偏,检测不准。
- 单维或独立特征——Z-score 不关心特征间关系。
我记得有个厂家的传感器数据,分布明显右偏,用 Z-score 怎么也找不到尾部异常点。后来换成 IQR 方法,一下就抓住了。所以 前提不对,工具再简单也白搭。
5. 3σ 原则不能回答的三个问题
我见过的最离谱的案例:有人用 3σ 原则做多维异常检测,把每个维度独立算一遍,然后做「或」逻辑。结果每个维度都正常的点,组合起来却是异常(比如(温度低,压力高)组合)。这就是 维度灾难 + 忽视相关性。
3σ 原则不能回答:
- 多维异常——它不建模特征间的关系
- 序列模式异常——比如突然的波动趋势,Z-score 看不到
- 非对称阈值——有业务流程要求上下界不对称,3σ 原则要求对称
6. 我建议的实践步骤
如果你在项目里第一次用 Z-score,按这个顺序走:
- 绘制直方图或 Q-Q 图,确认数据是否近似正态。
- 计算 Z-score,先用 threshold=3 标记异常。
- 检查被标记点的占比:远大于 0.3%,说明数据有偏或阈值不合适。
- 汇总标记点业务标签,计算误报率。
- 根据误报情况把阈值微调到 2.5 或 3.5。
我见过的最快上线方案:用 Z-score 做预处理 + 人工复核,两周上线,误报率 1.3%。后来迭代加了 Isolation Forest,误报率降到 0.4%。但初始版本的成本不到三天。
图中虚线表示:如果数据不满足正态分布假设,就不要走 Z-score 路线,换 IQR 或稳健方法。我在项目中经常做这个决策,它比盲目套公式重要得多。