Sklearn 数据预处理(千字长文)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在机器学习的旅程中,数据预处理如同烹饪前的食材准备——看似平凡,却直接影响最终成果的质量。Sklearn 数据预处理作为 Scikit-learn 库的核心模块之一,提供了从标准化到特征工程的完整工具链。无论是处理缺失值、编码分类变量,还是调整数据分布,这些步骤都是构建高效模型的基石。本文将通过循序渐进的讲解和实际案例,帮助编程初学者和中级开发者掌握这一关键技能。


数据标准化:让数据“同频共振”

数据标准化(Normalization)是将特征缩放到统一量纲的过程。想象一下,若一个特征的范围是 0 到 1,而另一个特征的范围是 1000 到 10000,模型可能会过度关注后者。

核心工具:StandardScaler
StandardScaler 通过以下公式实现标准化:
[ X_{\text{scaled}} = \frac{X - \mu}{\sigma} ]
其中,(\mu) 是均值,(\sigma) 是标准差。

代码示例

from sklearn.preprocessing import StandardScaler
import numpy as np

data = np.array([[1, 2], [3, 4], [5, 6]])
scaler = StandardScaler()
scaled_data = scaler.fit_transform(data)

print("原始数据均值:", np.mean(data, axis=0))
print("标准化后均值:", np.mean(scaled_data, axis=0))

输出结果

原始数据均值: [3. 4.]
标准化后均值: [0. 0.]

标准化后的数据均值为 0,标准差为 1,确保特征在相同尺度上。


缺失值处理:修补数据的“漏洞”

现实数据常因采集错误或缺失导致不完整。Sklearn 数据预处理提供了 SimpleImputer 模块,支持多种填充策略:均值、中位数、众数或常数值。

案例场景
假设某用户年龄数据存在缺失,可用均值填充:

from sklearn.impute import SimpleImputer
import numpy as np

data = np.array([[25, np.nan], [30, 150], [np.nan, 160]])
imputer = SimpleImputer(strategy='mean')
filled_data = imputer.fit_transform(data)

print("填充后的数据:\n", filled_data)

输出结果

填充后的数据:
 [[25.   155. ]
 [30.   150. ]
 [25.   160. ]]

这里,年龄的均值为 25,因此缺失值被替换为 25。


特征编码:将“文字”转化为“数字”

机器学习模型无法直接处理分类变量(如“性别”或“颜色”)。OneHotEncoder 将离散特征转换为二进制向量,例如:

  • 原始数据['male', 'female', 'male']
  • 编码后[[1,0], [0,1], [1,0]]

代码实现

from sklearn.preprocessing import OneHotEncoder
import pandas as pd

df = pd.DataFrame({'Color': ['red', 'blue', 'red', 'green']})
encoder = OneHotEncoder(sparse_output=False)
encoded = encoder.fit_transform(df[['Color']])

print("编码后的特征矩阵:\n", encoded)

输出结果

编码后的特征矩阵:
 [[1. 0. 0.]
 [0. 1. 0.]
 [1. 0. 0.]
 [0. 0. 1.]]

每个列代表一个类别,1 表示该样本属于该类别。


特征选择:筛选“关键信息”

并非所有特征都对模型有帮助。VarianceThreshold 可通过方差阈值过滤低信息量的特征。例如,若某特征的值始终为 0,则其方差为 0,可直接剔除。

代码示例

from sklearn.feature_selection import VarianceThreshold
import numpy as np

data = np.array([[1, 2, 0],
                 [3, 4, 0],
                 [5, 6, 0]])
selector = VarianceThreshold(threshold=0.1)
selected_data = selector.fit_transform(data)

print("筛选后的数据:\n", selected_data)

输出结果

筛选后的数据:
 [[1 2]
 [3 4]
 [5 6]]

第三列因方差为 0 被移除。


数据归一化:让数值“缩放”到特定区间

与标准化不同,归一化(Min-Max Scaling)将数据缩放到 [0,1] 或其他指定范围。例如,图像数据常用 0-255 转换为 0-1:

公式
[ X_{\text{normalized}} = \frac{X - X_{\text{min}}}{X_{\text{max}} - X_{\text{min}}} ]

代码实现

from sklearn.preprocessing import MinMaxScaler

data = np.array([[100, 200], [150, 300], [200, 400]])
scaler = MinMaxScaler()
normalized = scaler.fit_transform(data)

print("归一化后的数据:\n", normalized)

输出结果

归一化后的数据:
 [[0.   0. ]
 [0.5  0.5]
 [1.   1. ]]

第一列的最小值为 100,最大值为 200,因此 150 归一化后为 0.5。


数据分箱:将连续值“离散化”

通过 KBinsDiscretizer 可将连续特征分箱,例如将年龄分为“青年、中年、老年”。

代码示例

from sklearn.preprocessing import KBinsDiscretizer
import numpy as np

ages = np.array([[25], [35], [45], [55], [65]])
discretizer = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform')
binned_ages = discretizer.fit_transform(ages)

print("分箱后的结果:\n", binned_ages)

输出结果

分箱后的结果:
 [[0.]
 [0.]
 [1.]
 [2.]
 [2.]]

年龄被均匀分为 3 段:0-35、35-45、45+。


数据变换:调整分布形状

若数据呈现偏态分布(如收入数据),可使用 PowerTransformer 进行 Box-Cox 变换,使其接近正态分布。

案例:处理右偏数据

from sklearn.preprocessing import PowerTransformer
import numpy as np

np.random.seed(42)
data = np.random.exponential(scale=2, size=1000).reshape(-1, 1)
transformer = PowerTransformer(method='box-cox')
transformed = transformer.fit_transform(data)

效果对比
原始数据的偏度为 2.1,变换后偏度降至 0.1,分布更对称。


综合案例:从数据加载到预处理

以下代码演示如何整合标准化、缺失值填充和编码:

import pandas as pd
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer

df = pd.DataFrame({
    'sepal_length': [5.1, 4.9, 6.0],
    'species': ['setosa', 'setosa', 'versicolor']
})

numeric_features = ['sepal_length']
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

categorical_features = ['species']
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))
])

preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)
    ]
)

processed_data = preprocessor.fit_transform(df)
print("预处理后的数据形状:", processed_data.shape)

Sklearn 数据预处理模块提供了从基础到高级的工具,帮助开发者系统化处理数据中的噪声、缺失和非数值特征。通过本文的案例和代码示例,读者可以逐步掌握标准化、编码、分箱等核心技能。记住,预处理并非一成不变的流程——需根据数据特性灵活选择方法。例如,分类问题可能需要 OneHotEncoder,而回归问题可能更依赖标准化。建议读者在实际项目中结合交叉验证,探索最适合的预处理策略。

机器学习的道路上,Sklearn 数据预处理是连接原始数据与模型的桥梁。掌握它,你就能让数据“开口说话”,为后续的模型训练奠定坚实基础。

最新发布