第4章 数据分布与统计基础:正态分布、中位数、标准差、百分位数
做质控Pipeline选阈值,说白了就是和数据分布打交道。你想想看,同一套算法在不同批次的数据上跑,指标值不可能完全一样。那阈值该定多少?拍脑袋不行,得从数据本身找规律。这一章我们就把几个最常用的统计量掰开揉碎讲清楚。
我个人习惯是先看数据长什么样,再选阈值。为什么?因为不同分布下,均值和中位数可能差很多,标准差和百分位数的用法也不一样。我在项目里吃过亏——一开始直接用均值设阈值,结果数据偏态严重,误报率飙到15%。后来换成中位数加百分位数,问题才解决。
4.1 正态分布:当数据符合“钟形曲线”时
正态分布是很多统计方法的起点。如果数据近似正态,那么均值、标准差就能很好描述整体。比如延迟时间、响应大小的自然波动通常都接近正态。
判断正态分布最简单的方法:看直方图是不是中间高、两边对称。也可以用Shapiro-Wilk或Kolmogorov-Smirnov检验。不过项目里我更常直接画QQ图——一条直线基本就是正态。
4.2 中位数 vs 均值:谁更适合做阈值基准?
均值对异常值敏感。一个极端值就能把均值拉偏。中位数则稳健得多,只关心中间位置。我在一个日志处理Pipeline里见过:某天因为网络抖动产生几个超长延迟,均值从50ms跳到120ms,但中位数只从48ms变为52ms。如果阈值基于均值,当天就全线告警,运维根本来不及分析。后来改成中位数+百分位数的组合,才算消停。
| 统计量 | 对异常值敏感度 | 适用场景 |
|---|---|---|
| 均值 | 高 | 数据干净、对称分布 |
| 中位数 | 低 | 含有噪声、偏态分布 |
嗯,这里要注意:如果你选中位数做基准,阈值偏移量(比如中位数+3σ)的σ也得换成基于中位数的绝对偏差(MAD),不能直接用标准差。
4.3 标准差:量化数据的“离散程度”
标准差告诉你数据离均值有多远。在正态分布下,约68%的数据在均值±1σ内,95%在±2σ内,99.7%在±3σ内。很多人直接拿3σ当阈值——但这要求数据严格正态,且不能有太多异常点。
我曾经把一个模型的推理延迟设为均值+3σ,结果每周都有上百次误告警。一查原因,延迟分布其实是左偏的(大部分请求很快,少数慢),3σ的尾巴被拖得太长。后来改用百分位数,才把误报率降下来。
4.4 百分位数:非参数阈值的最强工具
百分位数不依赖任何分布假设。你直接说“取第99百分位数”意思是:把数据从小到大排序,排在第99%位置的那个值就是阈值。这样无论数据长什么样,都能保证不超过1%的点被判定异常。
常用百分位数在质控里的映射:
- 第50百分位数(P50) —— 中位数,做基准
- 第75百分位数(P75) —— 轻度异常线,用于提醒
- 第95/99百分位数(P95/P99) —— 严重异常线,用于告警
- 第5/1百分位数(P5/P1) —— 下侧阈值,比如内存占用过低可能是进程异常
我个人习惯在自动化Pipeline里用滑动窗口计算P99作为动态阈值。窗口大小通常取最近1000个点或最近1小时,这样阈值能跟着业务流量自动调整。
4.5 实战:用Python快速计算
下面是我常用的代码片段。不管数据来自日志、数据库还是API,都能直接套用。
import numpy as np
# 模拟一组服务延迟数据(单位ms)
data = np.random.normal(100, 15, 1000)
# 均值与中位数
mean = np.mean(data)
median = np.median(data)
# 标准差
std = np.std(data)
# 关键百分位数
p50 = np.percentile(data, 50)
p75 = np.percentile(data, 75)
p95 = np.percentile(data, 95)
p99 = np.percentile(data, 99)
print(f"均值: {mean:.2f} 中位数: {median:.2f}")
print(f"标准差: {std:.2f}")
print(f"P50={p50:.2f} P75={p75:.2f} P95={p95:.2f} P99={p99:.2f}")
# 输出结果举例
# 均值: 100.23 中位数: 100.51
# 标准差: 15.12
# P50=100.51 P75=110.78 P95=125.34 P99=135.87
实际项目里,我还会加一步:先剔除明显异常点(比如延迟>10000ms的),再计算百分位数,否则P99会被脏数据污染。
4.6 本章要点速查
- 正态分布适合用均值+标准差,但工程数据很少完全正态
- 中位数抗噪声能力强,适合做动态基线
- 百分位数不依赖分布,P95/P99是质控最常用的异常线
- 滑动窗口计算百分位数可实现阈值自适应
- 代码里记得先清洗异常点,再算统计量
这一章就到这里。记住一句话:没有万能的统计量,只有适合你数据的统计量。多画图、多对比,阈值自然就选对了。