3. 统计方法:基于3-Sigma、箱线图(IQR)的简单异常检测
说到异常检测,很多人第一反应就是上机器学习模型。但我个人习惯,先拿统计方法扫一遍数据。为什么?因为简单、快、可解释性强。你想想看,业务方问你「为什么这个用户被标记为异常」,你能说「模型告诉我的」吗?但如果你说「这个用户的交易金额偏离了均值3个标准差」,对方一听就懂。
这一章,我们就聊聊两个最经典的统计方法:3-Sigma 和 箱线图(IQR)。它们虽然简单,但在实际项目中,我遇到过不少坑,今天一并分享给你。
3.1 3-Sigma:正态分布下的黄金法则
3-Sigma 的原理其实特别朴素。它假设你的数据服从正态分布(或者近似正态)。在正态分布里,大约 99.7% 的数据会落在均值 ±3 个标准差范围内。那剩下的 0.3% 呢?嗯,它们就是潜在的异常点。
公式很简单:
下界 = μ - 3σ
上界 = μ + 3σ
任何超出这个范围的值,我都标记为异常。
核心要点:3-Sigma 对数据分布敏感。如果数据严重偏态,或者有多个峰值,直接用 3-Sigma 会误判很多正常点。
我在项目中遇到过一件事。有一次做电商平台的支付金额异常检测,直接用 3-Sigma 一跑,发现大量「异常」。仔细一看,原来是双十一当天的正常大额交易。数据分布根本不是正态的,而是长尾分布。所以,用 3-Sigma 前,先看看数据长什么样。
代码示例:Python 实现 3-Sigma
import numpy as np
import pandas as pd
def detect_3sigma(data, column):
mean = data[column].mean()
std = data[column].std()
lower_bound = mean - 3 * std
upper_bound = mean + 3 * std
anomalies = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return anomalies, lower_bound, upper_bound
# 使用示例
df = pd.DataFrame({'value': [10, 12, 11, 13, 100, 9, 11, 12, 10, 11]})
anomalies, low, high = detect_3sigma(df, 'value')
print(f"正常范围: [{low:.2f}, {high:.2f}]")
print(f"异常点数量: {len(anomalies)}")
我的小技巧:如果数据偏态严重,可以试试对数变换。把数据取 log 后再用 3-Sigma,效果会好很多。我曾经用这个方法处理过用户在线时长数据,准确率提升了 15%。
3.2 箱线图(IQR):不依赖分布的稳健方法
箱线图就不一样了。它不关心你的数据是不是正态分布。它只看四分位数。说白了,就是看数据的中位数、上四分位数(Q3)、下四分位数(Q1)。
异常判定标准:
IQR = Q3 - Q1
下界 = Q1 - 1.5 * IQR
上界 = Q3 + 1.5 * IQR
超出这个范围的点,就是箱线图眼中的异常。
为什么是 1.5 倍?这不是拍脑袋定的。我记得统计学里有个说法,对于近似正态分布的数据,1.5 倍 IQR 对应的概率大约和 3-Sigma 差不多。但它的优势在于:对极端值不敏感。你想想看,3-Sigma 的均值和标准差会被极端值拉偏,但箱线图用的是中位数和四分位数,稳健多了。
避坑指南:我曾经在金融风控场景里直接用默认的 1.5 倍 IQR,结果发现正常的高净值客户被大量误杀。后来我调整了系数,对高价值客户用了 3 倍 IQR,才把误报率降下来。所以,系数不是死的,要根据业务场景调。
代码示例:Python 实现 IQR 检测
def detect_iqr(data, column, multiplier=1.5):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - multiplier * IQR
upper_bound = Q3 + multiplier * IQR
anomalies = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return anomalies, lower_bound, upper_bound
# 使用示例
anomalies, low, high = detect_iqr(df, 'value', multiplier=1.5)
print(f"IQR 正常范围: [{low:.2f}, {high:.2f}]")
print(f"异常点: {anomalies['value'].tolist()}")
3.3 两种方法的对比与选择
我经常被问到:「到底该用 3-Sigma 还是 IQR?」我的回答是:看数据,看场景。
| 对比维度 | 3-Sigma | 箱线图(IQR) |
|---|---|---|
| 数据分布假设 | 要求近似正态 | 无要求,任意分布 |
| 对极端值敏感度 | 高(均值和标准差易被拉偏) | 低(基于中位数和四分位数) |
| 参数可调性 | 可调 Sigma 倍数(如 2.5、3.5) | 可调 IQR 倍数(如 1.5、2.0、3.0) |
| 适用场景 | 数据质量高、分布对称 | 数据偏态、含噪声、分布未知 |
| 计算复杂度 | 低 | 低 |
我个人习惯是:先用箱线图快速扫一遍。因为它不需要假设分布,不容易翻车。如果数据看起来比较「规整」,再试试 3-Sigma 做交叉验证。两个方法都标记为异常的点,那基本就是实锤了。
3.4 实际项目中的经验总结
最后,分享几个我在实战中踩过的坑和总结的经验:
- 不要只看单变量。 单变量的 3-Sigma 和 IQR 只能发现「数值过大或过小」的异常。但很多欺诈行为是组合异常,比如「金额正常但频率异常高」。这时候需要多变量方法,我们后面章节会讲。
- 分群后再检测。 我曾经做过一个信用卡交易检测,直接把所有交易放一起跑 IQR,结果全是正常的大额交易被标记。后来按用户等级分群,高等级用户单独设阈值,效果立竿见影。
- 阈值要动态调整。 业务是变化的。双十一的阈值和平时能一样吗?我建议定期(比如每周)重新计算均值和四分位数,或者用滑动窗口的方式更新。
- 可视化很重要。 画个箱线图或者直方图,一眼就能看出数据分布和异常点位置。不要只盯着数字看。
一句话总结:3-Sigma 和 IQR 是异常检测的「入门刀」,简单但锋利。用得好,能解决 80% 的简单场景。但别迷信它们,复杂场景还得上更高级的方法。
好了,这一章就到这里。下一章我们会聊聊「基于距离的异常检测方法」,比如 KNN 和 LOF。到时候你会发现,统计方法只是热身,真正的挑战还在后面。