第4章 无监督异常检测(一):统计学方法 — Z-score、IQR、箱线图
统计学方法,是异常检测里最经典的一类。说白了,就是拿数学公式去量每个点“有多离谱”。我早期做金融风控的时候,第一个落地项目用的就是 Z-score,简单、快、解释性强。这章咱们把三个最常用的工具过一遍:Z-score、IQR 和箱线图。
4.1 Z-score — 标准分数抓离群点
Z-score 的核心思路:看一个点到均值的距离,比上标准差。公式长这样:
Z = (x - μ) / σ
其中 μ 是均值,σ 是标准差。Z 的值越大,说明这个点越“特立独行”。通常阈值取 3,即 |Z| > 3 判定为异常。
举个例子:一组客户交易金额,均值 500,标准差 100。某笔交易 1200,Z = (1200-500)/100 = 7 > 3,妥妥异常。
但 Z-score 有个大坑——它对均值、标准差本身敏感。如果异常点本身已经把均值拉偏了,那 Z-score 就会漏检。我以前在电商销量监控里就吃过这个亏。
4.2 IQR — 四分位距的鲁棒方案
IQR 不依赖均值和标准差,它用中位数和四分位数。公式:
IQR = Q3 - Q1
下界 = Q1 - 1.5 * IQR
上界 = Q3 + 1.5 * IQR
超出上下界的,判为异常。系数 1.5 是 Tukey 老爷子定的经典值。可以用 3 倍做极端离群点。
为什么 IQR 更鲁棒?因为中位数和四分位数不怕极端值干扰。我做过一个传感器数据清洗项目,传感器偶尔飘高,均值被拉上天,Z-score 全废。换成 IQR 后,瞬间稳了。
| 方法 | 对异常敏感 | 假设分布 | 计算量 | 适用场景 |
|---|---|---|---|---|
| Z-score | 高 | 近似正态 | 低 | 数据无偏、量纲统一 |
| IQR | 低(鲁棒) | 任意连续 | 低 | 含离群点、分布偏态 |
4.3 箱线图 — 可视化你的异常
箱线图是 IQR 的图形化表达。盒子从 Q1 到 Q3,中间线是中位数,胡子延伸到上下界之外的点就是异常。
我通常把箱线图当作探索性分析的快照。拿到新数据,先画箱线图扫一眼,能看到:
- 哪些变量离群点多
- 分布是否对称
- 有没有极端值拖拽均值
箱线图虽然直观,但阈值是固定的(1.5×IQR)。不能灵活适应业务场景。所以我会在建模前用它做“初筛”,上线时还是用动态阈值。
4.4 三种方法联合诊断流程
在实际项目中,我不会只依赖一种方法。我会这样做:
- 先用箱线图全量扫一遍 —— 可视化看分布形态和异常点位置
- 对可疑点算 Z-score —— 看偏了多少个标准差
- 用 IQR 验证异常边界 —— 避免均值被污染影响判断
- 业务确认+标记 —— 最终异常由人做决策
这样组合下来,误报率比单用任何一种都低。我管这叫“三保险”。
好,这张图把核心逻辑串起来了:
4.5 代码速写 — Python 实现
动手跑一下,感觉就来了。下面这个例子,我刻意混入了一个异常值:
import numpy as np
import pandas as pd
# 生成数据:正态分布 + 1个异常
np.random.seed(42)
data = np.random.normal(50, 10, 100)
data = np.append(data, 150) # 异常值
# Z-score
mean = np.mean(data)
std = np.std(data)
z_scores = (data - mean) / std
outliers_z = data[np.abs(z_scores) > 3]
print("Z-score 异常:", outliers_z)
# IQR
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
iqr = Q3 - Q1
lower = Q1 - 1.5 * iqr
upper = Q3 + 1.5 * iqr
outliers_iqr = data[(data < lower) | (data > upper)]
print("IQR 异常:", outliers_iqr)
跑一下你会发现,Z-score 和 IQR 都能抓到 150 这个异常。但如果再多几个离群点,IQR 往往更稳定。我自己跑生产数据时,偏爱 IQR 做第一道防线。
4.6 优缺点总结
| 方法 | 优点 | 缺点 |
|---|---|---|
| Z-score | 计算快、直观、有统计理论支撑 | 对非正态分布不友好、均值易被污染 |
| IQR | 鲁棒、不依赖分布、实现简单 | 阈值固定(1.5×IQR)、无法自适应 |
| 箱线图 | 可视化易懂、便于探索 | 无法自动化、大数据量慢 |
嗯,这一块的内容先到这。多动手调一下阈值参数,你会对数据分布有更深的体感。