数据预处理是机器学习流程中至关重要的一环,直接影响模型的训练效果和泛化能力。Scikit-Learn(sklearn)作为Python主流的机器学习库,提供了一套完整、易用的预处理工具集,涵盖缺失值填充、特征缩放、类别特征编码、数据划分等核心功能。本文从实际应用场景出发,详细讲解Sklearn数据预处理的常用方法、实现步骤及避坑要点。
一、数据预处理核心原则与准备工作
1. 核心原则
- 数据泄露禁止:所有预处理操作(如特征缩放、缺失值填充)仅能基于训练集拟合,测试集需复用训练集的预处理规则,避免引入测试集信息;
- 维度对齐:预处理后特征矩阵的维度需与模型输入要求一致;
- 按需选择:不同模型对数据的要求不同(如线性模型需特征缩放,树模型无需),需针对性选择预处理方法。
2. 环境与数据准备
确保已安装所需库,本文以经典的泰坦尼克号数据集(含缺失值、类别特征)为例演示:
bash
# 安装依赖
pip install scikit-learn pandas numpy
python
# 导入工具库
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
# 加载数据集(可从Kaggle下载,或使用示例数据)
data = pd.read_csv('titanic.csv')
# 选取核心特征和标签
X = data[['Age', 'Fare', 'Pclass', 'Sex', 'Embarked']] # 特征
y = data['Survived'] # 标签(是否存活)
# 划分训练集和测试集(先划分再预处理,避免数据泄露)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42 # 固定随机种子,结果可复现
)
二、缺失值处理:SimpleImputer
真实数据中缺失值是常态,Sklearn的SimpleImputer支持数值型、类别型数据的缺失值填充,核心参数为strategy(填充策略)。
1. 数值型特征缺失值填充
针对Age(年龄)、Fare(票价)等数值特征,常用填充策略:均值(mean)、中位数(median)、固定值(constant)。
python
from sklearn.impute import SimpleImputer
# 1. 提取数值型特征
numeric_features = ['Age', 'Fare']
X_train_numeric = X_train[numeric_features]
X_test_numeric = X_test[numeric_features]
# 2. 实例化填充器(中位数填充,鲁棒性更强,抗异常值)
numeric_imputer = SimpleImputer(strategy='median')
# 3. 仅在训练集拟合(关键!),并转换训练集
X_train_numeric_imputed = numeric_imputer.fit_transform(X_train_numeric)
# 4. 用训练集的填充规则转换测试集
X_test_numeric_imputed = numeric_imputer.transform(X_test_numeric)
# 查看填充器的填充值(如Age的中位数)
print(f"Age的填充值:{numeric_imputer.statistics_[0]}")
print(f"Fare的填充值:{numeric_imputer.statistics_[1]}")
2. 类别型特征缺失值填充
针对Sex(性别)、Embarked(登船港口)等类别特征,常用填充策略:众数(most_frequent)、固定值(constant)。
python
# 1. 提取类别型特征
categorical_features = ['Pclass', 'Sex', 'Embarked']
X_train_categorical = X_train[categorical_features]
X_test_categorical = X_test[categorical_features]
# 2. 实例化填充器(众数填充)
categorical_imputer = SimpleImputer(strategy='most_frequent')
# 3. 拟合训练集并转换
X_train_categorical_imputed = categorical_imputer.fit_transform(X_train_categorical)
X_test_categorical_imputed = categorical_imputer.transform(X_test_categorical)
三、类别特征编码:OneHotEncoder & LabelEncoder
类别特征(如Sex:male/female)无法直接输入模型,需转换为数值形式,Sklearn提供两种核心编码方式:
1. 独热编码(OneHotEncoder):适合无序类别特征
针对无大小关系的类别(如Embarked:C/Q/S),独热编码将每个类别转换为独立的二进制特征,避免模型误判类别间的大小关系。
python
from sklearn.preprocessing import OneHotEncoder
# 实例化独热编码器(忽略未知类别,避免测试集出现新类别报错)
onehot_encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
# 拟合训练集的类别特征(已填充缺失值)
X_train_categorical_encoded = onehot_encoder.fit_transform(X_train_categorical_imputed)
# 转换测试集
X_test_categorical_encoded = onehot_encoder.transform(X_test_categorical_imputed)
# 查看编码后的特征名(便于理解)
print("编码后的类别特征名:", onehot_encoder.get_feature_names_out(categorical_features))
2. 标签编码(LabelEncoder):适合有序/二分类特征
仅适用于标签(y)或有序类别特征(如Pclass:1/2/3等舱位),不可用于无序特征的独热编码替代(会引入虚假顺序)。
python
from sklearn.preprocessing import LabelEncoder
# 示例:对标签y进行编码(二分类可省略,此处仅演示)
le = LabelEncoder()
y_train_encoded = le.fit_transform(y_train)
y_test_encoded = le.transform(y_test)
四、特征缩放:StandardScaler & MinMaxScaler
特征缩放用于消除不同特征量纲的影响(如Age:0-100,Fare:0-500),核心适用于线性模型、SVM、KNN等对尺度敏感的模型。
1. 标准化(StandardScaler):均值为0,方差为1
适用于数据近似正态分布的场景,鲁棒性较强:
python
from sklearn.preprocessing import StandardScaler
# 实例化标准化器
scaler = StandardScaler()
# 拟合训练集的数值特征(已填充缺失值)
X_train_numeric_scaled = scaler.fit_transform(X_train_numeric_imputed)
# 转换测试集
X_test_numeric_scaled = scaler.transform(X_test_numeric_imputed)
# 查看标准化参数(均值和标准差)
print(f"Age的均值:{scaler.mean_[0]}, 标准差:{scaler.scale_[0]}")
2. 归一化(MinMaxScaler):缩放到0-1区间
适用于数据分布无明显规律、需要固定范围的场景:
python
from sklearn.preprocessing import MinMaxScaler
# 实例化归一化器
minmax_scaler = MinMaxScaler()
X_train_numeric_normalized = minmax_scaler.fit_transform(X_train_numeric_imputed)
X_test_numeric_normalized = minmax_scaler.transform(X_test_numeric_imputed)
五、预处理流水线:Pipeline(推荐!)
手动分步处理易出错且繁琐,Sklearn的Pipeline可将缺失值填充、编码、缩放等步骤封装为一个整体,避免数据泄露,简化流程。
1. 构建预处理流水线
python
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
# 1. 定义数值特征的预处理流程:缺失值填充 + 标准化
numeric_pipeline = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# 2. 定义类别特征的预处理流程:缺失值填充 + 独热编码
categorical_pipeline = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(sparse_output=False, handle_unknown='ignore'))
])
# 3. 整合数值和类别特征的预处理(ColumnTransformer按列处理)
preprocessor = ColumnTransformer(
transformers=[
('numeric', numeric_pipeline, numeric_features),
('categorical', categorical_pipeline, categorical_features)
]
)
# 4. 拟合并转换训练集(一步完成所有预处理)
X_train_processed = preprocessor.fit_transform(X_train)
# 5. 转换测试集
X_test_processed = preprocessor.transform(X_test)
# 查看预处理后的特征维度
print(f"训练集预处理后维度:{X_train_processed.shape}")
print(f"测试集预处理后维度:{X_test_processed.shape}")
2. 流水线结合模型训练
可将预处理和模型训练封装为一个完整流水线,进一步简化流程:
python
from sklearn.linear_model import LogisticRegression
# 构建完整流水线:预处理 + 模型训练
full_pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', LogisticRegression(random_state=42))
])
# 训练(自动先预处理训练集,再训练模型)
full_pipeline.fit(X_train, y_train)
# 预测(自动预处理测试集,再预测)
y_pred = full_pipeline.predict(X_test)
# 评估模型
from sklearn.metrics import accuracy_score
print(f"模型准确率:{accuracy_score(y_test, y_pred):.2f}")
六、其他常用预处理方法
1. 异常值处理:RobustScaler
针对含异常值的数据,RobustScaler基于分位数(中位数、四分位距)缩放,抗异常值影响:
python
from sklearn.preprocessing import RobustScaler
robust_scaler = RobustScaler()
X_train_robust = robust_scaler.fit_transform(X_train_numeric_imputed)
2. 特征分箱:KBinsDiscretizer
将连续特征转换为离散类别(如Age分为少年/青年/中年/老年):
python
from sklearn.preprocessing import KBinsDiscretizer
binner = KBinsDiscretizer(n_bins=4, encode='onehot', strategy='quantile')
X_train_binned = binner.fit_transform(X_train[['Age']])
3. 缺失值标记:MissingIndicator
除填充缺失值外,可额外添加"是否缺失"的特征,提升模型对缺失值的感知:
python
from sklearn.impute import MissingIndicator
indicator = MissingIndicator()
X_train_missing = indicator.fit_transform(X_train_numeric)
# 合并原始特征和缺失标记特征
X_train_combined = np.hstack([X_train_numeric_imputed, X_train_missing])
七、预处理避坑指南
- 先划分数据集再预处理:绝对禁止先预处理再划分,否则测试集的均值/中位数等会包含训练集信息,导致模型评估偏乐观;
- OneHotEncoder处理未知类别 :设置
handle_unknown='ignore',避免测试集出现训练集未见过的类别时报错; - 稀疏矩阵优化:独热编码默认返回稀疏矩阵(sparse_output=True),适合高维数据,可减少内存占用;
- 有序类别特征编码 :若类别有顺序(如Pclass:1>2>3),可使用
OrdinalEncoder,而非OneHotEncoder; - 流水线参数调优 :可结合
GridSearchCV对流水线中的参数(如填充策略、缩放方式)进行调优:
python
from sklearn.model_selection import GridSearchCV
# 定义参数网格
param_grid = {
'preprocessor__numeric__imputer__strategy': ['mean', 'median'],
'classifier__C': [0.1, 1, 10]
}
# 网格搜索
grid_search = GridSearchCV(full_pipeline, param_grid, cv=5, scoring='accuracy')
grid_search.fit(X_train, y_train)
print(f"最优参数:{grid_search.best_params_}")