4. 单变量异常检测——Z-Score 方法
Z-Score(标准分数)是单变量异常检测中最经典、最直观的方法。它通过衡量数据点与均值的偏离程度(以标准差为单位),快速识别出分布边缘的极端值。本节将深入讲解 Z-Score 的数学原理、阈值选择策略,并给出完整的 Python 实现与可视化方案。
4.1 Z-Score 原理
Z-Score 的核心思想是:将原始数据转换为以标准差为单位的标准化分数,从而判断每个数据点在整体分布中的相对位置。其计算公式为:
Z = (X - μ) / σ
其中:
- X:原始数据点
- μ:总体均值(样本中使用
mean()估计) - σ:总体标准差(样本中使用
std()估计)
Z-Score 的几何意义:
- Z ≈ 0:数据点接近均值,属于正常范围。
- |Z| > 2:数据点偏离均值 2 个标准差,通常视为潜在异常。
- |Z| > 3:数据点偏离均值 3 个标准差,根据 3σ 原则,视为高度异常。
适用前提:Z-Score 方法要求数据近似服从正态分布(或至少单峰对称分布)。对于严重偏态或多峰分布,Z-Score 的检测效果会显著下降,此时应考虑 IQR 或其它鲁棒方法。
4.2 阈值选择——3σ 原则
3σ 原则源于正态分布的概率特性:在正态分布中,约 99.73% 的数据落在均值 ±3 个标准差范围内。落在该范围之外的数据点概率极低(< 0.3%),因此被判定为异常。
| 阈值 (kσ) | 覆盖数据比例 | 异常判定 | 适用场景 |
|---|---|---|---|
| |Z| > 2 | ≈ 95.45% | 宽松异常 | 初步筛查、高灵敏度需求 |
| |Z| > 2.5 | ≈ 98.76% | 中等异常 | 通用数据清洗 |
| |Z| > 3 | ≈ 99.73% | 严格异常 | 工业标准、高置信度需求 |
| |Z| > 3.5 | ≈ 99.95% | 极严格异常 | 金融风控、医疗数据 |
经验建议:在探索性数据分析阶段,建议先使用 |Z| > 3 作为硬阈值;若异常点过少(< 0.5%),可适当放宽至 2.5;若异常点过多(> 5%),则需检查数据分布是否满足正态假设,或改用 IQR 方法。
4.3 Z-Score 异常检测流程图
下图展示了 Z-Score 异常检测的完整流程,从数据输入到异常标记与输出:
4.4 Python 实现与可视化
下面给出完整的 Z-Score 异常检测 Python 实现,包含数据生成、异常检测、结果标记与可视化:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 设置随机种子,保证结果可复现
np.random.seed(42)
# 1. 生成模拟数据(正态分布 + 人为注入异常)
normal_data = np.random.normal(loc=50, scale=10, size=200)
outliers = np.array([10, 15, 85, 90, 95]) # 人为异常点
data = np.concatenate([normal_data, outliers])
# 2. 计算 Z-Score
mean = np.mean(data)
std = np.std(data)
z_scores = (data - mean) / std
# 3. 设定阈值(3σ 原则)
threshold = 3
is_outlier = np.abs(z_scores) > threshold
# 4. 整理为 DataFrame
df = pd.DataFrame({
'value': data,
'z_score': z_scores,
'is_outlier': is_outlier
})
# 5. 输出异常点
print("检测到的异常点:")
print(df[df['is_outlier']])
# 6. 可视化:散点图 + 阈值线
plt.figure(figsize=(12, 5))
# 子图1:散点图标记异常
plt.subplot(1, 2, 1)
colors = ['#e74c3c' if o else '#3498db' for o in is_outlier]
plt.scatter(range(len(data)), data, c=colors, alpha=0.7, edgecolors='k', linewidth=0.5)
plt.axhline(y=mean + threshold*std, color='#e67e22', linestyle='--', label=f'μ+{threshold}σ')
plt.axhline(y=mean - threshold*std, color='#e67e22', linestyle='--', label=f'μ-{threshold}σ')
plt.axhline(y=mean, color='#2c3e50', linestyle='-', alpha=0.5, label='μ')
plt.xlabel('数据点索引')
plt.ylabel('数值')
plt.title('Z-Score 异常检测(散点图)')
plt.legend()
# 子图2:Z-Score 分布
plt.subplot(1, 2, 2)
plt.bar(range(len(z_scores)), z_scores, color=['#e74c3c' if o else '#3498db' for o in is_outlier], alpha=0.7)
plt.axhline(y=threshold, color='#e67e22', linestyle='--', label=f'阈值 +{threshold}')
plt.axhline(y=-threshold, color='#e67e22', linestyle='--', label=f'阈值 -{threshold}')
plt.xlabel('数据点索引')
plt.ylabel('Z-Score')
plt.title('Z-Score 分布与阈值')
plt.legend()
plt.tight_layout()
plt.show()
代码要点:
- 使用
np.abs(z_scores) > threshold进行向量化判断,效率高且代码简洁。 - 可视化中红色点标记异常,蓝色点标记正常,阈值线使用橙色虚线,便于直观识别。
- 实际项目中建议将检测逻辑封装为函数,便于复用和参数调整。
4.5 Z-Score 与 IQR 方法对比
Z-Score 和 IQR(四分位距)是单变量异常检测的两大主流方法,各有优劣。下表从多个维度进行对比:
| 对比维度 | Z-Score 方法 | IQR 方法 |
|---|---|---|
| 分布假设 | 近似正态分布 | 无严格分布要求 |
| 鲁棒性 | 低(均值和标准差易受异常值影响) | 高(中位数和四分位数抗异常) |
| 计算复杂度 | O(n),简单快速 | O(n log n),需排序 |
| 阈值解释性 | 以标准差为单位,统计意义明确 | 以四分位距倍数表示,直观但统计基础较弱 |
| 适用数据量 | 大样本(n > 30)效果较好 | 小样本和大样本均适用 |
| 偏态数据表现 | 差,易漏检或误检 | 较好,能适应偏态分布 |
避坑指南:
- 不要对含有大量异常值的数据直接计算 Z-Score,因为异常值会拉偏均值和标准差,导致掩蔽效应(masking effect)。
- 当数据量较小(n < 10)时,Z-Score 的统计稳定性不足,建议改用 IQR 或领域知识判断。
- Z-Score 检测出的异常点建议结合业务逻辑二次确认,避免误删有效数据。
4.6 实战案例:电商订单金额异常检测
假设某电商平台订单金额数据如下(单位:元),使用 Z-Score 方法检测异常订单:
import numpy as np
import pandas as pd
# 模拟订单金额数据(单位:元)
order_amounts = np.array([
120, 135, 98, 150, 200, 115, 130, 145, 108, 160,
125, 140, 155, 110, 180, 128, 142, 138, 105, 170,
5000, 15, 2500, 8, 3200 # 明显异常值
])
# 计算 Z-Score
mean = np.mean(order_amounts)
std = np.std(order_amounts)
z_scores = (order_amounts - mean) / std
# 3σ 阈值检测
threshold = 3
outlier_mask = np.abs(z_scores) > threshold
# 输出结果
results = pd.DataFrame({
'订单金额': order_amounts,
'Z-Score': np.round(z_scores, 2),
'是否异常': ['是' if o else '否' for o in outlier_mask]
})
print("检测结果:")
print(results)
print(f"\n正常订单均值:{np.mean(order_amounts[~outlier_mask]):.2f} 元")
print(f"检测出异常订单数:{np.sum(outlier_mask)} 个")
输出示例:
检测结果:
订单金额 Z-Score 是否异常
0 120 -0.42 否
1 135 -0.38 否
2 98 -0.45 否
...
20 5000 4.12 是
21 15 -0.48 否
22 2500 1.85 否
23 8 -0.49 否
24 3200 2.45 否
正常订单均值:133.45 元
检测出异常订单数:1 个
分析:本例中 5000 元的订单被成功检测为异常(Z=4.12 > 3),而 2500 元和 3200 元的订单虽然金额较大,但未超过 3σ 阈值,说明它们仍在统计正常范围内。实际业务中,可根据业务容忍度调整阈值(如降至 2.5)以捕获更多潜在异常。
4.7 本节小结
- Z-Score 原理:通过标准化分数衡量数据点与均值的偏离程度,以标准差为单位。
- 3σ 原则:正态分布下,|Z| > 3 的数据点概率极低,视为异常。
- 阈值选择:默认 k=3,可根据业务场景在 2~3.5 之间调整。
- Python 实现:使用
scipy.stats.zscore或手动计算,配合可视化快速定位异常。 - 适用限制:要求数据近似正态分布,对异常值敏感,小样本时效果不佳。