Scikit-learn 缺失值处理:SimpleImputer 完整指南
📅 2026/7/31 22:11:58
👁️ 阅读次数
📝 编程学习
一、为什么需要缺失值处理
真实数据集普遍存在缺失值(NaN)。缺失值会导致:
- 机器学习模型训练报错(大部分 estimator 不接受 NaN)
- 统计分析结果偏差(缺失值不参与计算)
- 数据质量下降(影响决策准确性)
处理策略对比:
| 策略 | 适用类型 | 优点 | 缺点 |
|---|---|---|---|
| 删除法 | 任意 | 简单直接 | 丢失数据量,小数据集不可用 |
| 均值/中位数填充 | 数值型 | 保留样本量 | 引入偏差,扭曲分布 |
| 众数填充 | 类别型 | 保留样本量 | 可能加剧类别不平衡 |
| 常数填充 | 任意 | 灵活可控 | 需人工选择填充值 |
| 模型预测填充 | 任意 | 最精确 | 计算成本高,实现复杂 |
SimpleImputer覆盖了均值、中位数、众数、常数四种策略,是工程实践中的首选方案。
二、数据准备
import pandas as pd import numpy as np # 构造含缺失值的测试数据集 data = { 'size': ['XL', 'L', 'M', np.nan, 'M', 'M'], # 类别型,有缺失 'color': ['red', 'green', 'blue', 'green', 'red', 'green'], 'gender': ['female', 'male', np.nan, 'female', 'female', 'male'], # 类别型,有缺失 'price': [199.0, 89.0, np.nan, 129.0, 79.0, 89.0], # 数值型,有缺失 'weight': [500, 450, 300, np.nan, 410, np.nan], # 数值型,有缺失 'bought': ['yes', 'no', 'yes', 'no', 'yes', 'no'] } df = pd.DataFrame(data)数据概览:
| 索引 | size | color | gender | price | weight | bought |
|---|---|---|---|---|---|---|
| 0 | XL | red | female | 199.0 | 500.0 | yes |
| 1 | L | green | male | 89.0 | 450.0 | no |
| 2 | M | blue | NaN | NaN | 300.0 | yes |
| 3 | NaN | green | female | 129.0 | NaN | no |
| 4 | M | red | female | 79.0 | 410.0 | yes |
| 5 | M | green | male | 89.0 | NaN | no |
缺失值检测:
# 统计每列缺失值数量 df.isnull().sum() # size 1 # color 0 # gender 1 # price 1 # weight 2 # bought 0三、四种填充策略详解
策略 1:均值填充(mean)
适用场景:数值型特征,数据分布近似正态。
from sklearn.impute import SimpleImputer 创建填充器:strategy='mean' 使用列均值填充 imputer = SimpleImputer(missing_values=np.nan, strategy='mean') fit_transform 两步合一: fit() 计算 weight 列均值 = (500+450+300+410)/4 = 415.0 transform() 用 415.0 填充缺失位置 df[["weight"]] = imputer.fit_transform(df[["weight"]])填充结果:第 3 行和第 5 行的 weight 被 415.0 填充。
查看填充统计值:
imputer.statistics_ # array([415.]) imputer.statistics_[0] array([415.]) imputer.statistics_[0] np.float64(415.0)关键点:
- 输入必须是二维结构
df[["weight"]],不能是df["weight"](一维 Series) - 均值仅基于非缺失值计算
- 如果列全为 NaN,fit_transform 会报错
策略 2:常数填充(constant)
适用场景:需要用特定业务含义值填充时,如用 0 表示"未测量"、用 "unknown" 表示"未知"。
# strategy='constant' + fill_value 指定填充值 imputer = SimpleImputer( missing_values=np.nan, strategy='constant', fill_value=99.0 # 用 99.0 填充 price 列缺失值 ) df[["price"]] = imputer.fit_transform(df[["price"]])填充结果:第 2 行 price 被 99.0 填充。
imputer.statistics_ # array([99.0], dtype=object)关键点:
fill_value可以是任意类型(数值、字符串)- 数值型用数值填充,类别型用字符串填充
statistics_返回dtype=object,与均值策略的float64不同
策略 3:众数填充(most_frequent)
适用场景:类别型特征,或数值型离散值。
# strategy='most_frequent' 使用列中出现次数最多的值 imputer = SimpleImputer(missing_values=np.nan, strategy='most_frequent') size 列中 'M' 出现 3 次(最多),NaN 被替换为 'M' df[["size"]] = imputer.fit_transform(df[["size"]])填充结果:第 3 行 size 被 'M' 填充。
imputer.statistics_ # array(['M'], dtype=object)关键点:
- 如果有多个值出现次数相同,选第一个
- 适用于类别型,也可用于数值型离散值
- 可能加剧类别不平衡(已有 'M' 3 个,填充后变 4 个)
策略 4:批量填充字符串列
适用场景:一次性填充所有类别型列。
# 重建数据集 df = pd.DataFrame(data) 创建常量填充器,用 "empty" 填充 imputer = SimpleImputer( missing_values=np.nan, strategy='constant', fill_value="empty" ) 自动筛选所有 object 类型的列 columns = df.select_dtypes(include=['object']).columns 选中: size, color, gender, bought 批量填充 df.loc[:, columns] = imputer.fit_transform(df[columns])填充结果:size 和 gender 列的 NaN 被替换为 "empty"。
| 索引 | size | color | gender | price | weight | bought |
|---|---|---|---|---|---|---|
| 0 | XL | red | female | 199.0 | 500.0 | yes |
| 1 | L | green | male | 89.0 | 450.0 | no |
| 2 | M | blue | empty | NaN | 300.0 | yes |
| 3 | empty | green | female | 129.0 | NaN | no |
| 4 | M | red | female | 79.0 | 410.0 | yes |
| 5 | M | green | male | 89.0 | NaN | no |
关键点:
select_dtypes(include=['object'])选中所有字符串列df.loc[:, columns]确保 inplace 赋值不触发 SettingWithCopyWarning- 一次 fit_transform 处理多列,各列用同一个 fill_value
四、进阶技巧
技巧 1:按非缺失子集计算统计量
场景:需要基于某列的非缺失值计算其他列的统计量。
df = pd.DataFrame(data) 筛选 weight 非缺失的行,计算数值列均值 df[~df["weight"].isnull()].select_dtypes(include=['float']).mean() price 122.333333 weight 415.000000拆解说明:
# 步骤 1: df["weight"].isnull() → 布尔序列,True 表示缺失 # [False, False, False, True, False, True] 步骤 2: ~ 取反,True 变 False,筛选非缺失行 [True, True, True, False, True, False] 步骤 3: select_dtypes(include=['float']) 选中 price 和 weight 列 步骤 4: .mean() 计算均值 price = (199+89+129+79+89)/5 = 122.33(注意 price 第2行也缺失,被排除) weight = (500+450+300+410)/4 = 415.0技巧 2:fit 与 transform 分离
场景:训练集 fit,测试集用训练集的统计量 transform,避免数据泄露。
from sklearn.model_selection import train_test_split # 划分训练集和测试集 train_df, test_df = train_test_split(df, test_size=0.3, random_state=42) 训练集 fit:计算均值 imputer = SimpleImputer(strategy='mean') imputer.fit(train_df[["weight"]]) 测试集 transform:用训练集均值填充 避免测试集信息泄露到训练过程 test_df["weight"] = imputer.transform(test_df[["weight"]])技巧 3:Pipeline 集成
场景:将缺失值填充与模型训练组合成流水线。
from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression 构建 Pipeline:先填充缺失值,再训练模型 pipeline = Pipeline([ ('imputer', SimpleImputer(strategy='mean')), # 第一步:均值填充 ('classifier', LogisticRegression()) # 第二步:训练模型 ]) 直接 fit/predict,填充过程自动执行 pipeline.fit(X_train, y_train) predictions = pipeline.predict(X_test)五、策略选型决策树
缺失值类型? ├── 数值型 │ ├── 分布近似正态 → 均值填充(mean) │ ├── 有离群值 → 中位数填充(median) │ └── 离散值/业务含义明确 → 常数填充(constant) └── 类别型 ├── 分布均匀 → 众数填充(most_frequent) ├── 需要区分缺失与有效值 → 常数填充(constant,如 "unknown") └── 缺失比例高(>50%)→ 考虑删除该列缺失比例阈值参考:
| 缺失比例 | 推荐策略 |
|---|---|
| < 5% | 均值/众数填充 |
| 5% - 30% | 模型预测填充或常数填充 |
| > 50% | 考虑删除该列或转为二值指示特征 |
六、常见陷阱
陷阱 1:一维输入报错
# ❌ 错误:传入一维 Series imputer.fit_transform(df["weight"]) # ValueError: Expected 2D array, got 1D array instead ✅ 正确:传入二维 DataFrame imputer.fit_transform(df[["weight"]])陷阱 2:fit_transform 混用导致数据泄露
# ❌ 错误:测试集重新 fit imputer.fit(test_df[["weight"]]) # 测试集信息泄露 # ✅ 正确:用训练集的统计量 imputer.fit(train_df[["weight"]]) imputer.transform(test_df[["weight"]])陷阱 3:众数填充加剧类别不平衡
# size 列原始分布:M=3, L=1, XL=1 # 众数填充后:M=4, L=1, XL=1 # 'M' 占比从 50% 升至 67%应对:缺失比例高时,改用常数填充并标记为独立类别。
陷阱 4:SettingWithCopyWarning
# ❌ 可能触发警告 df[columns] = imputer.fit_transform(df[columns]) ✅ 使用 loc 显式赋值 df.loc[:, columns] = imputer.fit_transform(df[columns])七、速查表
SimpleImputer 参数速查
| 参数 | 类型 | 可选值 | 默认值 | 说明 |
|---|---|---|---|---|
missing_values |
申明:本文主要内容来源于ant-exercises-sklearn: scikit-learn 编程练习 100例,是个人进行sklearn学习时的产物,如有不适、请告之。
编程学习
技术分享
实战经验