数据预处理基础:缺失值处理、异常值检测与处理、数据标准化与归一化、数据离散化
数据预处理是特征筛选前的关键步骤,直接影响后续模型训练的质量。原始数据通常包含缺失值、异常值,且各特征量纲不一致,若不处理会导致模型偏差或收敛困难。本节系统梳理四大预处理环节的核心方法与避坑要点。
2.1 缺失值处理
缺失值处理需根据缺失机制和数据特性选择策略,常见方法如下:
| 方法 | 适用场景 | 注意事项 |
|---|---|---|
| 删除(行/列) | 缺失比例 < 5%,且随机缺失 | 避免删除过多导致样本量不足 |
| 均值/中位数填充 | 数值型特征,分布较对称 | 会降低方差,影响模型区分度 |
| 众数填充 | 分类型特征 | 可能引入偏差,尤其类别不均衡时 |
| 前向/后向填充 | 时间序列数据 | 需确保时间顺序正确 |
| 模型预测填充 | 缺失比例较高,特征间关联强 | 计算成本高,需防止过拟合 |
| KNN 填充 | 数值型特征,样本量适中 | K 值选择敏感,距离计算受量纲影响 |
避坑提示: 缺失值处理前务必分析缺失机制(MCAR/MAR/MNAR)。若为 MNAR(非随机缺失),直接填充会引入系统性偏差,建议结合业务逻辑单独处理。
# 使用 pandas 进行缺失值处理示例
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, 70000, 65000, 72000],
'gender': ['M', 'F', 'F', np.nan, 'M', 'F']
})
# 查看缺失比例
print(df.isnull().mean())
# 数值型:中位数填充
df['age'] = df['age'].fillna(df['age'].median())
df['income'] = df['income'].fillna(df['income'].median())
# 分类型:众数填充
df['gender'] = df['gender'].fillna(df['gender'].mode()[0])
2.2 异常值检测与处理
异常值会扭曲统计量、影响模型拟合。常用检测方法包括统计法、距离法和模型法。
核心原则: 异常值 ≠ 错误值。需结合业务判断是否剔除、修正或保留。例如,信用卡欺诈中的大额交易本身就是目标信号。
常用检测方法对比
| 方法 | 原理 | 适用场景 | 局限性 |
|---|---|---|---|
| Z-Score | |Z| > 3 视为异常 | 近似正态分布 | 对非正态分布不敏感 |
| IQR 四分位距 | Q1 - 1.5*IQR 或 Q3 + 1.5*IQR 之外 | 偏态分布,稳健 | 阈值固定,可能漏检 |
| DBSCAN 聚类 | 不在任何核心簇内的点 | 高维数据,簇形状复杂 | 参数 eps 和 min_samples 敏感 |
| 孤立森林 | 基于树模型隔离异常点 | 高维、大规模数据 | 对局部异常不敏感 |
# IQR 方法检测异常值
def detect_outliers_iqr(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return outliers
# 示例
outliers_df = detect_outliers_iqr(df, 'income')
print(f"检测到 {len(outliers_df)} 个异常值")
常见误区: 不要对异常值直接做截尾处理(Winsorization)而不记录处理比例。若异常值占比超过 5%,说明数据分布本身存在长尾,建议考虑对数变换或分箱处理。
2.3 数据标准化与归一化
标准化和归一化解决特征量纲不一致问题,避免大数值特征主导模型训练。两者适用场景不同,需根据模型类型选择。
| 方法 | 公式 | 输出范围 | 适用模型 |
|---|---|---|---|
| Z-Score 标准化 | z = (x - μ) / σ | 均值 0,标准差 1 | SVM、线性回归、PCA、神经网络 |
| Min-Max 归一化 | x' = (x - min) / (max - min) | [0, 1] | KNN、神经网络(输入层) |
| Robust 缩放 | x' = (x - median) / IQR | 无固定范围 | 含异常值的数据 |
| MaxAbs 缩放 | x' = x / |max| | [-1, 1] | 稀疏数据 |
避坑提示: 标准化/归一化的参数(均值、标准差、min、max)必须从训练集计算,再应用到验证集和测试集。切勿对整个数据集统一计算,否则会造成数据泄露。
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# 训练集拟合,转换训练集和测试集
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # 使用训练集的参数
# Min-Max 归一化
minmax_scaler = MinMaxScaler()
X_train_norm = minmax_scaler.fit_transform(X_train)
X_test_norm = minmax_scaler.transform(X_test)
2.4 数据离散化
离散化将连续特征转换为分类特征,可增强模型鲁棒性、降低过拟合风险,尤其适用于线性模型和决策树。
离散化方法对比
| 方法 | 策略 | 优点 | 缺点 |
|---|---|---|---|
| 等宽分箱 | 按值域等距划分 | 实现简单,直观 | 数据分布不均时箱内样本差异大 |
| 等频分箱 | 按分位数划分,每箱样本数相同 | 每箱样本均衡 | 可能将相近值分到不同箱 |
| 聚类分箱 | 使用 KMeans 聚类后分配箱标签 | 适应数据自然分布 | K 值选择依赖经验 |
| 决策树分箱 | 用目标变量监督划分切分点 | 与目标变量关联强 | 易过拟合,需控制树深度 |
# 等频分箱示例
import pandas as pd
data = pd.Series([12, 15, 23, 24, 30, 35, 40, 45, 50, 60])
# 等频分箱为 3 箱
bins = pd.qcut(data, q=3, labels=['低', '中', '高'])
print(bins)
离散化避坑: 分箱数量不宜过多(通常 3~10 箱),否则失去离散化意义。等宽分箱对异常值敏感,建议先处理异常值再分箱。决策树分箱需在训练集上拟合切分点,避免在测试集上重新分箱。
2.5 预处理流程总览
下图展示数据预处理的标准流程及各环节的衔接关系:
流程要点: 缺失值处理和异常值检测通常在前两步完成,标准化/归一化与离散化是互斥的预处理路径——树模型(如随机森林、XGBoost)对量纲不敏感,通常不需要标准化;而线性模型、SVM、神经网络则必须进行标准化。离散化更适合线性模型或需要可解释性的场景。