针对数值列

示例

def numeric_imputation(X_train, X_test, num_cols, strategy='median'):
    """
    数值缺失填充
    """
    # 确保 num_cols 是列表
    if isinstance(num_cols, str):
        num_cols = [num_cols]

    # 只保留实际存在的数值列
    valid_cols = []
    for col in num_cols:
        if col in X_train.columns:
            # 检查是否为数值类型
            if pd.api.types.is_numeric_dtype(X_train[col]):
                valid_cols.append(col)
            else:
                print(f"  ⚠️ 跳过非数值列: '{col}' (类型: {X_train[col].dtype})")

    if not valid_cols:
        print("  ⚠️ 没有有效的数值列需要填充")
        return X_train, X_test

    try:
        imputer = SimpleImputer(strategy=strategy)

        # 使用 loc 避免链式索引
        X_train.loc[:, valid_cols] = imputer.fit_transform(X_train[valid_cols])
        X_test.loc[:, valid_cols] = imputer.transform(X_test[valid_cols])

        print(f"  ✅ 数值填充完成: {len(valid_cols)} 列")
        return X_train, X_test

    except Exception as e:
        print(f"  ❌ 数值填充失败: {e}")
        print(f"  问题列: {valid_cols}")
        print(f"  数据类型: {X_train[valid_cols].dtypes}")
        raise

解析重要步骤

imputer = SimpleImputer(strategy=strategy)
X_train.loc[:, valid_cols] = imputer.fit_transform(X_train[valid_cols])
X_test.loc[:, valid_cols] = imputer.transform(X_test[valid_cols])

第一行:创建填充器对象

imputer = SimpleImputer(strategy=strategy)

语法拆解:

  • SimpleImputer(...):调用类的构造函数,创建一个实例对象

  • strategy=strategy:关键字参数,指定填充策略

    • 'mean':均值填充

    • 'median':中位数填充

    • 'most_frequent':众数填充

    • 'constant':常数填充(需要配合fill_value参数)

此时对象状态:

  • 只是一个配置好的工具,还没有执行任何计算

  • 内部存储了strategy参数,等待被调用


第二行:训练并转换训练集

X_train.loc[:, valid_cols] = imputer.fit_transform(X_train[valid_cols])

这行包含多个语法点,从右向左拆解:

① X_train[valid_cols]

  • 语法:DataFrame的列索引

  • 含义:选取valid_cols列表中指定的所有列

  • 返回:一个新的DataFrame,只包含这些列

  • 示例:如果valid_cols=['age','salary'],返回只有这两列的DataFrame

② imputer.fit_transform(...)

  • 方法链调用:先fittransform

  • fit:计算填充值(如计算中位数)

    # 内部大致执行:
    imputer.statistics_ = [median(age), median(salary)]
  • transform:用计算好的填充值替换缺失值

    # 内部大致执行:
    filled_data = 原始数据,NaN被替换为对应的中位数
  • 返回值二维NumPy数组(不是DataFrame)

③ X_train.loc[:, valid_cols]

  • loc:基于标签的索引器

  • 语法loc[行选择, 列选择]

    • : → 选择所有行

    • valid_cols → 选择这些列

  • 作用:定位到训练集中这些列的位置,作为赋值目标

④ 赋值 =

  • 将右边的NumPy数组赋值给左边定位的位置

  • 关键点:虽然右边是NumPy数组,但Pandas会自动对齐列名,将数据填入对应位置

整个步骤示意图:

原始X_train (含NaN)
    ↓
取出valid_cols列 → 2D DataFrame (含NaN)
    ↓
fit_transform → NumPy数组 (NaN已被填充)
    ↓
赋值回X_train的对应列 → X_train更新

第三行:仅转换测试集

X_test.loc[:, valid_cols] = imputer.transform(X_test[valid_cols])

与第二行的区别:

操作 训练集 测试集
方法 fit_transform transform只有transform
填充值来源 从训练集计算 使用训练集计算好的值imputer.statistics_
是否重新计算 (避免数据泄露)

transform执行过程:

# 内部使用之前fit时保存的统计量
filled_data = 测试集数据,NaN被替换为训练集的中位数

为什么用loc而不用直接赋值?

❌ 不推荐写法:

X_train[valid_cols] = imputer.fit_transform(X_train[valid_cols])
  • 可能触发SettingWithCopyWarning

  • 在某些情况下Pandas无法确定是在修改原DataFrame还是副本

✅ 推荐写法(loc):

X_train.loc[:, valid_cols] = imputer.fit_transform(X_train[valid_cols])
  • 明确指定:要修改的是原DataFrame的这些位置

  • 避免歧义:Pandas知道这是原地修改

  • 性能更好:减少不必要的拷贝

完整执行示例

假设数据如下:

# 原始数据
X_train = pd.DataFrame({
    'age': [25, 30, None, 28],
    'salary': [5000, None, 7000, 6000]
})
valid_cols = ['age', 'salary']
strategy = 'median'

执行过程:

# 1. 创建imputer
imputer = SimpleImputer(strategy='median')

# 2. 训练集
# fit: 计算中位数 → age中位数=28, salary中位数=6000
# transform: 填充 → [[25,5000], [30,6000], [28,7000], [28,6000]]
X_train.loc[:, valid_cols] = 上述数组

# 3. 测试集(假设有缺失)
X_test = pd.DataFrame({
    'age': [None, 35],
    'salary': [8000, None]
})
# transform: 使用训练集中位数填充 → [[28,8000], [35,6000]]
X_test.loc[:, valid_cols] = 上述数组

全部过程

import pandas as pd
from sklearn.impute import  SimpleImputer

# 原始数据
X_train = pd.DataFrame({
    'age': [25, 30, None, 28],
    'salary': [5000, None, 7000, 6000]
})
valid_cols = ['age', 'salary']
strategy = 'median'



# 1. 创建imputer
imputer = SimpleImputer(strategy='median')

# 2. 训练集
# fit: 计算中位数 → age中位数=28, salary中位数=6000
# transform: 填充 → [[25,5000], [30,6000], [28,7000], [28,6000]]
X_train.loc[:, valid_cols] =imputer.fit_transform(X_train[valid_cols])

# 3. 测试集(假设有缺失)
X_test = pd.DataFrame({
    'age': [None, 35],
    'salary': [8000, None]
})
# transform: 使用训练集中位数填充 → [[28,8000], [35,6000]]
X_test.loc[:, valid_cols] = imputer.transform(X_test[valid_cols])

print(X_train)
print(X_test)

常见问题

Q1:为什么填充测试集不用fit

:如果测试集也用fit,它会用自己的数据计算填充值,导致:

  • 训练集和测试集使用不同的填充标准

  • 模型评估不准确(测试集"偷看"了自己的分布)

Q2:fit_transform返回的是DataFrame吗?

:不是,返回的是NumPy数组。Pandas在赋值时会自动处理类型转换。

Q3:如果列顺序变化怎么办?

loc是基于列名定位的,所以即使valid_cols顺序变化,也会正确匹配到对应的列。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