器→工具, 工具软件, 数据, 术→技巧

Scikit-Learn体系化学习之数据预处理

钱魏Way · · 193 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

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)缩放,减少异常值影响。

  • 公式:$x’=\frac{x-\text{median}}{IQR}$
  • 适用场景:数据中存在显著异常值。
  • scikit-learn 类:RobustScaler
  • 示例代码:

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

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注