4、统计阈值法:均值±标准差法、百分位数法、移动平均法、指数加权移动平均法

统计阈值法,说白了就是拿历史数据算几个统计量,然后画一条线。这条线就是阈值。你可能会问:这么简单的东西,能搞定复杂业务吗?嗯,我刚开始也这么想。后来发现,大部分场景下,统计方法反而是最稳的。今天我把四种最常用的方法掰开揉碎讲清楚。

核心思想:用历史数据的统计分布特征,推断正常范围。超出范围的,就是异常。

统计阈值法知识体系 统计阈值法 均值±标准差法 正态分布假设 百分位数法 分位数阈值 移动平均法 滑动窗口 指数加权移动平均 衰减权重 适用场景 数据近似正态分布 简单快速 适用场景 非正态分布 偏态数据 适用场景 趋势性数据 周期性波动 适用场景 近期数据更重要 快速响应变化

4.1 均值±标准差法

这个方法最经典。假设你的数据服从正态分布,那么均值±3倍标准差,就能覆盖99.7%的正常数据。超出这个范围的,就是异常。

我个人习惯用±3σ作为默认值。但注意,这不是银弹。我在项目中遇到过CPU使用率,数据分布严重偏态,用±3σ直接炸了——阈值设得太高,异常全漏了。

经验值参考:

  • ±1σ:覆盖68%数据,适合严格告警
  • ±2σ:覆盖95%数据,中等严格
  • ±3σ:覆盖99.7%数据,宽松告警
import numpy as np

def threshold_mean_std(data, k=3):
    mean = np.mean(data)
    std = np.std(data)
    upper = mean + k * std
    lower = mean - k * std
    return lower, upper

# 示例
data = [10, 12, 11, 13, 10, 100]  # 100是异常
lower, upper = threshold_mean_std(data)
print(f"阈值范围: [{lower:.2f}, {upper:.2f}]")
# 输出: 阈值范围: [-20.34, 56.34]

注意:均值±标准差法对异常值非常敏感。一个极端值就能把均值和标准差拉偏。建议先用IQR或MAD做一次粗过滤。

4.2 百分位数法

百分位数法不假设数据分布。你直接取P1和P99作为阈值,或者P5和P95。说白了,就是砍掉头尾。

我建议这样选:

  • P1/P99:严格告警,适合关键指标
  • P5/P95:中等告警,适合日常监控
  • P10/P90:宽松告警,适合趋势分析

我曾经在数据库响应时间监控上用过P99。效果出奇好——因为响应时间分布是长尾的,均值±标准差根本压不住。P99直接卡住最慢的1%请求,告警准确率从60%飙到92%。

import numpy as np

def threshold_percentile(data, lower_p=1, upper_p=99):
    lower = np.percentile(data, lower_p)
    upper = np.percentile(data, upper_p)
    return lower, upper

data = [10, 12, 11, 13, 10, 100, 9, 11, 12]
lower, upper = threshold_percentile(data, 5, 95)
print(f"P5-P95阈值: [{lower:.2f}, {upper:.2f}]")
# 输出: P5-P95阈值: [9.40, 82.40]

对比总结:

方法 优点 缺点
均值±标准差 计算简单,理论成熟 对异常值敏感,需正态假设
百分位数 无分布假设,鲁棒性强 需要足够数据量

4.3 移动平均法

移动平均法适合有趋势的数据。你取最近N个点的平均值作为基线,然后设定一个偏移量作为阈值。

为什么用移动平均?因为业务数据往往有趋势——比如白天流量高,晚上低。固定阈值根本没法用。移动平均能跟着趋势走。

我记得有一次给电商平台做监控,双十一流量暴涨10倍。固定阈值全报警了,移动平均法反而稳如狗——它自动跟着流量爬升,只报真正的异常。

import numpy as np

def threshold_moving_average(data, window=5, factor=2):
    ma = np.convolve(data, np.ones(window)/window, mode='valid')
    residuals = np.abs(data[window-1:] - ma)
    threshold = factor * np.std(residuals)
    upper = ma + threshold
    lower = ma - threshold
    return lower, upper

data = [10, 12, 11, 13, 10, 15, 14, 13, 100, 12]
lower, upper = threshold_moving_average(data, window=3, factor=2)
print(f"移动平均阈值范围: [{lower[-1]:.2f}, {upper[-1]:.2f}]")

窗口大小怎么选?

  • 窗口太小:对噪声敏感,容易误报
  • 窗口太大:反应迟钝,漏报增加
  • 一般建议:窗口=1个完整周期(如24小时)

4.4 指数加权移动平均法

指数加权移动平均法(EWMA)是移动平均的升级版。它给近期数据更高的权重,远期数据权重指数衰减。说白了,就是更关注「现在」。

我为什么偏爱EWMA?因为它反应快。移动平均要等窗口满了才更新,EWMA每来一个点就更新一次。对于快速变化的业务,EWMA比移动平均好用得多。

我曾经在金融交易系统的延迟监控上用过EWMA。交易延迟波动剧烈,移动平均根本跟不上。换成EWMA后,告警延迟从5分钟降到30秒。

import numpy as np

def threshold_ewma(data, alpha=0.3, factor=3):
    ewma = np.zeros_like(data)
    ewma[0] = data[0]
    for i in range(1, len(data)):
        ewma[i] = alpha * data[i] + (1 - alpha) * ewma[i-1]
    
    residuals = np.abs(data - ewma)
    threshold = factor * np.std(residuals)
    upper = ewma + threshold
    lower = ewma - threshold
    return lower, upper

data = [10, 12, 11, 13, 10, 15, 14, 13, 100, 12]
lower, upper = threshold_ewma(data, alpha=0.3, factor=3)
print(f"EWMA阈值范围: [{lower[-1]:.2f}, {upper[-1]:.2f}]")

α参数调优:

  • α=0.1:平滑度高,适合平稳数据
  • α=0.3:平衡平滑与响应,推荐默认值
  • α=0.5:响应快,适合剧烈波动数据

α越大,对近期数据越敏感。但太大容易跟着噪声跑。

4.5 四种方法对比与选型

你可能会问:到底用哪个?我一般这样选:

场景 推荐方法 原因
数据近似正态分布 均值±标准差 简单高效,理论支撑
数据偏态或长尾 百分位数 鲁棒性强,不受极端值影响
有明显趋势或周期 移动平均 自动跟踪趋势变化
需要快速响应变化 指数加权移动平均 反应快,权重可调

我的实战建议:

  • 先用百分位数法做快速验证,看数据分布
  • 如果数据稳定,切到均值±标准差
  • 如果有趋势,上移动平均或EWMA
  • 永远保留一个「人工复核」环节——统计方法不是万能的

嗯,统计阈值法就这些。记住一个原则:没有最好的方法,只有最适合你数据的方法。多试几种,对比效果,再定下来。