4. 数据预处理:异常值剔除、缺失值填充与数据归一化方法
在告警阈值 A/B 测试中,原始历史数据往往包含噪声、缺失记录和量纲差异。直接使用原始数据会导致阈值计算偏差、A/B 组对比失真。数据预处理是保证验证结论可靠的第一道防线,本章聚焦三类核心操作:异常值剔除、缺失值填充与数据归一化。
4.1 异常值剔除:识别与处理离群点
告警指标中的异常值通常由瞬时故障、采集抖动、人为误操作引起。若不剔除,会拉高或拉低阈值基线,导致误告警或漏告警。常用方法包括 IQR 规则与 Z-Score 规则。
| 方法 | 适用场景 | 判定条件 | 优点 | 局限 |
|---|---|---|---|---|
| IQR(四分位距) | 非正态分布、偏态数据 | 值 < Q1 - 1.5×IQR 或 > Q3 + 1.5×IQR | 不受极端值影响,稳健 | 对尾部数据不敏感 |
| Z-Score | 近似正态分布 | |Z| > 3(或 2.5) | 计算简单,可调阈值 | 受异常值本身影响 |
| MAD(中位数偏差) | 含较多离群点 | 值 < median - 3×MAD 或 > median + 3×MAD | 比 Z-Score 更稳健 | 计算稍复杂 |
实践建议:对于 CPU 使用率、内存占用等有明确上下界的指标,优先使用 IQR 或 MAD;对于请求延迟、错误率等长尾分布指标,建议先做对数变换再使用 Z-Score。
4.2 缺失值填充:保持时序完整性
告警数据采集链路中,网络抖动、采集器重启、存储延迟都会造成数据点缺失。缺失比例超过 20% 的指标应标记为不可用;低于 20% 时,根据缺失模式选择填充策略。
| 填充方法 | 适用条件 | 实现方式 | 对阈值影响 |
|---|---|---|---|
| 前向填充(ffill) | 短时连续缺失(≤3 点) | 用上一个有效值填充 | 引入滞后,降低波动性 |
| 线性插值 | 缺失段两端趋势稳定 | 按时间比例线性计算 | 平滑过渡,保留趋势 |
| 中位数填充 | 随机缺失、无趋势 | 用该指标历史中位数填充 | 压缩方差,阈值偏保守 |
| 季节性插值 | 具有日/周周期规律 | 用同期历史值加权填充 | 保留周期特征,最准确 |
注意:缺失值填充后必须记录填充标记(mask),在 A/B 测试分组时确保填充点均匀分布在 A/B 两组,避免因填充方式不同引入分组偏差。
4.3 数据归一化:消除量纲差异
告警阈值 A/B 测试中,不同指标(如 CPU 百分比、内存 GB、延迟 ms)量纲差异巨大。归一化使多指标可比较、可加权、可聚合。常用方法包括 Min-Max 归一化与 Z-Score 标准化。
| 方法 | 公式 | 输出范围 | 适用场景 |
|---|---|---|---|
| Min-Max 归一化 | X' = (X - X_min) / (X_max - X_min) | [0, 1] | 指标有明确上下界,如 CPU、内存 |
| Z-Score 标准化 | X' = (X - μ) / σ | 均值为 0,标准差为 1 | 指标分布近似正态,如延迟、错误率 |
| Robust 缩放 | X' = (X - median) / IQR | 无固定范围 | 含较多离群点,需稳健缩放 |
关键原则:归一化参数(Min/Max、μ/σ、median/IQR)必须基于 A/B 测试的 公共基线期 数据计算,然后统一应用到 A 组和 B 组。禁止分别计算各组参数,否则会破坏组间可比性。
4.4 预处理流水线代码示例
以下 Python 代码展示完整的预处理流水线,包含异常值剔除、缺失值填充与归一化,适用于 Pandas DataFrame 格式的告警时序数据。
import pandas as pd
import numpy as np
def preprocess_alarm_series(df, value_col='value', method='iqr', fill_method='linear', norm_method='minmax'):
"""
告警时序数据预处理流水线
参数:
df: 包含时间戳和值的 DataFrame
value_col: 指标值列名
method: 异常值剔除方法 ('iqr', 'zscore', 'mad')
fill_method: 缺失值填充方法 ('ffill', 'linear', 'median')
norm_method: 归一化方法 ('minmax', 'zscore', 'robust')
返回:
预处理后的 DataFrame 及归一化参数
"""
data = df.copy()
# 1. 异常值剔除
if method == 'iqr':
Q1 = data[value_col].quantile(0.25)
Q3 = data[value_col].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
data = data[(data[value_col] >= lower) & (data[value_col] <= upper)]
elif method == 'zscore':
mean = data[value_col].mean()
std = data[value_col].std()
data['z'] = (data[value_col] - mean) / std
data = data[data['z'].abs() <= 3]
data.drop(columns=['z'], inplace=True)
elif method == 'mad':
median = data[value_col].median()
mad = np.median(np.abs(data[value_col] - median))
data = data[np.abs(data[value_col] - median) <= 3 * mad]
# 2. 缺失值填充
data.set_index('timestamp', inplace=True)
if fill_method == 'ffill':
data[value_col] = data[value_col].fillna(method='ffill', limit=3)
elif fill_method == 'linear':
data[value_col] = data[value_col].interpolate(method='linear', limit=3)
elif fill_method == 'median':
med_val = data[value_col].median()
data[value_col] = data[value_col].fillna(med_val)
# 3. 数据归一化(基于基线期参数)
if norm_method == 'minmax':
v_min = data[value_col].min()
v_max = data[value_col].max()
data[value_col + '_norm'] = (data[value_col] - v_min) / (v_max - v_min)
params = {'min': v_min, 'max': v_max}
elif norm_method == 'zscore':
mu = data[value_col].mean()
sigma = data[value_col].std()
data[value_col + '_norm'] = (data[value_col] - mu) / sigma
params = {'mean': mu, 'std': sigma}
elif norm_method == 'robust':
med = data[value_col].median()
iqr_val = data[value_col].quantile(0.75) - data[value_col].quantile(0.25)
data[value_col + '_norm'] = (data[value_col] - med) / iqr_val
params = {'median': med, 'iqr': iqr_val}
data.reset_index(inplace=True)
return data, params
4.5 预处理质量检查清单
完成预处理后,建议逐项检查以下指标,确保数据质量满足 A/B 测试要求:
- 异常值剔除比例:应在 1%–5% 之间,超过 10% 需检查数据源或调整阈值
- 缺失值填充比例:填充后缺失率应为 0%,填充标记记录完整
- 归一化后分布:Min-Max 归一化后值域为 [0,1];Z-Score 标准化后均值接近 0,标准差接近 1
- 时序连续性:时间戳间隔均匀,无跳点或重复
- A/B 组一致性:两组数据经过同一套预处理参数,分布无显著差异(KS 检验 p > 0.05)
效率提示:将预处理流水线封装为可复用的 Python 类或 Airflow DAG 任务,每次 A/B 测试前自动运行并输出质量报告。避免手动处理带来的不一致风险。