机器学习特征预处理之数值缺失填充
针对数值列
示例
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(...)
-
方法链调用:先
fit再transform -
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顺序变化,也会正确匹配到对应的列。
更多推荐



所有评论(0)