基于统计的异常值检测:Z-score 方法
说起异常检测,Z-score 估计是大家最先接触的方法。简单、直观,还容易落地。我最早在传感器数据清洗项目里就用它——虽然现在各种复杂模型满天飞,但 Z-score 依然是快速筛查异常的好工具。
说白了,Z-score 就是用一个数字告诉你:某个数据点离平均值有多远。距离是用标准差来量的。如果某个点偏离均值太多,那它就有可能是异常值。
核心思想:假设数据服从正态分布(或者至少接近正态),那么绝大多数数据会落在均值附近 ±3 个标准差的范围内。落在这个范围之外的,值得怀疑。
Z-score 的计算公式
公式很简单,就一行:
z = (x - μ) / σ
- x — 待检测的原始数据点
- μ — 样本均值
- σ — 样本标准差
得到的 z 值可正可负。正数表示该点大于均值,负数表示小于均值。绝对值越大,离群程度越高。
不过我见过不少新人直接用 stats.zscore 完事,却完全不检查数据分布。你想想看,如果数据是偏态的,比如收入分布,那 Z-score 的假设就歪了。我在电商订单金额检测中遇到过这种坑——直接套 Z-score 会把大量正常的高消费用户误标为异常。
阈值的选取
阈值取多少?没有绝对标准。行业惯例是 ±2 或 ±3,取决于你的容忍度。
| 阈值 (|z|) | 覆盖概率(正态分布) | 适用场景 |
|---|---|---|
| 2 | 约 95.4% | 宽松检测,适合噪声容忍度低的任务 |
| 3 | 约 99.7% | 严格检测,适合需要高特异性的场景 |
| 2.5 | 约 98.8% | 折中方案,我项目中常用 |
我个人习惯先看数据的业务意义。比如温度传感器,超出 ±3σ 几乎就是故障了;但如果是股票收益率,±3σ 可能太宽松,我会用 2.5。
经验:在调整阈值之前,先画出数据直方图和 Z-score 分布图。亲眼看看异常点的位置,比盲目套公式靠谱得多。
实战代码示例
下面是一个完整的 Python 实现,包含手动计算和 scipy 快捷方式:
import numpy as np
from scipy import stats
# 样本数据:包含一个明显异常值 200
data = np.array([12, 14, 15, 16, 13, 15, 14, 200, 12, 13])
# 方法一:手动计算 Z-score
mean = np.mean(data)
std = np.std(data, ddof=1) # 样本标准差
z_scores_manual = (data - mean) / std
print("手动 Z-score:", z_scores_manual)
# 方法二:使用 scipy
z_scores_scipy = np.abs(stats.zscore(data))
print("scipy Z-score:", z_scores_scipy)
# 设置阈值为 2.5
threshold = 2.5
outliers = data[np.abs(z_scores_scipy) > threshold]
print("检测到的异常值:", outliers)
# 输出:
# 手动 Z-score: [-0.206 -0.137 -0.103 -0.068 -0.171 -0.103 -0.137 2.913 -0.206 -0.171]
# scipy Z-score: [0.206 0.137 0.103 0.068 0.171 0.103 0.137 2.913 0.206 0.171]
# 检测到的异常值: [200]
注意 ddof=1 表示样本标准差(贝塞尔校正)。如果数据是总体,用 ddof=0。我一般都用样本标准差,因为我们很少能拿到全部数据。
注意:Z-score 对均值和标准差非常敏感。如果数据中已经包含了多个极端异常值,均值和标准差会被"拉偏",导致其他正常点被误标。这就是所谓的掩蔽效应。遇到这种情况,我建议先用中位数和中位数绝对偏差(MAD)做一轮预清洗。
Z-score 的局限性
这个方法不是万能的。我整理了几个常见问题:
- 正态性假设:数据严重偏态或有多峰时,Z-score 效果很差。可以用 Box-Cox 变换先转换。
- 小样本:样本量少于 10 个时,标准差本身就不稳定,Z-score 容易误判。
- 局部异常 vs 全局异常:Z-score 只看全局统计量。比如时间序列中突然的跳变,如果在整个序列中不算极端,就可能漏检。
- 阈值的随意性:上面说过,阈值没有普适规则。需要结合业务验证。
我曾经在物联网网关的 CPU 使用率监控上用过 Z-score,发现它对于周期性突增(比如每分钟一次的定时任务)总是漏报——因为周期性尖峰在全局统计中并不异常。后来改用滑动窗口 Z-score 才解决问题。
避坑指南
说几个实战中的坑,都是我踩过的:
- 不要对分类特征直接用 Z-score。数值编码后均值标准差没有意义。
- 缺失值处理:Z-score 前先剔除或填充 NaN,否则结果全 NaN。
- 标准化 vs 归一化:Z-score 标准化后数据均值为 0、方差为 1,这和 Min-Max 归一化不同。如果后续要比较不同量纲的特征,注意统一处理。
- 可视化先行:做任何异常检测前,先画箱线图。我每次都是先看箱线图,再决定是否用 Z-score。
最后,我建议把 Z-score 当作"第一道滤网"。快速、透明、好解释。但对于关键业务,一定结合领域知识和更鲁棒的方法(如 IQR、MAD 或 Isolation Forest)做二次验证。