登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

scikit-learn ColumnTransformer 怎样让数值与类别特征分别预处理

来源:17golang原创

时间:2026-10-29 01:40:48 459浏览 收藏

表格数据同时包含年龄、金额这样的数值列,以及城市、套餐这类类别列时,最稳妥的做法不是先在 pandas 里手工改完整张表,而是让 ColumnTransformer 按列分流:数值列走缺失值填补和标准化,类别列走缺失值填补和独热编码,最后交给同一个 Pipeline。这样训练和预测使用的是同一套规则,也不会把测试集的信息提前带进训练过程。

要点速览
  • 同一组列上的连续操作要嵌套在 Pipeline,不同列组之间再交给 ColumnTransformer 并行组合。
  • 类别编码建议使用 handle_unknown="ignore",避免预测数据出现新类别时直接报错。
  • 列清单必须互不重叠;未列出的列用 remainder="drop" 或 passthrough 明确处理。

官方地址:https://scikit-learn.org/stable/auto_examples/compose/plot_column_transformer_mixed_types.html

先把输入列按语义分成两组

先不要急着写转换器。假设 DataFrame 中有 age、income、city、plan 四个特征,前两列是数值,后两列是类别。列分组的关键是“一个字段只进入它应该进入的分支”,不要让同一列同时出现在数值和类别列表里。

字段组典型处理需要注意
数值列中位数填补、StandardScaler填补策略只在训练集拟合
类别列众数填补、OneHotEncoder新类别用 handle_unknown 忽略
未列出的字段drop 或 passthrough不要依赖默认行为猜结果
scikit-learn ColumnTransformer 将年龄收入数值列与城市套餐类别列分流到不同预处理器的结构说明图
图1:结构说明图,展示数值列与类别列进入不同预处理分支的关系;这是原创静态说明图,不是运行截图。

让每一组列先完成自己的处理链

ColumnTransformer 负责“哪些列交给哪个转换器”,并不负责同一列上的先后顺序。因此数值列的填补和标准化要放进一个内部 Pipeline,类别列的填补和独热编码也要放进另一个内部 Pipeline。

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

# 数值列先填补缺失值,再按训练集统计量做标准化
numeric_pipeline = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])

# 类别列先填补缺失值;预测时出现新类别就忽略该列编码
categorical_pipeline = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("encoder", OneHotEncoder(handle_unknown="ignore")),
])

# 两条分支处理不同列,未列出的字段暂时丢弃
preprocessor = ColumnTransformer(
    transformers=[
        ("numeric", numeric_pipeline, ["age", "income"]),
        ("categorical", categorical_pipeline, ["city", "plan"]),
    ],
    remainder="drop",
)

这里的 most_frequent 只是一个示例,类别缺失也可以使用固定占位值;选择哪一种取决于业务是否把“缺失”视为独立状态。重要的是,填补器和标准化器都要作为 Pipeline 的一部分,在训练阶段拟合,而不是先用整张数据表计算均值或众数。

把预处理和模型绑定到同一个 Pipeline

实际训练时,把上面的 preprocessor 与模型串起来。调用 fit 时,预处理只从训练数据学习;调用 predict 时,测试集或线上新数据只执行已经学到的转换规则。

from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline

# 预处理和模型绑定,避免手工转换训练集与预测集产生分叉
model = Pipeline([
    ("preprocessor", preprocessor),
    ("classifier", LogisticRegression(max_iter=1000)),
])

# X_train 是特征表,y_train 是目标列;示例不把目标列送入转换器
model.fit(X_train, y_train)
predicted = model.predict(X_test)

# 输出展开后的特征名,便于排查列遗漏和编码结果
feature_names = model.named_steps["preprocessor"].get_feature_names_out()

如果需要保留原始字段,可以把 remainder 改成 "passthrough",但要确认透传列没有字符串或未处理的缺失值。对特征名的检查也很有价值:编码后的类别列通常会展开成多个带前缀的特征,看到它们能快速判断分支是否接对。

scikit-learn ColumnTransformer 输出数值标准化列和类别独热编码列的特征关系说明图
图2:结果说明图,展示两条分支汇合后的输出特征与编码前字段的对应关系;这是原创静态说明图,不是运行证据。

常见问题与边界

为什么同一列不能连续写两个转换器?

因为 ColumnTransformer 的各个转换器针对列组并行取数。需要连续处理时,把多个步骤放进同一个内部 Pipeline。

预测集出现训练时没有的类别怎么办?

独热编码使用 handle_unknown="ignore" 时,新类别会对应全零编码,不会因为类别字典变化而中断预测;但这不代表新类别一定具有业务意义,仍要观察数据质量。

如何确认没有把目标列泄漏进去?

让 X 只包含特征列,y 单独传给 fit,并检查转换器的列清单。不要在预处理器中使用目标列做填补、编码或筛选。

我在这类表格模型里更愿意保留显式列名和一个完整 Pipeline。它比“先手工改表、再把转换后的数组交给模型”多几行代码,却把训练、评估和线上预测的边界固定下来了;当字段增减或类别发生变化时,也更容易从 get_feature_names_out() 和列清单找到问题。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>