数据预处理与特征工程:数据清洗、归一化与标准化、特征选择、PCA降维
说实话,做决策算法这些年,我踩过最大的坑往往不是模型选错了,而是数据没处理好。你想想看,垃圾数据喂进去,再牛的算法也白搭。这一章咱们就聊聊数据预处理和特征工程,我把这些年积累的实战经验都抖出来。
数据清洗——别让脏数据毁了你的模型
我记得刚入行那会儿,接手一个信贷风控项目。数据量挺大,几十万条样本。我兴冲冲地跑了个逻辑回归,结果AUC只有0.52,跟抛硬币差不多。排查了两天,发现是某个字段有大量空值,程序默认填了-1,直接把模型带偏了。嗯,从那以后,数据清洗成了我第一个检查项。
缺失值处理
缺失值怎么搞?我一般分三步走:
- 先看缺失比例——超过70%的字段,我通常直接扔掉。信息量太少,补了也是噪声。
- 再看缺失模式——是随机缺失还是有规律?比如用户收入字段,高收入人群往往不愿意填,这就不是随机缺失。
- 最后选填充策略——数值型用中位数或均值,分类型用众数。我个人习惯用中位数,因为它对异常值不敏感。
核心原则:缺失值处理没有银弹。我在项目中试过用模型预测缺失值,效果反而不好,因为预测本身就有误差,误差会层层传递。简单方法往往更稳健。
异常值检测
异常值这东西,说白了就是数据里的「刺头」。我常用的方法有两种:
- 3σ原则——数据服从正态分布时,超过均值±3倍标准差的值视为异常。简单粗暴,但好用。
- 箱线图法——用四分位数来界定。Q1-1.5IQR和Q3+1.5IQR之外的都是异常。这个方法不要求数据分布,适用范围更广。
注意:异常值不一定要删除。我曾经处理过一个电商数据,发现「购买金额」字段有个100万的记录,以为是异常,结果一查是公司大客户采购。所以,先搞清楚异常值是怎么来的,再决定怎么处理。
归一化与标准化——让特征站在同一起跑线
为什么需要归一化?你想想看,一个特征取值范围是0-1,另一个是0-10000,模型会天然认为后者更重要。这显然不合理。
Min-Max归一化
公式很简单:(x - min) / (max - min)。把数据压缩到[0,1]区间。我一般在神经网络里用这个,因为激活函数对输入范围有要求。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_normalized = scaler.fit_transform(X)
Z-score标准化
公式:(x - mean) / std。处理后数据均值为0,标准差为1。这个更适合有异常值的情况,因为它不依赖最大值最小值。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_standardized = scaler.fit_transform(X)
我的经验:如果你用树模型(随机森林、XGBoost),归一化其实没啥用。树模型是基于分裂点的,不受特征尺度影响。但如果你用SVM、KNN、神经网络,归一化是必须的。我有个项目,KNN没做归一化,准确率只有65%,做完直接飙到82%。
特征选择——去芜存菁的艺术
特征不是越多越好。我见过有人搞了200个特征,模型训练要跑一整天,效果还不如20个关键特征。特征选择就是帮你找到那20个「关键先生」。
过滤式(Filter)
先算特征和目标的相关性,再选特征。速度快,适合高维数据。
| 方法 | 适用场景 | 我的评价 |
|---|---|---|
| 方差选择法 | 特征值变化很小的场景 | 简单但粗糙,我一般用它做第一道筛选 |
| 卡方检验 | 分类问题,特征和标签都是离散的 | 文本分类里特别好用 |
| 互信息法 | 能捕捉非线性关系 | 我最喜欢的方法,就是计算量稍大 |
包裹式(Wrapper)
把特征选择当成搜索问题。每次选一个特征子集,训练模型看效果。效果最好,但计算量最大。
- 递归特征消除(RFE)——每次去掉最不重要的特征,递归直到达到目标特征数。我有个项目用RFE从50个特征选到15个,AUC反而提升了0.03。
- 前向/后向搜索——前向是逐步加特征,后向是逐步减特征。数据量小的时候可以试试。
避坑指南:我曾经在100维的数据上跑包裹式选择,跑了整整两天没出结果。后来改用过滤式+嵌入式组合,半小时搞定。高维数据慎用包裹式。
嵌入式(Embedded)
特征选择和模型训练同时进行。最典型的就是L1正则化(Lasso)。
from sklearn.linear_model import Lasso
lasso = Lasso(alpha=0.01)
lasso.fit(X_train, y_train)
# 系数为0的特征就是被剔除的
selected_features = X.columns[lasso.coef_ != 0]
树模型的特征重要性也是嵌入式方法。我常用XGBoost的feature_importances_来快速筛选特征,效果不错。
PCA降维——化繁为简的利器
PCA说白了就是找数据变化最大的方向,把高维数据投影到低维空间。我刚开始学的时候觉得这玩意儿很玄乎,后来用多了才发现,它就是线性代数里的特征值分解。
核心思想
PCA做三件事:
- 去中心化——每个特征减去均值
- 找主成分——计算协方差矩阵的特征向量,特征值越大,这个方向越重要
- 降维——选前k个特征向量,把数据投影过去
实战代码
from sklearn.decomposition import PCA
import numpy as np
# 假设X是1000个样本,50个特征
pca = PCA(n_components=0.95) # 保留95%的方差
X_pca = pca.fit_transform(X)
print(f"原始维度: {X.shape[1]}")
print(f"降维后维度: {X_pca.shape[1]}")
print(f"各主成分方差占比: {pca.explained_variance_ratio_}")
我的习惯:我不会硬性指定降维到多少维,而是设置一个方差保留比例,比如95%。这样既能压缩数据,又不会丢失太多信息。另外,PCA之前一定要做标准化,否则量纲大的特征会主导主成分方向。
什么时候用PCA?
- 可视化——降到2维或3维,画个散点图看看数据分布
- 去噪——PCA能过滤掉方差小的噪声成分
- 加速训练——特征太多时,降维能大幅提升训练速度
注意:PCA后的特征失去了可解释性。每个主成分都是原始特征的线性组合,你没法说「这个主成分代表年龄」。如果业务方要求解释模型,PCA可能不是好选择。
知识体系总览
下面这张图是我梳理的本章知识结构,方便你快速回顾:
数据预处理这块,说白了就是「磨刀不误砍柴工」。我见过太多人急着调模型参数,结果数据里一堆问题,白白浪费时间。先把数据收拾利索了,后面的事情就顺了。