3. 数据清洗实战:缺失值处理与异常值检测
数据清洗是特征工程中最基础也最关键的环节。原始数据通常包含缺失值、异常值、重复值和格式不一致等问题,若不加以处理,后续的模型训练和MRM验证将产生严重偏差。本章聚焦于缺失值处理和异常值检测与处理两大核心任务,提供从理论到代码的完整实战方案。
3.1 缺失值处理
缺失值是指数据集中某个或某些特征的值未被记录或丢失。处理缺失值前,需先分析缺失机制:完全随机缺失(MCAR)、随机缺失(MAR)或非随机缺失(MNAR)。不同机制影响填充方法的选择。
3.1.1 缺失值可视化与统计
在填充前,应通过统计和可视化手段了解缺失情况:
- 缺失比例:计算每列缺失值占比,决定是否删除或填充。
- 缺失模式:使用热力图或矩阵图观察缺失是否集中在某些样本或特征上。
- 缺失相关性:检查缺失值之间是否存在关联。
经验法则:缺失比例 < 5% 可直接删除或简单填充;5%-20% 需谨慎选择填充方法;> 20% 应考虑特征是否保留或使用模型预测填充。
3.1.2 常用填充方法对比
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 均值填充 | 数值型特征,数据近似正态分布 | 简单快速,保持均值不变 | 降低方差,扭曲分布形态 |
| 中位数填充 | 数值型特征,存在偏态或异常值 | 对异常值鲁棒,保持中位数 | 忽略特征间关系 |
| 众数填充 | 分类型特征 | 保持类别分布 | 可能引入偏差 |
| 模型预测填充 | 缺失值较多,特征间关系复杂 | 利用特征间关联,填充更准确 | 计算成本高,可能过拟合 |
3.1.3 均值/中位数/众数填充实战
以下代码展示使用Pandas进行简单填充:
import pandas as pd
import numpy as np
# 示例数据
df = pd.DataFrame({
'age': [25, 30, np.nan, 35, 40, np.nan, 28],
'salary': [50000, 60000, 55000, np.nan, 70000, 65000, np.nan],
'gender': ['M', 'F', 'M', np.nan, 'F', 'M', 'F']
})
# 均值填充(数值型)
df['age_fill_mean'] = df['age'].fillna(df['age'].mean())
# 中位数填充(数值型,偏态数据更稳健)
df['salary_fill_median'] = df['salary'].fillna(df['salary'].median())
# 众数填充(分类型)
df['gender_fill_mode'] = df['gender'].fillna(df['gender'].mode()[0])
print(df[['age', 'age_fill_mean', 'salary', 'salary_fill_median', 'gender', 'gender_fill_mode']])
3.1.4 模型预测填充实战
模型预测填充将缺失值视为目标变量,利用其他完整特征建立预测模型。常用算法包括KNN、随机森林、线性回归等。
from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import KNNImputer
# 方法一:KNN填充(基于样本相似度)
imputer_knn = KNNImputer(n_neighbors=3)
df_knn_imputed = pd.DataFrame(
imputer_knn.fit_transform(df[['age', 'salary']]),
columns=['age', 'salary']
)
# 方法二:迭代随机森林填充(IterativeImputer)
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imputer_rf = IterativeImputer(
estimator=RandomForestRegressor(n_estimators=100, random_state=42),
max_iter=10,
random_state=42
)
df_rf_imputed = pd.DataFrame(
imputer_rf.fit_transform(df[['age', 'salary']]),
columns=['age', 'salary']
)
print("KNN填充结果:\n", df_knn_imputed)
print("\n随机森林迭代填充结果:\n", df_rf_imputed)
实战建议:在MRM验证场景中,模型预测填充通常优于简单填充,但需注意避免数据泄露——填充模型应在训练集上拟合,再转换验证集和测试集。
3.2 异常值检测与处理
异常值(Outlier)是指显著偏离其他观测值的样本点。异常值可能由测量误差、数据录入错误或真实稀有事件引起。在MRM验证中,异常值会严重影响模型稳定性和泛化能力。
3.2.1 异常值检测方法对比
| 方法 | 原理 | 适用分布 | 阈值 | 优缺点 |
|---|---|---|---|---|
| Z-score | 基于标准差,衡量数据点与均值的偏离程度 | 近似正态分布 | |Z| > 3 通常视为异常 | 简单直观,但对偏态数据敏感 |
| IQR(四分位距) | 基于分位数,不受极端值影响 | 任意分布(偏态稳健) | Q1 - 1.5×IQR 或 Q3 + 1.5×IQR | 对偏态数据鲁棒,不依赖正态假设 |
| 孤立森林 | 基于树模型,异常点更容易被孤立 | 高维数据 | 异常分数阈值 | 适合高维和大规模数据 |
| DBSCAN | 基于密度聚类,低密度区域为异常 | 非球形分布 | 邻域半径和最小点数 | 可发现任意形状异常簇 |
3.2.2 Z-score 方法实战
from scipy import stats
import numpy as np
# 生成示例数据(含异常值)
np.random.seed(42)
data = np.random.normal(loc=50, scale=10, size=100)
data = np.append(data, [120, 5, 130]) # 人为添加异常值
# 计算Z-score
z_scores = np.abs(stats.zscore(data))
threshold = 3
outliers_z = np.where(z_scores > threshold)[0]
print(f"Z-score方法检测到 {len(outliers_z)} 个异常值")
print(f"异常值索引: {outliers_z}")
print(f"异常值数据: {data[outliers_z]}")
3.2.3 IQR 方法实战
# IQR方法检测异常值
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = np.where((data < lower_bound) | (data > upper_bound))[0]
print(f"IQR方法检测到 {len(outliers_iqr)} 个异常值")
print(f"异常值索引: {outliers_iqr}")
print(f"异常值数据: {data[outliers_iqr]}")
print(f"下界: {lower_bound:.2f}, 上界: {upper_bound:.2f}")
注意:Z-score 对极端值敏感,若数据本身包含多个异常值,均值和标准差会被“拉偏”,导致部分异常值无法被检测(掩蔽效应)。此时 IQR 方法更为稳健。
3.2.4 异常值处理策略
- 删除:当异常值比例很小(< 1%)且确认由错误引起时,直接删除。
- 截尾(Winsorization):将异常值替换为边界值(如上下1%分位数)。
- 视为缺失值:将异常值标记为缺失,再用缺失值处理方法填充。
- 单独建模:当异常值代表重要稀有事件时,可单独建模或使用对异常值鲁棒的算法。
# 截尾处理示例
def winsorize_series(series, limits=(0.01, 0.01)):
"""对序列进行截尾处理"""
lower = series.quantile(limits[0])
upper = series.quantile(1 - limits[1])
return series.clip(lower, upper)
# 应用截尾
data_clean = winsorize_series(pd.Series(data), limits=(0.05, 0.05))
print("截尾后数据统计:")
print(f"均值: {data_clean.mean():.2f}, 标准差: {data_clean.std():.2f}")
3.3 缺失值与异常值协同处理流程
在实际项目中,缺失值和异常值往往同时存在,且相互影响。推荐的处理顺序为:
- 初步探索:统计缺失比例、异常值比例,可视化分布。
- 异常值检测:先使用IQR或Z-score标记异常值(不立即处理)。
- 缺失值填充:使用模型预测填充或简单填充,注意异常值可能影响填充模型。
- 异常值处理:对填充后的数据再次检测异常值,进行截尾或替换。
- 验证:对比处理前后的分布变化,确保不引入系统性偏差。
MRM验证协同要点:在MRM(多模态回归模型)验证中,缺失值和异常值处理应在训练集上定义规则(如填充值、截尾边界),并一致地应用到验证集和测试集,避免数据泄露。
3.4 本章小结
- 缺失值处理需根据缺失比例、特征类型和数据分布选择合适方法,模型预测填充在复杂场景下效果更优。
- 异常值检测中,IQR方法对偏态数据更稳健,Z-score适用于正态分布数据。
- 异常值处理策略包括删除、截尾、视为缺失值和单独建模,推荐优先使用截尾或视为缺失值以保留样本量。
- 缺失值与异常值应协同处理,遵循“先检测异常值→填充缺失值→再处理异常值”的流程。
- 所有处理规则必须在训练集上拟合,并一致应用到验证集和测试集,确保MRM验证的公平性。