Scikit-Learn 的 sklearn.preprocessing 模块提供了一系列数据预处理工具,帮助将原始数据转换为适合机器学习模型的形式。

缺失值处理
在 scikit-learn 中,缺失值处理是数据预处理的关键步骤之一。大多数机器学习模型无法直接处理缺失值,因此需要采用适当的方法填充或删除缺失数据。
核心工具:SimpleImputer
SimpleImputer 是 scikit-learn 中用于填充缺失值的主要类,支持多种填充策略,适用于数值型和分类型数据。
常用参数
- missing_values: 指定数据中的缺失值标识,默认为 nan。
- strategy: 填充策略,可选值:
- “mean”: 用特征列的均值填充(仅数值型)
- “median”: 用特征列的中位数填充(仅数值型)
- “most_frequent”: 用特征列的众数填充(数值型和分类型均适用)
- “constant”: 用固定值填充,需配合 fill_value 参数指定常量。
- fill_value: 当 strategy=”constant” 时,指定填充的常量值。
- copy: 是否创建数据副本(默认为 True)。
示例代码
from sklearn.impute import SimpleImputer import numpy as np # 示例数据(包含缺失值 NaN) X = np.array([[1, 2, np.nan], [np.nan, 3, 4], [5, np.nan, 6]]) # 用均值填充缺失值 imputer_mean = SimpleImputer(strategy="mean") X_mean = imputer_mean.fit_transform(X) # 用众数填充缺失值 imputer_mode = SimpleImputer(strategy="most_frequent") X_mode = imputer_mode.fit_transform(X)
处理分类型数据
当数据中包含分类型特征(如字符串标签)时,需确保使用正确的填充策略。
示例:填充分类特征
from sklearn.preprocessing import OneHotEncoder # 示例数据(含字符串类型的缺失值) X_cat = np.array([["a", "x"], [np.nan, "y"], ["b", np.nan]]) # 用众数填充分类型数据 imputer_cat = SimpleImputer(strategy="most_frequent") X_cat_imputed = imputer_cat.fit_transform(X_cat) # 输出:[['a' 'x'], ['a' 'y'], ['b' 'y']]
处理混合类型数据
若数据同时包含数值型和分类型列,需结合 ColumnTransformer 对不同列应用不同策略。
示例:数值列用均值,分类列用众数
from sklearn.compose import ColumnTransformer
import pandas as pd
# 示例 DataFrame
data = pd.DataFrame({
'Age': [25, 30, np.nan, 35], # 数值型
'Gender': ['M', np.nan, 'F', 'F'] # 分类型
})
# 定义不同列的填充策略
preprocessor = ColumnTransformer(
transformers=[
('num', SimpleImputer(strategy='mean'), ['Age']),
('cat', SimpleImputer(strategy='most_frequent'), ['Gender'])
])
data_imputed = preprocessor.fit_transform(data)
高级缺失值处理
KNNImputer:基于近邻的填充
用相邻样本的特征值填充缺失值,适合数据分布复杂的情况。
from sklearn.impute import KNNImputer imputer_knn = KNNImputer(n_neighbors=2) X_knn = imputer_knn.fit_transform(X)
IterativeImputer:多重插补(实验性功能)
通过迭代模型(如随机森林)预测缺失值,更适用于复杂数据。
from sklearn.experimental import enable_iterative_imputer from sklearn.impute import IterativeImputer imputer_iter = IterativeImputer(random_state=0) X_iter = imputer_iter.fit_transform(X)
在 Pipeline 中集成缺失值处理
为避免数据泄露,建议将 SimpleImputer 嵌入 Pipeline 中,确保仅在训练集上拟合。
示例:完整预处理流程
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
pipeline = Pipeline([
('imputer', SimpleImputer(strategy='median')), # 填充缺失值
('scaler', StandardScaler()), # 标准化
('classifier', RandomForestClassifier()) # 分类模型
])
pipeline.fit(X_train, y_train)
最佳实践与注意事项
- 检测缺失值:使用 isnull() 或 numpy.isnan() 检查缺失值分布。
- 分类型数据:若分类特征缺失较多,可单独填充为 “missing” 类别。
- 版本兼容性:IterativeImputer 需要 scikit-learn ≥ 21 且导入 enable_iterative_imputer。
- 避免数据泄露:在交叉验证或训练/测试集分割后,仅在训练集上拟合 Imputer。
- 替代方案:删除缺失值过多的列:dropna(axis=1, thresh=0.8*len(df))。
实战案例:泰坦尼克数据集
import pandas as pd
from sklearn.model_selection import train_test_split
# 加载数据
data = pd.read_csv('titanic.csv')
X = data[['Age', 'Fare', 'Embarked']]
y = data['Survived']
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 定义预处理:Age 用中位数,Embarked 用众数
preprocessor = ColumnTransformer(
transformers=[
('age', SimpleImputer(strategy='median'), ['Age']),
('embarked', SimpleImputer(strategy='most_frequent'), ['Embarked']),
('fare', 'passthrough', ['Fare']) # 无缺失值,直接保留
])
# 在 Pipeline 中集成预处理和模型
pipeline = Pipeline([
('preprocessor', preprocessor),
('classifier', RandomForestClassifier())
])
pipeline.fit(X_train, y_train)
print("Test Accuracy:", pipeline.score(X_test, y_test))
特征缩放
在机器学习中,特征缩放(Feature Scaling)是将不同量纲或范围的特征转换到统一尺度的重要步骤。许多模型(如 SVM、KNN、神经网络)对特征的尺度敏感,特征缩放能显著提升其性能。
为什么需要特征缩放?
- 消除量纲影响:例如,身高(cm)和体重(kg)数值范围差异巨大,缩放后模型能公平对待各特征。
- 加速梯度下降:线性模型和神经网络的优化过程在尺度统一时收敛更快。
- 提高距离计算准确性:KNN、聚类等基于距离的算法依赖特征尺度一致。
核心特征缩放方法
标准化(Standardization)
- 方法:将数据转换为均值为 0、标准差为 1 的分布。
- 公式:$z=\frac{x-\mu}{\sigma}$
- 适用场景:数据近似服从正态分布,且异常值较少。
- scikit-learn 类:StandardScaler
示例代码:
from sklearn.preprocessing import StandardScaler
import numpy as np
# 示例数据
X = np.array([[1, 2], [3, 4], [5, 6]])
# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 输出结果(均值≈0,标准差≈1)
print("均值:", scaler.mean_) # [3. 4.]
print("标准差:", scaler.scale_) # [1.63299316 1.63299316]
print("缩放后数据:\n", X_scaled)
归一化(Min-Max Scaling)
- 方法:将数据缩放到指定范围(默认[0,1])。
- 公式:$x’=\frac{x-x_{\min}}{x_{\max}-x_{\min}}$
- 适用场景:需要严格限定特征范围(如图像像素值)。
- scikit-learn 类:MinMaxScaler
示例代码:
from sklearn.preprocessing import MinMaxScaler
# 归一化到[0,1]
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)
# 输出结果
print("最小值:", scaler.data_min_) # [1 2]
print("最大值:", scaler.data_max_) # [5 6]
print("缩放后数据:\n", X_scaled)
鲁棒缩放(Robust Scaling)
方法:用中位数和四分位数范围(IQR)缩放,减少异常值影响。
示例代码:
from sklearn.preprocessing import RobustScaler
# 含异常值的数据
X = np.array([[1, 2], [3, 4], [5, 6], [100, 200]])
# 鲁棒缩放
scaler = RobustScaler()
X_scaled = scaler.fit_transform(X)
# 输出结果
print("中位数:", scaler.center_) # [3. 4.]
print("四分位范围:", scaler.scale_) # [2. 2.]
print("缩放后数据:\n", X_scaled)
最大绝对值缩放(MaxAbsScaling)
- 方法:将数据缩放到[-1,1]区间,保留原始数据的稀疏性。
- 公式:$x’=\frac{x}{\max(|x|)}$
- 适用场景:稀疏数据(如词频矩阵)。
- scikit-learn 类:MaxAbsScaler
示例代码:
from sklearn.preprocessing import MaxAbsScaler
# 稀疏数据示例
X = np.array([[1, -2], [3, 0], [-5, 4]])
# 最大绝对值缩放
scaler = MaxAbsScaler()
X_scaled = scaler.fit_transform(X)
# 输出结果
print("最大值绝对值:", scaler.max_abs_) # [5. 4.]
print("缩放后数据:\n", X_scaled)
特征缩放的实践应用
在Pipeline中集成缩放器
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
# 创建包含标准化的 Pipeline
pipeline = Pipeline([
('scaler', StandardScaler()), # 特征缩放
('clf', LogisticRegression()) # 分类模型
])
# 使用方式与普通模型一致
pipeline.fit(X_train, y_train)
accuracy = pipeline.score(X_test, y_test)
处理训练集与测试集
- 训练集:使用 fit_transform 计算参数并转换。
- 测试集:仅使用 transform 应用训练集的参数,避免数据泄露。
# 训练集 scaler.fit(X_train) X_train_scaled = scaler.transform(X_train) # 测试集 X_test_scaled = scaler.transform(X_test)
结合 ColumnTransformer 处理混合特征
当数据包含数值型和分类型特征时,对不同列应用不同预处理:
from sklearn.compose import ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), ['age', 'income']), # 数值列标准化
('cat', OneHotEncoder(), ['gender']) # 分类型列编码
])
pipeline = Pipeline([
('preprocessor', preprocessor),
('model', RandomForestClassifier())
])
特征缩放的注意事项
- 不需要缩放的情况:
- 决策树、随机森林等树模型对特征尺度不敏感。
- 特征本身已在相似范围内(如像素值 0-255)。
- 异常值处理:
- 若使用 StandardScaler 或 MinMaxScaler,需先处理异常值,否则缩放后数据仍可能失真。
- 对异常值较多的数据,优先选择 RobustScaler。
- 稀疏数据:
- 避免使用 StandardScaler 或 MinMaxScaler,可能破坏稀疏性。优先使用 MaxAbsScaler。
- 顺序问题:
- 先处理缺失值(如用 SimpleImputer),再进行特征缩放。
不同缩放方法对比
| 方法 | 适用场景 | 对异常值敏感度 | 输出范围 |
| StandardScaler | 数据近似正态分布 | 高 | 无固定范围 |
| MinMaxScaler | 需要严格限定范围(如图像) | 高 | [0,1] 或 [-1,1] |
| RobustScaler | 数据含较多异常值 | 低 | 无固定范围 |
| MaxAbsScaler | 稀疏数据(如文本 TF-IDF) | 中 | [-1, 1] |
选择合适的特征缩放方法需考虑数据分布、异常值情况以及模型需求:
- 默认尝试:从 StandardScaler 开始,大部分场景表现良好。
- 异常值处理:优先使用 RobustScaler。
- 稀疏数据:选择 MaxAbsScaler 或保持原始数据。
通过合理应用特征缩放,可以显著提升模型性能并加速训练过程。建议在模型开发初期将特征缩放纳入预处理流程,并通过交叉验证对比不同缩放方法的效果。
分类变量编码
在机器学习中,分类变量(Categorical Variables)需要转换为数值形式才能被模型处理。scikit-learn 提供了多种编码方法,适用于不同场景。
分类变量类型
- 有序分类变量(Ordinal):类别有顺序关系(如学历:小学、中学、大学)。
- 无序分类变量(Nominal):类别无顺序关系(如城市:北京、上海、广州)。
- 高基数变量(High-Cardinality):类别数量极多(如用户 ID)。
核心编码方法
标签编码(Label Encoding)
- 方法:将每个类别映射为整数(0, 1, 2,…)。
- 适用场景:有序分类变量。
- scikit-learn 类:LabelEncoder(仅用于标签列)或 OrdinalEncoder(用于特征列)。
示例代码:
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder # 标签列编码(适用于目标变量) y = ["cat", "dog", "cat", "bird"] le = LabelEncoder() y_encoded = le.fit_transform(y) # 输出: [0, 1, 0, 2] # 特征列编码(推荐使用 OrdinalEncoder) X = [["cat"], ["dog"], ["bird"]] oe = OrdinalEncoder() X_encoded = oe.fit_transform(X) # 输出: [[0.], [1.], [2.]]
独热编码(One-Hot Encoding)
- 方法:为每个类别创建一个二进制列(0/1)。
- 适用场景:无序分类变量(类别数较少)。
- scikit-learn 类:OneHotEncoder
示例代码:
from sklearn.preprocessing import OneHotEncoder
X = [["apple"], ["banana"], ["orange"], ["apple"]]
ohe = OneHotEncoder(sparse_output=False)
X_ohe = ohe.fit_transform(X)
# 输出(每个类别对应一列):
# [[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]
# [1. 0. 0.]]
print("类别名称:", ohe.get_feature_names_out()) # ['apple', 'banana', 'orange']
关键参数:
- sparse_output=False:输出密集矩阵(默认 True 为稀疏矩阵)。
- handle_unknown=”ignore”:遇到未知类别时全填 0。
- drop=”first”:删除第一个类别以避免多重共线性。
目标编码(Target Encoding)
- 方法:用目标变量的均值(或其他统计量)代替类别。
- 适用场景:高基数分类变量。
- 库:需安装 category_encoders(非 scikit-learn 内置)。
示例代码:
# 需要先安装:pip install category_encoders
from category_encoders import TargetEncoder
import pandas as pd
X = pd.DataFrame({"city": ["A", "B", "A", "C"]})
y = [10, 20, 15, 30]
encoder = TargetEncoder()
X_encoded = encoder.fit_transform(X, y)
# 输出(A: (10+15)/2=12.5, B: 20, C: 30):
# city
# 0 12.5
# 1 20.0
# 2 12.5
# 3 30.0
注意事项:
- 需配合交叉验证使用,避免数据泄露。
- 可添加平滑(smoothing)参数防止过拟合。
混合类型数据处理
当数据集包含数值型和分类型特征时,使用 ColumnTransformer 对不同的列应用不同编码。
示例代码:
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
# 定义预处理流程
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), ['age', 'income']), # 数值列标准化
('cat', OneHotEncoder(), ['gender', 'city']) # 分类型列独热编码
])
# 构建 Pipeline
pipeline = Pipeline([
('preprocessor', preprocessor),
('classifier', RandomForestClassifier())
])
# 训练模型
pipeline.fit(X_train, y_train)
高级编码技巧
处理未知类别
设置 handle_unknown=”ignore”,避免测试集出现新类别时报错:
ohe = OneHotEncoder(handle_unknown="ignore")
稀疏矩阵优化
当类别数较多时,使用稀疏矩阵节省内存:
ohe = OneHotEncoder(sparse_output=True) # 默认即为 True
分箱处理高基数变量
对高基数变量(如邮编)先分箱再编码:
from sklearn.preprocessing import KBinsDiscretizer # 将城市按频率分箱为 5 类 encoder = KBinsDiscretizer(n_bins=5, encode="ordinal", strategy="quantile") X_binned = encoder.fit_transform(X[["city_freq"]])
编码方法对比
| 方法 | 适用场景 | 优点 | 缺点 |
| OrdinalEncoder | 有序分类变量 | 保留顺序信息 | 可能引入虚假的数值关系 |
| OneHotEncoder | 无序分类变量(类别少) | 消除类别间虚假顺序 | 维度爆炸(高基数不适用) |
| TargetEncoder | 高基数分类变量 | 减少维度,保留信息 | 需谨慎防止数据泄露 |
| LabelEncoder | 目标变量编码 | 简单快速 | 仅用于标签列,不适用特征 |
最佳实践
- 有序变量:优先使用 OrdinalEncoder,需手动指定顺序(通过 categories 参数)。
- 无序变量:类别数 <10 时用 OneHotEncoder,否则用 TargetEncoder。
- 高基数变量:分箱(Binning)或目标编码(TargetEncoding)。
- 流程控制:始终在交叉验证或 Pipeline 中处理编码,避免数据泄露。
通过合理选择编码方法,可以显著提升模型性能。建议在项目初期通过实验对比不同编码方式的效果(如使用交叉验证评估准确率)。
离散化与分箱
离散化(分箱)是将连续特征转换为离散区间的过程,常用于处理非线性关系、减少噪声或适应只能处理类别特征的模型。scikit-learn 提供了 KBinsDiscretizer 类实现多种分箱策略。
核心类:KBinsDiscretizer
主要参数
- n_bins:分箱数量(整数或数组,数组为各特征分别指定)。
- encode:编码方式:
- ‘onehot’:独热编码(生成稀疏矩阵)。
- ‘onehot-dense’:独热编码(生成密集矩阵)。
- ‘ordinal’:序数编码(默认,生成整数标签)。
- strategy:分箱策略:
- ‘uniform’:等宽分箱(区间宽度相同)。
- ‘quantile’:等频分箱(每箱样本数相近)。
- ‘kmeans’:基于 K-Means 聚类的分箱。
代码示例
from sklearn.preprocessing import KBinsDiscretizer
import numpy as np
# 示例数据(单特征)
X = np.array([[1.2], [3.4], [2.5], [5.6], [0.8], [4.1]])
# 等宽分箱(3 箱,序数编码)
encoder = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform')
X_binned = encoder.fit_transform(X)
print("等宽分箱结果:\n", X_binned)
# 输出分箱边界
print("分箱边界:", encoder.bin_edges_)
输出:
等宽分箱结果: [[0.] [2.] [1.] [2.] [0.] [2.]] 分箱边界: [array([0.8, 2.6, 4.4, 5.6])]
分箱策略对比
等宽分箱(strategy=’uniform’)
- 原理:按特征值范围均分区间。
- 优点:计算简单。
- 缺点:对异常值敏感,可能导致箱内样本不均衡。
- 适用场景:数据分布均匀,无显著异常值。
等频分箱(strategy=’quantile’)
- 原理:按样本分位数划分,使每箱样本数大致相同。
- 优点:适应数据分布,避免空箱。
- 缺点:箱边界可能不稳定(小数据集)。
- 适用场景:数据分布倾斜,需平衡样本分布。
K-Means 分箱(strategy=’kmeans’)
- 原理:通过聚类中心确定箱边界。
- 优点:根据数据分布自适应划分。
- 缺点:计算成本较高。
- 适用场景:数据存在自然聚类。
分箱编码方式
序数编码(encode=’ordinal’)
- 输出:整数标签(如 0,1,2),表示样本所属的箱。
- 优点:保留顺序信息,维度低。
- 适用模型:树模型、线性模型。
独热编码(encode=’onehot’)
- 输出:稀疏矩阵,每箱对应一个二元特征。
- 优点:消除序数影响,适合非线性模型。
- 适用模型:逻辑回归、神经网络。
进阶应用
多特征分箱
# 多特征分箱示例(不同特征不同分箱数)
X_multi = np.array([[1.2, 0.5], [3.4, 2.7], [2.5, 4.1]])
encoder = KBinsDiscretizer(n_bins=[3, 2], encode='ordinal', strategy='uniform')
X_multi_binned = encoder.fit_transform(X_multi)
print("多特征分箱结果:\n", X_multi_binned)
分箱后特征解释
# 获取分箱边界
bin_edges = encoder.bin_edges_
for i, edges in enumerate(bin_edges):
print(f"特征{i}的分箱边界:", edges)
分箱与 Pipeline 整合
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
pipeline = Pipeline([
('discretizer', KBinsDiscretizer(n_bins=3, encode='onehot-dense')),
('classifier', LogisticRegression())
])
pipeline.fit(X_train, y_train)
注意事项
处理边缘值
- 默认包含最小值,不包含最大值(可通过 subsample 参数调整样本估计分位数)。
稀疏矩阵处理
- 使用 encode=’onehot’ 时,输出为 sparse 矩阵,需转换为密集矩阵(.toarray())供部分模型使用。
分箱数量选择
- 通过交叉验证比较不同 n_bins 的模型性能。
- 探索性分析(如直方图)辅助确定合理分箱数。
完整案例:分箱提升线性模型性能
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# 加载数据
data = fetch_california_housing()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 分箱预处理
encoder = KBinsDiscretizer(n_bins=10, encode='onehot-dense', strategy='quantile')
X_train_binned = encoder.fit_transform(X_train)
X_test_binned = encoder.transform(X_test)
# 训练线性回归
model = LinearRegression()
model.fit(X_train_binned, y_train)
y_pred = model.predict(X_test_binned)
print("分箱后MSE:", mean_squared_error(y_test, y_pred))
# 对比未分箱的线性回归
model_raw = LinearRegression()
model_raw.fit(X_train, y_train)
y_pred_raw = model_raw.predict(X_test)
print("原始数据MSE:", mean_squared_error(y_test, y_pred_raw))
总结
| 分箱方法 | 适用场景 | 优点 | 缺点 |
| 等宽分箱(uniform) | 数据分布均匀,无异常值 | 计算简单 | 对异常值敏感 |
| 等频分箱(quantile) | 避免空箱 | 边界不稳定 | |
| K-Means 分箱 | 数据存在自然聚类结构 | 自适应划分 | 计算成本高 |
通过合理选择分箱策略和参数,可有效捕捉连续特征的非线性关系,提升模型性能。建议优先尝试等频分箱,结合交叉验证优化分箱数,并通过可视化(如直方图)验证分箱效果。
多项式特征生成
多项式特征生成(Polynomial Features)是一种通过组合特征的幂次和交互项来扩展特征空间的技术,能够帮助线性模型捕捉非线性关系,提升模型表达能力。
核心概念与原理
- 目标:将原始特征的乘积和幂次作为新特征加入模型。
- 数学表达:对于特征 $x_1$ 和 $x_2$,生成二次多项式特征包括:$\{1, x_1, x_2, x_1^2, x_2^2, x_1x_2\}$
- 适用场景:
- 线性模型(如线性回归、逻辑回归)需要捕捉非线性关系。
- 特征间存在交互效应(如房价预测中“面积 × 地段”)。
scikit-learn 实现:PolynomialFeatures
核心参数
- degree:多项式的最高次数(默认=2)。
- interaction_only:是否仅生成交互项(如 x1x2,不含 x1^2),默认=False。
- include_bias:是否包含偏置项(全1列),默认=True。若后续模型已有截距项,建议设为 False。
示例代码
from sklearn.preprocessing import PolynomialFeatures
import numpy as np
# 原始特征(2个样本,2个特征)
X = np.array([[2, 3], [1, 4]])
# 生成二次多项式特征(包含所有组合)
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X)
print("原始特征维度:", X.shape) # (2, 2)
print("多项式特征维度:", X_poly.shape) # (2, 5)
print("特征名称:", poly.get_feature_names_out())
# 输出:['x0' 'x1' 'x0^2' 'x0 x1' 'x1^2']
print("转换后数据:\n", X_poly)
输出:
[[ 2. 3. 4. 6. 9.] [ 1. 4. 1. 4. 16.]]
实战应用场景
提升线性回归模型性能
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error
# 构造非线性数据
X = np.arange(0, 10).reshape(-1, 1)
y = 3 * X[:, 0]**2 + 5 # 二次关系
# 对比普通线性回归 vs 多项式回归
model_linear = LinearRegression()
model_linear.fit(X, y)
pred_linear = model_linear.predict(X)
model_poly = Pipeline([
('poly', PolynomialFeatures(degree=2)),
('linear', LinearRegression())
])
model_poly.fit(X, y)
pred_poly = model_poly.predict(X)
# 计算 MSE
print("普通线性回归 MSE:", mean_squared_error(y, pred_linear)) # 较高
print("多项式回归 MSE:", mean_squared_error(y, pred_poly)) # ≈0
可视化效果
import matplotlib.pyplot as plt plt.scatter(X, y, label='真实数据') plt.plot(X, pred_linear, 'r--', label='线性回归') plt.plot(X, pred_poly, 'g-', label='二次多项式回归') plt.legend() plt.show()
图示:多项式回归完美拟合二次曲线,而线性回归无法捕捉非线性趋势。
高级用法与调优
控制特征维度
问题:当原始特征为n 维,生成 degree=d 的多项式特征数为:$C(n+d, d)=\frac{(n+d)!}{n!d!}$
例如,n=10 且 d=3 时,特征数增至 286,可能引发维度灾难。
解决方案:
- 使用 interaction_only=True 仅保留交互项(如 $x_1x_2$,不含 $x_1^2$)。
- 结合 SelectKBest 或正则化(Lasso)选择重要特征。
交叉验证选择最佳次数
from sklearn.model_selection import GridSearchCV
# 定义 Pipeline
pipeline = Pipeline([
('poly', PolynomialFeatures()),
('model', LinearRegression())
])
# 参数网格:尝试不同多项式次数
param_grid = {'poly__degree': [1, 2, 3, 4]}
# 网格搜索
grid = GridSearchCV(pipeline, param_grid, cv=5, scoring='neg_mean_squared_error')
grid.fit(X, y)
print("最佳次数:", grid.best_params_['poly__degree'])
与正则化结合防止过拟合
from sklearn.linear_model import Ridge # 生成高次多项式特征 poly = PolynomialFeatures(degree=10) X_poly = poly.fit_transform(X) # 使用岭回归(L2 正则化) model = Ridge(alpha=0.1) model.fit(X_poly, y)
注意事项
- 数据标准化:生成多项式特征前应先标准化(StandardScaler),避免数值不稳定。
- 避免高阶过拟合:高次多项式易在训练集过拟合,需通过验证集评估泛化性能。
- 稀疏性处理:若原始数据稀疏(如文本特征),多项式特征可能进一步加剧稀疏性。
- 业务意义:优先生成具有业务解释性的交互项(如“收入 × 年龄”)。
总结
- 优点:增强线性模型对非线性关系的拟合能力。
- 缺点:特征维度快速增长,可能引入噪声和过拟合。
- 适用模型:线性回归、逻辑回归、支持向量机(核函数替代时可能更高效)。
- 替代方案:对非线性问题,也可考虑树模型(如随机森林)或神经网络。
通过合理选择多项式次数、结合正则化和特征选择,多项式特征生成能显著提升模型性能。建议在特征工程初期尝试低次(2-3次)多项式,并通过交叉验证优化参数。
其他变换工具
正则化(Normalizer)
核心作用
目的:将每个样本的向量归一化为单位范数(默认L2范数),常用于文本分类或聚类(如计算余弦相似度)。
数学公式:$x_{\text{norm}}=\frac{x}{\|x\|}$
- L1范数:向量元素绝对值之和。
- L2范数:向量欧氏距离。
- Max范数:向量最大绝对值。
参数说明
- norm:范数类型(’l1’、’l2’、’max’,默认’l2’)。
代码示例
from sklearn.preprocessing import Normalizer
import numpy as np
# 示例数据(3个样本,2个特征)
X = np.array([[1, 2], [3, 4], [5, 6]])
# 使用L2范数正则化
normalizer = Normalizer(norm='l2')
X_normalized = normalizer.fit_transform(X)
print("正则化后数据:\n", X_normalized)
[[0.447 0.894] [0.6 0.8] [0.641 0.769]]
适用场景
- 文本向量化:TF-IDF矩阵的样本归一化。
- 相似度计算:确保样本向量在相同尺度比较(如KNN算法)。
- 图像处理:像素值归一化。
注意事项
- 按行处理:与StandardScaler(按列处理)不同,Normalizer对每个样本独立操作。
- 稀疏矩阵:支持稀疏输入(如sparse矩阵)。
幂变换(PowerTransformer)
核心作用
目的:通过幂函数变换使数据更接近正态分布,提升模型的稳定性和性能(如线性回归、SVM)。
常用方法:
- Yeo-Johnson:支持正负值数据。
- Box-Cox:仅适用于正值数据。
参数说明
- method:变换方法(’yeo-johnson’、’box-cox’,默认’yeo-johnson’)。
- standardize:变换后是否标准化(默认True,即零均值、单位方差)。
代码示例
from sklearn.preprocessing import PowerTransformer
# 生成右偏数据
X = np.random.exponential(scale=2, size=(100, 1))
# 应用Yeo-Johnson变换
pt = PowerTransformer(method='yeo-johnson')
X_transformed = pt.fit_transform(X)
# 可视化变换前后分布
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.hist(X, bins=30)
plt.title("原始数据分布")
plt.subplot(1, 2, 2)
plt.hist(X_transformed, bins=30)
plt.title("Yeo-Johnson变换后分布")
plt.show()
适用场景
- 线性模型:处理残差非正态分布问题。
- 异方差数据(方差随均值变化)。
- 非对称分布(如指数分布、对数正态分布)。
注意事项
- Box-Cox限制:输入数据必须为正,否则自动切换为Yeo-Johnson。
- 拟合与变换分离:测试集需使用训练集的变换参数(通过fit(X_train)后pt.transform(X_test))。
二值化(Binarizer)
核心作用
- 目的:根据阈值将特征值转换为0或1,常用于生成布尔型特征或简化模型。
- 数学公式:
参数说明
- threshold:阈值(默认0)。
代码示例
from sklearn.preprocessing import Binarizer
# 示例数据
X = np.array([[1.5, 0.3], [-0.7, 2.1]])
# 阈值设为0.5
binarizer = Binarizer(threshold=0.5)
X_binarized = binarizer.fit_transform(X)
print("二值化结果:\n", X_binarized)
输出:
[[1. 0.] [0. 1.]]
适用场景
- 图像处理:像素值二值化(如OCR文字识别)。
- 特征工程:将连续特征转换为布尔特征(如年龄是否大于30岁)。
- 简化模型:逻辑回归中的特征处理。
注意事项
- 阈值选择:需结合业务背景或通过网格搜索优化。
- 信息损失:过度二值化可能导致特征信息丢失。
组合与自动化流程
ColumnTransformer是scikit-learn中用于对数据的不同列应用不同预处理步骤的工具,特别适用于处理包含混合类型特征(如数值型、分类型、文本型)的数据集。
核心功能与使用场景
功能
- 并行处理多列:对指定列应用特定转换器(如标准化、编码、缺失值填充)。
- 灵活列选择:通过列名或索引选择目标列。
- 结果合并:自动拼接各转换器的输出,生成统一特征矩阵。
适用场景
- 数据集包含数值型、分类型、文本型等混合特征。
- 需对不同类型特征分别预处理(如数值列标准化、分类型列独热编码)。
基本语法与参数
构造函数
from sklearn.compose import ColumnTransformer
ct = ColumnTransformer(
transformers=[
('name1', transformer1, columns1),
('name2', transformer2, columns2),
...
],
remainder='drop', # 处理未指定的列:'drop'(默认)或'passthrough'
sparse_threshold=0.3, # 稀疏矩阵合并阈值(稀疏输出比例>该值时返回稀疏矩阵)
n_jobs=None, # 并行任务数
verbose=False # 是否显示处理日志
)
关键参数说明
- transformers:
- 元组列表,格式为(名称, 转换器, 列)。
- 列选择:可通过列名(DataFrame)或列索引(numpy数组)指定。
- remainder:
- ‘drop’:丢弃未指定的列。
- ‘passthrough’:保留未指定的列,不做处理。
- sparse_threshold:控制输出类型,默认3(若转换后的稀疏矩阵占比超过30%,则返回稀疏矩阵)。
基础使用示例
数值型与分类型列分别处理
import pandas as pd
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
# 示例数据
data = pd.DataFrame({
'age': [25, 30, 35],
'income': [50000, 80000, 120000],
'gender': ['M', 'F', 'M']
})
# 定义列转换器
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), ['age', 'income']), # 数值列标准化
('cat', OneHotEncoder(), ['gender']) # 分类型列独热编码
],
remainder='passthrough' # 保留未处理的列(此处无)
)
# 应用转换
X_transformed = preprocessor.fit_transform(data)
print("转换后数据:\n", X_transformed)
输出:
[[-1.336 -0.894 1. 0.] [ 0. -0.358 0. 1.] [ 1.336 1.342 1. 0.]]
进阶用法
结合Pipeline处理缺失值
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
# 数值列处理:填充缺失值→标准化
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='mean')),
('scaler', StandardScaler())
])
# 分类型列处理:填充缺失值→独热编码
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
# 定义ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, ['age', 'income']),
('cat', categorical_transformer, ['gender'])
]
)
处理文本特征
from sklearn.feature_extraction.text import TfidfVectorizer
# 假设数据包含文本列'text'
text_transformer = TfidfVectorizer(max_features=100)
preprocessor = ColumnTransformer(
transformers=[
('text', text_transformer, 'text'),
('num', numeric_transformer, ['age', 'income'])
]
)
获取转换后的特征名称
# 获取转换后的特征名称
feature_names = preprocessor.get_feature_names_out()
print("特征名称:", feature_names)
输出:
['num__age', 'num__income', 'cat__gender_F', 'cat__gender_M']
与模型集成的完整流程
构建完整Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
# 创建完整流程:预处理→模型
model = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', RandomForestClassifier())
])
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 训练与评估
model.fit(X_train, y_train)
print("测试集准确率:", model.score(X_test, y_test))
注意事项与最佳实践
- 防止数据泄露:始终在训练集上调用fit方法,再在测试集上应用transform。
- 顺序操作:先处理缺失值,再编码分类变量,最后进行特征缩放。
- 类别型变量处理:使用OneHotEncoder时设置handle_unknown=’ignore’,防止测试集出现新类别。
- 特征名称保留:使用get_feature_names_out()获取转换后特征名,方便调试和解释模型。
- 稀疏矩阵优化:若转换器生成稀疏矩阵(如 OneHotEncoder),设置 sparse_threshold=0.3 平衡内存与效率。
- 列选择方式:优先使用列名(pandas 数据)而非索引,避免列顺序变化导致错误。
实战案例:泰坦尼克数据集预处理
import pandas as pd
from sklearn.datasets import fetch_openml
# 加载数据
titanic = fetch_openml('titanic', version=1)
X = titanic.data[['pclass', 'sex', 'age', 'fare']]
y = titanic.target
# 定义预处理流程
numeric_features = ['age', 'fare']
categorical_features = ['pclass', 'sex']
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder())
])
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
]
)
# 构建模型
model = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', RandomForestClassifier())
])
# 训练与评估
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model.fit(X_train, y_train)
print("准确率:", model.score(X_test, y_test))
通过 sklearn.preprocessing,用户可以高效完成数据清洗、特征工程和格式标准化,为后续建模奠定基础。结合 Pipeline 和 ColumnTransformer,能够构建自动化、可复用的数据处理流程,显著提升机器学习工作流的效率。



