3. 数据预处理:数据清洗、标准化与归一化
做模式分类,数据预处理排第一。你想想看,垃圾数据进来,再牛的算法也白搭。我个人习惯把预处理分成两大块:先把数据洗干净,再把它弄成算法喜欢的尺度。今天咱们就聊聊这两步怎么做。
3.1 数据清洗——先把脏东西筛掉
数据清洗,说白了就是处理缺失值和异常值。
3.1.1 缺失值处理
缺失值很常见。传感器抽风、人工录入遗漏、系统传输丢包,都可能造成某个特征没值。我在项目中遇到过一份UCI的二手房数据,卧室数量那一列大概缺了12%。
怎么处理?我一般按这几个原则来:
- 删除:缺失比例超过70%的特征,直接删列。数据量够大时,缺失比例小的样本也可以删行。
- 填充:数值型用均值、中位数或众数。分类型用最频繁的类别。注意,极端分布时中位数比均值更靠谱。
- 模型预测:缺失值当标签,用其他特征训练模型去补。比如用KNN或回归。
- 标记:干脆把“是否缺失”当新特征扔给模型。树模型往往能利用这个信息。
举个简单的填充例子:
import pandas as pd
import numpy as np
# 读数据
df = pd.read_csv('houses.csv')
# 查看缺失比例
print(df.isnull().mean())
# 用中位数填充卧室数
df['bedrooms'].fillna(df['bedrooms'].median(), inplace=True)
# 用众数填充房屋朝向(分类型)
df['orientation'].fillna(df['orientation'].mode()[0], inplace=True)
小提示:填充前最好看看分布。如果缺失值来自极端值,中位数保留比均值更稳健。
3.1.2 异常值检测
异常值就是离群点。比如年龄列冒出个200岁,房价列出现负值。这些点会拉偏均值和方差,很多模型都对异常点敏感。
常用方法有几个:
- Z-Score法:假设数据正态,超过3个标准差的算异常。
- IQR法:四分位距。小于Q1-1.5×IQR或大于Q3+1.5×IQR就算异常,对偏态数据更鲁棒。
- 孤立森林:无监督,适合高维数据。
我常用IQR法,因为大部分真实数据并不正态。给你看看代码:
def detect_outliers_iqr(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
return data[(data[column] < lower) | (data[column] > upper)]
outliers = detect_outliers_iqr(df, 'price')
print(f'发现 {len(outliers)} 个异常房价')
注意:不要机械地删异常值。我曾经把一批真实的豪宅价格当成异常删了,结果模型在高端区间完全失效。得结合业务判断——这到底是不是真异常。
3.2 数据标准化与归一化
数据洗干净了,接下来要让不同特征的尺度统一。为什么?因为很多算法(SVM、KNN、PCA、神经网络)都依赖距离或梯度,特征范围差太多会出问题。
3.2.1 标准化(Z-Score)
标准化把数据变成均值为0、标准差为1。适合数据近似正态分布的情况。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # X是特征矩阵
转换公式很简单:
- 新值 = (原始值 - 均值) / 标准差
我在项目中做过一个用户画像聚类,用标准化后轮廓系数从0.2跳到了0.7。效果很明显。
3.2.2 归一化(Min-Max)
归一化把数据缩放到[0,1]区间。适合那些有明确上下限的特征,比如像素0-255、评分1-5。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)
公式:
- 新值 = (原始值 - 最小值) / (最大值 - 最小值)
选择原则:
数据近似正态 → 标准化
数据有边界(如RGB、百分比) → 归一化
存在异常值 → 标准化更抗干扰(归一化会被极端值压扁正常数据)
数据近似正态 → 标准化
数据有边界(如RGB、百分比) → 归一化
存在异常值 → 标准化更抗干扰(归一化会被极端值压扁正常数据)
3.2.3 其他常见变换
| 方法 | 适用场景 | 备注 |
|---|---|---|
| RobustScaler | 数据含大量异常值 | 用中位数和IQR,不被离群点干扰 |
| MaxAbsScaler | 稀疏矩阵 | 缩放到[-1,1],保持0元素 |
| 对数/幂变换 | 偏态分布 | 把长尾拉近正态 |
举个RobustScaler的例子:
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler()
X_robust = scaler.fit_transform(X)
3.3 实战中的几个坑
最后说几个我踩过的坑。
- 先切分,后缩放。一定先做训练/测试集切分,再用训练集的数据fit scaler,然后用这个scaler分别transform训练集和测试集。我之前在测试集上用了全局的均值和标准差,结果模型过拟合了。
- 特征工程在缩放之后?先缩放再生成多项式特征,否则高次项会被放大。每次做特征变换,我都重新考虑顺序。
- 缺失值填充不能太土。有一次我用全局均值填充,结果模型学了一个偏差。后来改用按聚类分组的均值,效果就好多了。
一句话总结:
数据预处理不是流水账,每一步都要带着问题去做。先清洗再缩放,是模式分类的起跑线。