4. 数据清洗基础:缺失值处理策略与异常值检测

数据清洗是特征工程中最基础也最关键的环节。原始数据几乎总是包含缺失值、异常值或噪声,这些瑕疵会严重干扰自动机器学习(AutoML)对照实验的公平性与模型泛化能力。本章聚焦于缺失值处理与异常值检测两大核心任务,提供可复用的策略与代码模板。

4.1 缺失值处理策略

缺失值产生的原因包括传感器故障、用户未填写、数据合并错位等。处理策略可分为三类:删除、统计填充与模型预测填充。选择哪种策略取决于缺失比例、数据分布与业务含义。

策略 适用场景 优点 缺点
删除(行/列) 缺失比例 < 5%,且缺失随机 简单、无偏差引入 丢失信息,样本量减少
均值/中位数填充 数值型特征,缺失比例 5%~20% 计算快,保持样本量 降低方差,可能扭曲分布
众数填充 分类型特征 保留类别分布 可能引入偏差
模型预测填充 缺失比例 > 20%,特征间相关性强 利用特征关系,填充更准确 计算成本高,可能过拟合
对照实验提示: 在 AutoML 对照实验中,建议将缺失值处理作为预处理管道的一部分,而非在实验前单独处理。这样可以确保不同实验组使用相同的填充逻辑,避免因填充方式不同导致模型性能差异无法归因。

4.1.1 删除策略

当缺失值比例极低(通常 < 5%)且缺失机制为完全随机缺失(MCAR)时,直接删除缺失样本是最安全的选择。删除列(特征)适用于该特征缺失比例超过 70% 且无重要业务含义的情况。

import pandas as pd
import numpy as np

# 示例数据
df = pd.DataFrame({
    'feature_a': [1, 2, np.nan, 4, 5],
    'feature_b': [10, np.nan, 30, 40, 50],
    'target': [0, 1, 0, 1, 0]
})

# 删除包含缺失值的行
df_drop_row = df.dropna()

# 删除缺失比例超过 70% 的列
threshold = 0.7
df_drop_col = df.loc[:, df.isnull().mean() < threshold]

print(f"原始行数: {len(df)}, 删除后行数: {len(df_drop_row)}")

4.1.2 均值/中位数填充

均值填充适用于近似正态分布的数据;中位数填充对异常值更鲁棒,适用于偏态分布。填充后建议添加“缺失指示列”以保留缺失模式信息。

from sklearn.impute import SimpleImputer

# 均值填充
imputer_mean = SimpleImputer(strategy='mean')
df['feature_a_filled'] = imputer_mean.fit_transform(df[['feature_a']])

# 中位数填充
imputer_median = SimpleImputer(strategy='median')
df['feature_b_filled'] = imputer_median.fit_transform(df[['feature_b']])

# 添加缺失指示列
df['feature_a_missing'] = df['feature_a'].isnull().astype(int)
df['feature_b_missing'] = df['feature_b'].isnull().astype(int)

print(df)

4.1.3 模型预测填充

当缺失值比例较高且特征间存在较强相关性时,使用其他特征作为输入,以缺失特征作为目标进行预测填充。常用模型包括 KNN、线性回归、随机森林等。这种方法能更好地保留特征间的联合分布。

from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import KNNImputer

# 方法一:KNN 填充(基于样本相似度)
knn_imputer = KNNImputer(n_neighbors=3)
df_knn_filled = pd.DataFrame(
    knn_imputer.fit_transform(df[['feature_a', 'feature_b']]),
    columns=['feature_a', 'feature_b']
)

# 方法二:迭代模型填充(以 feature_a 为例)
# 将数据分为有缺失和无缺失两部分
df_train = df[df['feature_a'].notna()]
df_missing = df[df['feature_a'].isna()]

X_train = df_train[['feature_b']]
y_train = df_train['feature_a']
X_missing = df_missing[['feature_b']]

model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
predicted_values = model.predict(X_missing)

