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,能够构建自动化、可复用的数据处理流程,显著提升机器学习工作流的效率。