特征工程与数据预处理
「垃圾进,垃圾出」——原始数据质量和特征设计的好坏,对最终效果的影响往往大于算法选择。这篇系统梳理数据预处理和特征工程的核心步骤,配实用代码。
数据预处理
缺失值处理
先搞清楚缺失的原因,再决定策略:
| 场景 | 推荐策略 |
|---|---|
| 随机缺失、缺失量少 | 均值/中位数/众数填充 |
| 有规律缺失(如高端用户不填收入) | 单独作为一个「未知」类别编码 |
| 大量缺失(>50%) | 考虑直接删列 |
| 时序数据 | 前向/后向填充或插值 |
import pandas as pd
df = pd.read_csv("data.csv")
# 数值列:用中位数填充(比均值对异常值更稳健)
df["age"].fillna(df["age"].median(), inplace=True)
# 类别列:填充为出现频率最高的值
df["city"].fillna(df["city"].mode()[0], inplace=True)
# 查看各列缺失率
print(df.isnull().mean().sort_values(ascending=False))异常值处理
异常值不一定是「噪声」——业务异常(如欺诈)本身就是你要学习的信号,删掉反而有害。
- 可视化先行:箱形图看分布,散点图找离群点。
- IQR 法:1.5 倍四分位距之外的点标记为异常。
- 处理方式:截断(Clipping)通常比直接删除更安全——把极端值压到合理上下界。
# 对 salary 列做上下界截断(保留 1%~99% 分位数范围内)
lower = df["salary"].quantile(0.01)
upper = df["salary"].quantile(0.99)
df["salary"] = df["salary"].clip(lower, upper)特征编码
机器学习模型只认数字,类别特征必须先编码。
独热编码(One-Hot Encoding)
适合无序类别,如城市、颜色。缺点:高基数列会炸维度。
df = pd.get_dummies(df, columns=["city"], drop_first=True)标签编码(Label Encoding)
适合有序类别,如评级(低/中/高)、学历。
from sklearn.preprocessing import OrdinalEncoder
enc = OrdinalEncoder(categories=[["初中", "高中", "本科", "研究生"]])
df[["edu"]] = enc.fit_transform(df[["edu"]])目标编码(Target Encoding)
用目标变量的均值替换类别值,适合高基数类别(如商品 ID)。需结合交叉验证做,否则容易数据泄露。
数值特征缩放
不缩放,基于距离的算法(KNN、SVM)和梯度下降的模型(神经网络、线性回归)会被量级大的特征主导。
| 方法 | 公式 | 适用场景 |
|---|---|---|
| 标准化 Z-Score | (x - mean) / std | 特征近似正态分布;大多数情况首选 |
| 归一化 Min-Max | (x - min) / (max - min) | 需要把值压到 [0, 1];对异常值敏感 |
| 鲁棒缩放 Robust | (x - median) / IQR | 有异常值时更稳健 |
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 注意:fit 只在训练集上做,transform 在训练/测试集上分别做
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # 不能 fit_transform!测试集必须用训练集的统计量来变换(即只
transform,不 fit_transform),否则发生数据泄露,评估失真。特征选择
特征不是越多越好。冗余特征增加噪声、拖慢训练、降低可解释性。
- 方差过滤:方差接近 0 的特征几乎不提供信息,直接删。
- 相关性过滤:两个特征相关系数 > 0.95,留其中一个。
- 基于模型的重要性:训练一个随机森林,按
feature_importances_排序,砍掉末尾。 - 递归特征消除(RFE):sklearn 提供
RFE类,自动剔除最不重要的特征。
from sklearn.ensemble import RandomForestClassifier
import numpy as np
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train_scaled, y_train)
# 按重要性排序
importances = pd.Series(model.feature_importances_, index=feature_names)
print(importances.sort_values(ascending=False).head(10))一句话小结
预处理的顺序:处理缺失值 → 处理异常值 → 编码类别特征 → 缩放数值特征 → 特征选择。每步的 fit 只看训练集,处理逻辑一致地应用到测试集。
最后更新于