df.loc[df['feature_a'].isna(), 'feature_a'] = predicted_values
注意: 模型预测填充在对照实验中容易引入数据泄露。务必在训练集上拟合填充模型,再转换验证集和测试集。使用 sklearn.pipeline.Pipeline 可以自动保证这一流程。
缺失值处理策略决策流程 缺失值处理策略决策流程 检测缺失值 缺失比例 是否 < 5%? 是 删除行 否 5%~20% 且分布偏态? 是 中位数填充 正态 均值填充 缺失 > 20% 特征相关性强? 是 模型预测填充 否 删除列 提示:所有填充策略均建议配合缺失指示列使用,以保留缺失模式信息。

4.2 异常值检测与处理

异常值(Outliers)是指显著偏离数据集中其他观测值的样本。异常值可能由测量误差、数据录入错误或真实的稀有事件产生。在 AutoML 对照实验中,异常值会扭曲模型训练,尤其对线性模型、KNN 和神经网络影响显著。

4.2.1 异常值检测方法

方法 原理 适用场景 参数
Z-Score 基于均值和标准差,假设正态分布 单变量、近似正态分布 阈值通常 2~4
IQR(四分位距) 基于 Q1 和 Q3,不依赖分布假设 单变量、偏态分布 系数通常 1.5~3
Isolation Forest 随机划分,异常点更容易被孤立 多变量、高维数据 contamination 参数
DBSCAN 基于密度聚类,低密度点为异常 多变量、非球形簇 eps, min_samples

4.2.2 异常值处理方法

  1. 删除异常样本:当异常值明显由错误产生且数量较少时。
  2. 截尾(Capping/Winsorizing):将异常值替换为阈值(如 1% 和 99% 分位数)。
  3. 转换:对特征进行对数、Box-Cox 或 Yeo-Johnson 变换,压缩极端值影响。
  4. 分箱:将连续特征离散化,消除异常值对距离度量的影响。
import numpy as np
import pandas as pd
from scipy import stats

# 示例数据
np.random.seed(42)
data = np.random.normal(50, 10, 100)
data = np.append(data, [120, 130, 5, 3])  # 加入异常值
df = pd.DataFrame({'feature': data})

# 方法一:Z-Score 检测
z_scores = np.abs(stats.zscore(df['feature']))
outliers_z = df[z_scores > 3]
print(f"Z-Score 检测到 {len(outliers_z)} 个异常值")

# 方法二:IQR 检测
Q1 = df['feature'].quantile(0.25)
Q3 = df['feature'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = df[(df['feature'] < lower_bound) | (df['feature'] > upper_bound)]
print(f"IQR 检测到 {len(outliers_iqr)} 个异常值")

# 截尾处理(Winsorizing)
from scipy.stats.mstats import winsorize
df['feature_winsorized'] = winsorize(df['feature'], limits=[0.01, 0.01])

# 对数变换
df['feature_log'] = np.log1p(df['feature'] - df['feature'].min() + 1)
对照实验关键点: 异常值检测的阈值(如 Z-Score 的 3 或 IQR 的 1.5)应在实验设计阶段固定,并在所有实验组中保持一致。建议在验证集上评估异常值处理对模型性能的影响,避免过度清洗导致信息丢失。
异常值检测与处理流程 异常值检测与处理流程 原始数据 选择检测方法 Z-Score / IQR Isolation Forest DBSCAN 处理:删除 / 截尾 / 转换 / 分箱 提示:多变量异常检测(Isolation Forest、DBSCAN)能发现单变量方法无法识别的组合异常。

4.3 数据清洗在 AutoML 对照实验中的实践建议

  • 管道化:将缺失值填充和异常值处理封装在 sklearn.pipeline.Pipeline 中,确保训练/验证/测试集使用完全相同的转换逻辑。
  • 记录转换参数:保存填充值(均值、中位数、模型参数)和异常值阈值,便于实验复现。
  • 对比实验设计:设置“无清洗”、“仅缺失值处理”、“仅异常值处理”、“完整清洗”四个对照组,量化每个步骤对模型性能的贡献。
  • 避免数据泄露:填充模型和异常值检测的拟合过程必须仅在训练集上执行,再应用到验证集和测试集。
效率技巧: 使用 sklearn.compose.ColumnTransformer 可以对不同特征列应用不同的填充策略(如数值列用中位数、分类列用众数),实现精细化清洗。