注意
跳至末尾以下载完整的示例代码,或在浏览器中通过 JupyterLite 或 Binder 运行此示例。
混合类型的列转换器#
本示例阐述了如何使用 ColumnTransformer 对不同的特征子集应用不同的预处理和特征提取流水线。这对于包含异构数据类型的数据集尤其方便,因为我们可能需要缩放数值特征并对类别特征进行独热编码(one-hot encode)。
在此示例中,数值数据在进行均值插补(mean-imputation)后进行标准缩放。类别数据通过 OneHotEncoder 进行独热编码,这会为缺失值创建一个新类别。我们通过使用卡方检验选择类别来进一步降低维度。
此外,我们展示了两种将列分发给特定预处理器的不同方法:按列名和按列数据类型。
最后,预处理流水线与一个简单的分类模型结合,使用 Pipeline 整合到一个完整的预测流水线中。
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.datasets import fetch_openml
from sklearn.feature_selection import SelectPercentile, chi2
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RandomizedSearchCV, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
np.random.seed(0)
从 https://www.openml.org/d/40945 加载数据
X, y = fetch_openml("titanic", version=1, as_frame=True, return_X_y=True)
# Alternatively X and y can be obtained directly from the frame attribute:
# X = titanic.frame.drop('survived', axis=1)
# y = titanic.frame['survived']
通过按名称选择列来使用 ColumnTransformer
我们将使用以下特征训练分类器
数值特征
age: float;fare: float。
类别特征
embarked: 编码为字符串的类别{'C', 'S', 'Q'};sex: 编码为字符串的类别{'female', 'male'};pclass: 顺序整数{1, 2, 3}。
我们为数值数据和类别数据创建预处理流水线。请注意,pclass 既可以作为类别特征处理,也可以作为数值特征处理。
numeric_features = ["age", "fare"]
numeric_transformer = Pipeline(
steps=[("imputer", SimpleImputer(strategy="median")), ("scaler", StandardScaler())]
)
categorical_features = ["embarked", "sex", "pclass"]
categorical_transformer = Pipeline(
steps=[
("encoder", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
("selector", SelectPercentile(chi2, percentile=50)),
]
)
preprocessor = ColumnTransformer(
transformers=[
("num", numeric_transformer, numeric_features),
("cat", categorical_transformer, categorical_features),
]
)
将分类器追加到预处理流水线。现在我们得到了一个完整的预测流水线。
clf = Pipeline(
steps=[("preprocessor", preprocessor), ("classifier", LogisticRegression())]
).set_output(transform="pandas")
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
clf.fit(X_train, y_train)
print("model score: %.3f" % clf.score(X_test, y_test))
model score: 0.798
Pipeline 的 HTML 表示(显示图表)
在 Jupyter Notebook 中打印 Pipeline 时,会显示该估算器的 HTML 表示形式
clf
Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='median')),
('scaler',
StandardScaler())]),
['age', 'fare']),
('cat',
Pipeline(steps=[('encoder',
OneHotEncoder(handle_unknown='ignore',
sparse_output=False)),
('selector',
SelectPercentile(percentile=50,
score_func=<function chi2 at 0x7841675187d0>))]),
['embarked', 'sex',
'pclass'])])),
('classifier', LogisticRegression())])在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示形式或信任 notebook。在 GitHub 上,HTML 表示形式无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
classes_ classes_: 形状为 (n_classes,) 的 ndarray 类别标签。仅在流水线的最后一步是分类器时存在。 |
ndarray[object](2,) | ['0','1'] |
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当下层评估器在拟合时公开了此类属性时才定义。 .. 新增版本:: 1.0 |
ndarray[object](13,) | ['pclass','name','sex',...,'boat','body','home.dest'] |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。仅当 `steps` 中的下层首个评估器在拟合时公开了此类属性时才定义。 .. 新增版本:: 0.24 |
int | 13 |
参数
拟合属性
['age', 'fare']
参数
拟合属性
2 个特征
| age |
| fare |
参数
拟合属性
2 个特征
| age |
| fare |
['embarked', 'sex', 'pclass']
参数
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
categories_ categories_: 数组列表 在拟合期间确定的每个特征的类别(按 X 中特征的顺序,且与 ``transform`` 的输出相对应)。这包括在 ``drop`` 中指定的类别(如果有)。 |
list | [array(['C', '... dtype=object), array(['femal... dtype=object), array([1, 2, 3])] |
|
drop_idx_ drop_idx_: 形状为 (n_features,) 的数组 - ``drop_idx_[i]`` 是 ``categories_[i]`` 中要为每个特征删除的类别的索引。 - 如果索引为 ``i`` 的特征不需要删除任何类别,则 ``drop_idx_[i] = None``,例如当 `drop='if_binary'` 且该特征不是二元特征时。 - 如果将保留所有转换后的特征,则 ``drop_idx_ = None``。 如果通过将 `min_frequency` 或 `max_categories` 设置为非默认值启用了罕见类别,且 `drop_idx[i]` 对应于一个罕见类别,则整个罕见类别将被删除。 .. 版本变更:: 0.23 增加了包含 `None` 值的可能性。 |
NoneType | None |
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当 `X` 具有全部为字符串的特征名称时才定义。 .. 新增版本:: 1.0 |
ndarray[object](3,) | ['embarked','sex','pclass'] |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。 .. 新增版本:: 1.0 |
int | 3 |
9 个特征
| embarked_C |
| embarked_Q |
| embarked_S |
| embarked_nan |
| sex_female |
| sex_male |
| pclass_1 |
| pclass_2 |
| pclass_3 |
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当 `X` 具有全部为字符串的特征名称时才定义。 .. 新增版本:: 1.0 |
ndarray[object](9,) | ['embarked_C','embarked_Q','embarked_S',...,'pclass_1','pclass_2', 'pclass_3'] |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。 .. 新增版本:: 0.24 |
int | 9 |
|
pvalues_ pvalues_: 形状为 (n_features,) 的类似数组 特征得分的 p 值,如果 `score_func` 仅返回得分,则为 None。 |
ndarray[float64](9,) | [0. ,0.67,0.01,...,0. ,0.05,0. ] |
|
scores_ scores_: 形状为 (n_features,) 的类似数组 特征的得分。 |
ndarray[float64](9,) | [27.8 , 0.18, 7.33,...,60.96, 4.02,42.24] |
4 个特征
| sex_female |
| sex_male |
| pclass_1 |
| pclass_3 |
6 个特征
| num__age |
| num__fare |
| cat__sex_female |
| cat__sex_male |
| cat__pclass_1 |
| cat__pclass_3 |
参数
拟合属性
通过按数据类型选择列来使用 ColumnTransformer
在处理清理过的数据集时,可以使用列的数据类型自动决定是将一列视为数值特征还是类别特征来进行预处理。sklearn.compose.make_column_selector 提供了这种可能性。首先,我们只选择一个列子集来简化我们的示例。
subset_feature = ["embarked", "sex", "pclass", "age", "fare"]
X_train, X_test = X_train[subset_feature], X_test[subset_feature]
然后,我们内省有关每个列数据类型的信息。
X_train.info()
<class 'pandas.DataFrame'>
Index: 1047 entries, 1118 to 684
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 embarked 1045 non-null category
1 sex 1047 non-null category
2 pclass 1047 non-null int64
3 age 841 non-null float64
4 fare 1046 non-null float64
dtypes: category(2), float64(2), int64(1)
memory usage: 34.8 KB
我们可以观察到,在使用 fetch_openml 加载数据时,embarked 和 sex 列被标记为了 category(类别)列。因此,我们可以使用此信息将类别列分发给 categorical_transformer,而将其余列分发给 numerical_transformer。
注意
在实际操作中,您必须自己处理列数据类型。如果您希望将某些列视为 category,则必须将它们转换为类别列。如果您使用的是 pandas,可以参阅其有关 Categorical data(分类数据)的文档。
from sklearn.compose import make_column_selector as selector
preprocessor = ColumnTransformer(
transformers=[
("num", numeric_transformer, selector(dtype_exclude="category")),
("cat", categorical_transformer, selector(dtype_include="category")),
]
)
clf = Pipeline(
steps=[("preprocessor", preprocessor), ("classifier", LogisticRegression())]
)
clf.fit(X_train, y_train)
print("model score: %.3f" % clf.score(X_test, y_test))
clf
model score: 0.798
Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='median')),
('scaler',
StandardScaler())]),
<sklearn.compose._column_transformer.make_column_selector object at 0x784116939fd0>),
('cat',
Pipeline(steps=[('encoder',
OneHotEncoder(handle_unknown='ignore',
sparse_output=False)),
('selector',
SelectPercentile(percentile=50,
score_func=<function chi2 at 0x7841675187d0>))]),
<sklearn.compose._column_transformer.make_column_selector object at 0x78411770d6d0>)])),
('classifier', LogisticRegression())])在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示形式或信任 notebook。在 GitHub 上,HTML 表示形式无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
classes_ classes_: 形状为 (n_classes,) 的 ndarray 类别标签。仅在流水线的最后一步是分类器时存在。 |
ndarray[object](2,) | ['0','1'] |
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当下层评估器在拟合时公开了此类属性时才定义。 .. 新增版本:: 1.0 |
ndarray[object](5,) | ['embarked','sex','pclass','age','fare'] |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。仅当 `steps` 中的下层首个评估器在拟合时公开了此类属性时才定义。 .. 新增版本:: 0.24 |
int | 5 |
参数
拟合属性
['pclass', 'age', 'fare']
参数
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当 `X` 具有全部为字符串的特征名称时才定义。 .. 新增版本:: 1.0 |
ndarray[object](3,) | ['pclass','age','fare'] |
|
indicator_ indicator_: :class:`~sklearn.impute.MissingIndicator` 用于为缺失值添加二进制指示器的指示器。 如果 `add_indicator=False` 则为 `None`。 |
NoneType | None |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。 .. 新增版本:: 0.24 |
int | 3 |
|
statistics_ statistics_: 形状为 (n_features,) 的数组 每个特征的插补填充值。 计算统计数据可能导致 `np.nan` 值。在 :meth:`transform` 期间,对应于 `np.nan` 统计数据的特征将被丢弃。 |
ndarray[float64](3,) | [ 3. ,28. ,14.45] |
3 个特征
| pclass |
| age |
| fare |
参数
拟合属性
3 个特征
| pclass |
| age |
| fare |
['embarked', 'sex']
参数
拟合属性
6 个特征
| embarked_C |
| embarked_Q |
| embarked_S |
| embarked_nan |
| sex_female |
| sex_male |
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当 `X` 具有全部为字符串的特征名称时才定义。 .. 新增版本:: 1.0 |
ndarray[object](6,) | ['embarked_C','embarked_Q','embarked_S','embarked_nan','sex_female', 'sex_male'] |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。 .. 新增版本:: 0.24 |
int | 6 |
|
pvalues_ pvalues_: 形状为 (n_features,) 的类似数组 特征得分的 p 值,如果 `score_func` 仅返回得分,则为 None。 |
ndarray[float64](6,) | [0. ,0.67,0.01,0.07,0. ,0. ] |
|
scores_ scores_: 形状为 (n_features,) 的类似数组 特征的得分。 |
ndarray[float64](6,) | [ 27.8 , 0.18, 7.33, 3.23,187.26,108.46] |
3 个特征
| embarked_C |
| sex_female |
| sex_male |
6 个特征
| num__pclass |
| num__age |
| num__fare |
| cat__embarked_C |
| cat__sex_female |
| cat__sex_male |
参数
拟合属性
所得得分与上一个流水线的得分不完全相同,因为基于 dtype 的选择器将 pclass 列视为了数值特征,而之前是视为了类别特征。
selector(dtype_exclude="category")(X_train)
['pclass', 'age', 'fare']
selector(dtype_include="category")(X_train)
['embarked', 'sex']
在网格搜索中使用预测流水线
网格搜索也可以在 ColumnTransformer 对象中定义的各种预处理步骤上,与分类器的超参数(作为 Pipeline 的一部分)一起进行。我们将使用 RandomizedSearchCV 同时搜索数值预处理的插补策略 and 逻辑回归的正则化参数。该超参数搜索会随机选择由 n_iter 配置的固定数量的参数设置。或者,也可以使用 GridSearchCV,但这样会评估参数空间的笛卡尔积。
param_grid = {
"preprocessor__num__imputer__strategy": ["mean", "median"],
"preprocessor__cat__selector__percentile": [10, 30, 50, 70],
"classifier__C": [0.1, 1.0, 10, 100],
}
search_cv = RandomizedSearchCV(clf, param_grid, n_iter=10, random_state=0)
search_cv
RandomizedSearchCV(estimator=Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='median')),
('scaler',
StandardScaler())]),
<sklearn.compose._column_transformer.make_column_selector object at 0x784116939fd0>),
('cat',
Pipeline(steps=[('encoder',
OneHotEncoder(handle_unknown='ignore',
spars...
score_func=<function chi2 at 0x7841675187d0>))]),
<sklearn.compose._column_transformer.make_column_selector object at 0x78411770d6d0>)])),
('classifier',
LogisticRegression())]),
param_distributions={'classifier__C': [0.1, 1.0, 10, 100],
'preprocessor__cat__selector__percentile': [10,
30,
50,
70],
'preprocessor__num__imputer__strategy': ['mean',
'median']},
random_state=0)在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示形式或信任 notebook。在 GitHub 上,HTML 表示形式无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
参数
['pclass', 'age', 'fare']
参数
参数
['embarked', 'sex']
参数
参数
调用 ‘fit’ 会触发交叉验证以寻找最佳超参数组合
search_cv.fit(X_train, y_train)
print("Best params:")
print(search_cv.best_params_)
search_cv
Best params:
{'preprocessor__num__imputer__strategy': 'mean', 'preprocessor__cat__selector__percentile': 30, 'classifier__C': 100}
RandomizedSearchCV(estimator=Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='median')),
('scaler',
StandardScaler())]),
<sklearn.compose._column_transformer.make_column_selector object at 0x784116939fd0>),
('cat',
Pipeline(steps=[('encoder',
OneHotEncoder(handle_unknown='ignore',
spars...
score_func=<function chi2 at 0x7841675187d0>))]),
<sklearn.compose._column_transformer.make_column_selector object at 0x78411770d6d0>)])),
('classifier',
LogisticRegression())]),
param_distributions={'classifier__C': [0.1, 1.0, 10, 100],
'preprocessor__cat__selector__percentile': [10,
30,
50,
70],
'preprocessor__num__imputer__strategy': ['mean',
'median']},
random_state=0)在 Jupyter 环境中,请重新运行此单元格以显示 HTML 表示形式或信任 notebook。在 GitHub 上,HTML 表示形式无法渲染,请尝试使用 nbviewer.org 加载此页面。
参数
拟合属性
参数
拟合属性
['pclass', 'age', 'fare']
参数
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当 `X` 具有全部为字符串的特征名称时才定义。 .. 新增版本:: 1.0 |
ndarray[object](3,) | ['pclass','age','fare'] |
|
indicator_ indicator_: :class:`~sklearn.impute.MissingIndicator` 用于为缺失值添加二进制指示器的指示器。 如果 `add_indicator=False` 则为 `None`。 |
NoneType | None |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。 .. 新增版本:: 0.24 |
int | 3 |
|
statistics_ statistics_: 形状为 (n_features,) 的数组 每个特征的插补填充值。 计算统计数据可能导致 `np.nan` 值。在 :meth:`transform` 期间,对应于 `np.nan` 统计数据的特征将被丢弃。 |
ndarray[float64](3,) | [ 2.3 ,30.05,33.74] |
3 个特征
| pclass |
| age |
| fare |
参数
拟合属性
3 个特征
| pclass |
| age |
| fare |
['embarked', 'sex']
参数
拟合属性
6 个特征
| embarked_C |
| embarked_Q |
| embarked_S |
| embarked_nan |
| sex_female |
| sex_male |
拟合属性
| 名称 | 类型 | 值 |
|---|---|---|
|
feature_names_in_ feature_names_in_: 形状为 (`n_features_in_`,) 的 ndarray 在 :term:`fit` 期间看到的特征名称。仅当 `X` 具有全部为字符串的特征名称时才定义。 .. 新增版本:: 1.0 |
ndarray[object](6,) | ['embarked_C','embarked_Q','embarked_S','embarked_nan','sex_female', 'sex_male'] |
|
n_features_in_ n_features_in_: int 在 :term:`fit` 期间看到的特征数量。 .. 新增版本:: 0.24 |
int | 6 |
|
pvalues_ pvalues_: 形状为 (n_features,) 的类似数组 特征得分的 p 值,如果 `score_func` 仅返回得分,则为 None。 |
ndarray[float64](6,) | [0. ,0.67,0.01,0.07,0. ,0. ] |
|
scores_ scores_: 形状为 (n_features,) 的类似数组 特征的得分。 |
ndarray[float64](6,) | [ 27.8 , 0.18, 7.33, 3.23,187.26,108.46] |
2 个特征
| sex_female |
| sex_male |
5 个特征
| num__pclass |
| num__age |
| num__fare |
| cat__sex_female |
| cat__sex_male |
参数
拟合属性
通过这些参数获得的内部交叉验证得分为
print(f"Internal CV score: {search_cv.best_score_:.3f}")
Internal CV score: 0.786
我们还可以将顶部的网格搜索结果以内省形式展示为 pandas DataFrame
import pandas as pd
cv_results = pd.DataFrame(search_cv.cv_results_)
cv_results = cv_results.sort_values("mean_test_score", ascending=False)
cv_results[
[
"mean_test_score",
"std_test_score",
"param_preprocessor__num__imputer__strategy",
"param_preprocessor__cat__selector__percentile",
"param_classifier__C",
]
].head(5)
| mean_test_score | std_test_score | param_preprocessor__num__imputer__strategy | param_preprocessor__cat__selector__percentile | param_classifier__C | |
|---|---|---|---|---|---|
| 7 | 0.786015 | 0.031020 | mean | 30 | 100.0 |
| 0 | 0.785063 | 0.030498 | median | 30 | 1.0 |
| 4 | 0.785063 | 0.030498 | mean | 10 | 10.0 |
| 2 | 0.785063 | 0.030498 | mean | 30 | 1.0 |
| 3 | 0.783149 | 0.030462 | mean | 30 | 0.1 |
最佳超参数已用于在完整训练集上重新拟合最终模型。我们可以在未用于超参数调优的留出测试数据上评估该最终模型。
print(
"accuracy of the best model from randomized search: "
f"{search_cv.score(X_test, y_test):.3f}"
)
accuracy of the best model from randomized search: 0.798
脚本总运行时间: (0 分 2.118 秒)
相关示例