Nota
Haz clic en aquí para descargar el código de ejemplo completo o para ejecutar este ejemplo en tu navegador a través de Binder
Transformador de columna con tipos mixtos¶
Este ejemplo ilustra cómo aplicar diferentes pipelines de preprocesamiento y extracción de características a diferentes subconjuntos de características, utilizando ColumnTransformer
. Esto es particularmente útil para el caso de conjuntos de datos que contienen tipos de datos heterogéneos, ya que podemos querer escalar las características numéricas y codificar de una sola vez las categóricas.
En este ejemplo, los datos numéricos se ajustan a la escala estándar después de la imputación de la media, mientras que los datos categóricos se codifican de una sola vez después de imputar los valores faltantes con una nueva categoría ('missing'
).
Además, mostramos dos formas diferentes de enviar las columnas al preprocesador particular: por nombres de columna y por tipos de datos de columna.
Por último, el pipeline de preprocesamiento se integra en un pipeline de predicción completo utilizando Pipeline
, junto con un modelo de clasificación simple.
# Author: Pedro Morales <part.morales@gmail.com>
#
# License: BSD 3 clause
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.datasets import fetch_openml
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split, GridSearchCV
np.random.seed(0)
# Load data from 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']
Usa ColumnTransformer
seleccionando columna por nombres¶
Entrenaremos a nuestro clasificador con las siguientes características:
Características numéricas:
age
: float;
fare
: float.Características categóricas:
embarked
: categorías codificadas como cadenas{'C', 'S', 'Q'}
;
sex
: categorías codificadas como cadenas{'female', 'male'}
;
pclass
: enteros ordinales{1, 2, 3}
.Creamos pipelines de preprocesamiento para datos numéricos y categóricos. Tenga en cuenta que
pclass
puede ser tratada como una característica categórica o numérica.
numeric_features = ['age', 'fare']
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())])
categorical_features = ['embarked', 'sex', 'pclass']
categorical_transformer = OneHotEncoder(handle_unknown='ignore')
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)])
# Append classifier to preprocessing pipeline.
# Now we have a full prediction pipeline.
clf = Pipeline(steps=[('preprocessor', preprocessor),
('classifier', LogisticRegression())])
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))
Out:
model score: 0.790
Representación HTML de Pipeline
¶
Cuando el
Pipeline
se imprime en un Cuaderno de Jupyter se muestra una representación HTML del estimador como la siguiente:
from sklearn import set_config
set_config(display='diagram')
clf
Utiliza ColumnTransformer
seleccionando la columna por tipos de datos¶
Cuando se trata de un conjunto de datos limpio, el preprocesamiento puede ser automático utilizando los tipos de datos de la columna para decidir si se trata una columna como característica numérica o categórica.
sklearn.compose.make_column_selector
ofrece esta posibilidad. Primero, vamos a seleccionar sólo un subconjunto de columnas para simplificar nuestro ejemplo.
subset_feature = ['embarked', 'sex', 'pclass', 'age', 'fare']
X_train, X_test = X_train[subset_feature], X_test[subset_feature]
A continuación, introspeccionamos la información relativa a cada tipo de dato de la columna.
X_train.info()
Out:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 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 float64
3 age 841 non-null float64
4 fare 1046 non-null float64
dtypes: category(2), float64(3)
memory usage: 35.0 KB
Podemos observar que las columnas embarked
y sex
fueron etiquetadas como columnas de categoría
al cargar los datos con fetch_openml
. Por tanto, podemos utilizar esta información para enviar las columnas categóricas al categorical_transformer
y el resto de columnas al numerical_transformer
.
Nota
En la práctica, deberás manejar tú mismo el tipo de datos de la columna. Si quieres que algunas columnas sean consideradas como categoría
, tendrás que convertirlas en columnas categóricas. Si utilizas pandas, puede consultar su documentación sobre Datos categóricos.
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))
Out:
model score: 0.794
La puntuación resultante no es exactamente la misma que la del pipeline anterior porque el selector basado en dtype trata las columnas pclass
como características numéricas en lugar de categóricas como anteriormente:
selector(dtype_exclude="category")(X_train)
Out:
['pclass', 'age', 'fare']
selector(dtype_include="category")(X_train)
Out:
['embarked', 'sex']
Uso del pipeline de predicción en una búsqueda en cuadrícula¶
La búsqueda en cuadrícula también se puede realizar en los diferentes pasos de preprocesamiento definidos en el objeto
ColumnTransformer
, junto con los hiperparámetros del clasificador como parte delPipeline
. Buscaremos tanto la estrategia de imputación del preprocesamiento numérico como el parámetro de regularización de la regresión logística utilizandoGridSearchCV
.
param_grid = {
'preprocessor__num__imputer__strategy': ['mean', 'median'],
'classifier__C': [0.1, 1.0, 10, 100],
}
grid_search = GridSearchCV(clf, param_grid, cv=10)
grid_search
Al llamar a “fit” se desencadena la búsqueda de validada cruzada de la mejor combinación de hiperparámetros:
grid_search.fit(X_train, y_train)
print(f"Best params:")
print(grid_search.best_params_)
Out:
Best params:
{'classifier__C': 0.1, 'preprocessor__num__imputer__strategy': 'mean'}
La puntuación de validación cruzada interna obtenida por esos parámetros es:
print(f"Internal CV score: {grid_search.best_score_:.3f}")
Out:
Internal CV score: 0.784
También podemos introspeccionar los resultados de la búsqueda en cuadrícula superior como un dataframe de pandas:
import pandas as pd
cv_results = pd.DataFrame(grid_search.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_classifier__C"
]].head(5)
Los mejores hiperparámetros se han utilizado para reajustar un modelo final en el conjunto de entrenamiento completo. Podemos evaluar ese modelo final en los datos de prueba retenidos que no se utilizaron para el ajuste de los hiperparámetros.
print(("best logistic regression from grid search: %.3f"
% grid_search.score(X_test, y_test)))
Out:
best logistic regression from grid search: 0.794
Tiempo total de ejecución del script: ( 0 minutos 2.496 segundos)