2. 数据预处理基础:缺失值与异常值处理
数据预处理是特征工程的基石。原始数据几乎总是包含缺失值、异常值或不一致记录,若不处理,后续建模会产生偏差甚至错误。本章聚焦于缺失值处理与异常值检测两大核心任务,提供从基础到进阶的完整方案。
2.1 缺失值处理
缺失值产生的原因多样:设备故障、用户未填写、数据合并错位等。处理策略分为三类:删除、填充与模型预测。
2.1.1 删除法
删除法是最简单的策略,但会损失样本量,适用于缺失比例极低或随机缺失的场景。
| 方法 | 操作 | 适用场景 | 风险 |
|---|---|---|---|
| 行删除 | 删除含缺失值的整行 | 缺失率 < 5%,且随机缺失 | 样本量减少,可能引入偏差 |
| 列删除 | 删除缺失率过高的整列 | 缺失率 > 70%,且信息量低 | 丢失潜在有用特征 |
| 成对删除 | 只在计算具体统计量时排除缺失 | 相关性分析、协方差计算 | 不同统计量基于不同样本,结果不一致 |
注意: 删除法在非随机缺失(MNAR)场景下会严重扭曲数据分布,务必先分析缺失机制。
2.1.2 统计量填充
用集中趋势统计量替换缺失值,简单高效,但会降低方差,削弱特征区分度。
- 均值填充: 适用于近似正态分布的数值特征。
- 中位数填充: 适用于偏态分布或存在异常值的数值特征,鲁棒性强。
- 众数填充: 适用于分类特征或离散数值特征。
- 常数填充: 用业务含义明确的特殊值(如 -1、0、'Unknown')填充,保留缺失信息。
import pandas as pd
import numpy as np
# 示例数据
df = pd.DataFrame({'age': [25, np.nan, 30, 35, np.nan, 28],
'income': [50000, 60000, np.nan, 55000, 65000, 58000],
'gender': ['M', 'F', 'F', np.nan, 'M', 'F']})
# 均值填充
df['age_filled_mean'] = df['age'].fillna(df['age'].mean())
# 中位数填充
df['income_filled_median'] = df['income'].fillna(df['income'].median())
# 众数填充
df['gender_filled_mode'] = df['gender'].fillna(df['gender'].mode()[0])
print(df[['age', 'age_filled_mean', 'income', 'income_filled_median', 'gender', 'gender_filled_mode']])
2.1.3 模型预测填充
将缺失特征作为目标变量,利用其他完整特征构建预测模型进行填充。精度高,但计算成本大,且可能引入模型偏差。
| 方法 | 模型类型 | 优点 | 缺点 |
|---|---|---|---|
| KNN 填充 | 无监督 | 利用样本相似性,无需假设分布 | 对 K 值敏感,高维数据效果差 |
| 回归填充 | 有监督 | 利用特征间线性关系 | 假设线性关系,可能过拟合 |
| 随机森林填充 | 有监督 | 处理非线性关系,自带特征重要性 | 计算量大,需调参 |
| MICE(链式方程) | 迭代多模型 | 处理多变量缺失,理论成熟 | 实现复杂,迭代收敛慢 |
实践建议: 对于中小规模数据(< 10 万行),优先尝试 KNN 填充(K=5~10)或随机森林填充;对于大规模数据,使用中位数/众数填充更高效。
2.2 异常值检测与处理
异常值(Outlier)是显著偏离其他观测值的样本点。异常值可能由测量误差、数据录入错误或真实稀有事件引起,需根据业务场景决定保留或修正。
2.2.1 3σ 原则(拉依达准则)
基于正态分布假设:数据落在均值 ±3 倍标准差之外的概率仅为 0.27%,视为异常。
- 适用条件: 数据近似服从正态分布。
- 计算公式: 异常值区间为 (μ - 3σ, μ + 3σ) 之外。
- 局限性: 对偏态分布和多重异常值敏感,标准差本身会被异常值拉大。
import numpy as np
def detect_outliers_3sigma(data):
mean = np.mean(data)
std = np.std(data)
lower_bound = mean - 3 * std
upper_bound = mean + 3 * std
outliers = [x for x in data if x < lower_bound or x > upper_bound]
return outliers, lower_bound, upper_bound
# 示例
data = [10, 12, 11, 13, 100, 9, 11, 12, 10, 11]
outliers, lb, ub = detect_outliers_3sigma(data)
print(f"下界: {lb:.2f}, 上界: {ub:.2f}")
print(f"检测到异常值: {outliers}")
2.2.2 IQR(四分位距)法
基于分位数,不依赖分布假设,鲁棒性强,是实际项目中最常用的方法。
- 定义: IQR = Q3 - Q1(第三四分位数与第一四分位数之差)。
- 异常判定: 小于 Q1 - 1.5×IQR 或大于 Q3 + 1.5×IQR 的点。
- 极端异常: 使用 3×IQR 作为倍数,判定为极端异常值。
| 倍数 | 区间 | 异常程度 | 常见处理 |
|---|---|---|---|
| 1.5 × IQR | [Q1 - 1.5IQR, Q3 + 1.5IQR] | 温和异常 | 截尾、替换或单独标记 |
| 3.0 × IQR | [Q1 - 3.0IQR, Q3 + 3.0IQR] | 极端异常 | 优先删除或深入调查 |
2.2.3 箱线图可视化
箱线图(Box Plot)是 IQR 法的图形化呈现,可直观展示数据分布、偏态和异常点。
箱线图关键元素:
- 箱体: 上边界 Q3,下边界 Q1,箱内线为中位数。
- 须(Whisker): 延伸至 1.5×IQR 范围内的最远非异常点。
- 异常点: 须之外的点,通常用圆点或星号标记。
2.2.4 异常值处理策略
检测到异常值后,需根据业务含义选择处理方式,不可机械删除。
| 策略 | 操作 | 适用场景 |
|---|---|---|
| 删除 | 直接移除异常样本 | 确认是录入错误或测量故障 |
| 截尾(Winsorize) | 将异常值替换为边界值(如 Q1 或 Q3) | 保留样本量,降低极端值影响 |
| 视为缺失 | 标记为 NaN,再用填充方法处理 | 不确定异常原因,保留处理弹性 |
| 单独编码 | 创建二值特征标记是否为异常 | 异常本身可能携带业务信息(如欺诈) |
| 变换 | 对数变换、Box-Cox 变换压缩尺度 | 偏态分布,异常值来自自然长尾 |
实践建议: 在金融风控、医疗诊断等场景,异常值可能代表欺诈或疾病,应单独编码保留;在传感器数据处理中,异常值多为噪声,优先截尾或删除。
2.3 本章小结
- 缺失值处理: 从删除到统计量填充再到模型预测,精度递增但成本也递增;务必先分析缺失机制。
- 异常值检测: 3σ 原则适用于正态分布,IQR 法更通用;箱线图是直观的辅助工具。
- 处理策略: 删除、截尾、标记、变换各有适用场景,需结合业务判断。
- 工程要点: 所有预处理步骤应在训练集上拟合参数(均值、分位数等),再应用到测试集,避免数据泄露。