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。
异常值剔除流程(IQR 方法) 原始时序数据 计算 Q1、Q3、IQR IQR = Q3 - Q1 判定上下边界 [Q1-1.5IQR, Q3+1.5IQR] 正常数据保留 异常数据剔除 说明:IQR 方法不假设数据分布,适合告警指标常见的偏态、多峰场景 剔除比例建议控制在 1%–5%,避免过度清洗丢失真实波动

4.2 缺失值填充:保持时序完整性

告警数据采集链路中,网络抖动、采集器重启、存储延迟都会造成数据点缺失。缺失比例超过 20% 的指标应标记为不可用;低于 20% 时,根据缺失模式选择填充策略。

填充方法 适用条件 实现方式 对阈值影响
前向填充(ffill) 短时连续缺失(≤3 点) 用上一个有效值填充 引入滞后,降低波动性
线性插值 缺失段两端趋势稳定 按时间比例线性计算 平滑过渡,保留趋势
中位数填充 随机缺失、无趋势 用该指标历史中位数填充 压缩方差,阈值偏保守
季节性插值 具有日/周周期规律 用同期历史值加权填充 保留周期特征,最准确
注意:缺失值填充后必须记录填充标记(mask),在 A/B 测试分组时确保填充点均匀分布在 A/B 两组,避免因填充方式不同引入分组偏差。
缺失值填充策略选择决策树 缺失比例 < 20%? 是 → 继续填充 否 → 标记不可用 判断缺失模式 短时连续缺失 → 前向填充 随机缺失 → 中位数填充 周期规律缺失 → 季节性插值

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 测试前自动运行并输出质量报告。避免手动处理带来的不一致风险。