Pipelines de Scikit-Learn en 2026: Feature Engineering y Preguntas de Entrevista

Domina los pipelines de scikit-learn para feature engineering y prepara tus entrevistas técnicas. Guía completa con ColumnTransformer, GridSearchCV y mejores prácticas de despliegue.

Pipelines de Scikit-Learn en 2026: Feature Engineering y Preguntas de Entrevista

Los pipelines de scikit-learn transforman código de preprocesamiento caótico en flujos de trabajo reproducibles y listos para producción. Con la versión 1.9 lanzada en junio de 2026, Pipeline y ColumnTransformer siguen siendo la base de cualquier proyecto serio de machine learning, ahora con monitoreo mediante callbacks, visualización HTML mejorada y soporte para Array API.

Punto Clave para Entrevistas

Los entrevistadores suelen preguntar: "¿Cómo se previene la fuga de datos durante la validación cruzada?" La respuesta es: pipelines. Ajustar los pasos de preprocesamiento dentro del bucle de CV garantiza que los datos de prueba nunca influyan en las decisiones de escalado o codificación.

Por Qué los Pipelines Eliminan la Fuga de Datos

La fuga de datos ocurre cuando información del conjunto de prueba influye en el preprocesamiento. Un error común: ajustar un StandardScaler en todo el dataset antes de dividirlo. El scaler aprende la media y varianza de las muestras de prueba, inflando artificialmente los scores de validación cruzada.

Los pipelines corrigen esto encadenando transformers y estimadores en un único objeto que ajusta todos los pasos juntos:

python
# pipeline_basics.py
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score

# Create a pipeline that scales features and trains a classifier
pipeline = Pipeline([
    ('scaler', StandardScaler()),  # Step 1: Normalize features
    ('classifier', LogisticRegression(max_iter=1000))  # Step 2: Train model
])

# cross_val_score fits the scaler separately for each fold
# No data leakage: test fold is never seen during scaler.fit()
scores = cross_val_score(pipeline, X, y, cv=5, scoring='accuracy')
print(f"Mean accuracy: {scores.mean():.3f} (+/- {scores.std():.3f})")

El objeto Pipeline implementa fit, predict y score, haciéndolo intercambiable con cualquier estimador de scikit-learn. Esto significa que GridSearchCV, RandomizedSearchCV y todas las utilidades de validación cruzada funcionan sin modificaciones.

ColumnTransformer para Tipos de Datos Mixtos

Los datasets reales contienen columnas numéricas (edad, salario) y columnas categóricas (país, tipo_producto). ColumnTransformer aplica diferentes preprocesamientos a diferentes subconjuntos de columnas y luego concatena los resultados:

python
# column_transformer_example.py
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
import pandas as pd

# Sample dataset with mixed types
df = pd.DataFrame({
    'age': [25, 30, None, 45],
    'salary': [50000, 60000, 75000, None],
    'country': ['US', 'UK', 'DE', 'US'],
    'education': ['Bachelor', 'Master', 'PhD', 'Bachelor']
})

# Define numeric preprocessing: impute missing values, then scale
numeric_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# Define categorical preprocessing: impute with mode, then one-hot encode
categorical_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('encoder', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])

# Combine transformers with ColumnTransformer
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, ['age', 'salary']),
        ('cat', categorical_transformer, ['country', 'education'])
    ],
    verbose_feature_names_out=True  # Prefix output names with transformer name
)

# Fit and inspect output shape
X_transformed = preprocessor.fit_transform(df)
print(f"Transformed shape: {X_transformed.shape}")
print(f"Feature names: {preprocessor.get_feature_names_out()}")

El parámetro verbose_feature_names_out (mejorado en la versión 1.6 para aceptar strings y callables) controla el nombramiento de las features de salida. Establecerlo en True prefija cada feature con el nombre del transformer, evitando colisiones de nombres cuando múltiples transformers generan nombres de columnas similares.

Automatizar la Selección de Columnas con make_column_selector

Codificar nombres de columnas de forma fija rompe el código cuando los datasets cambian. make_column_selector selecciona columnas automáticamente por dtype:

python
# automatic_column_selection.py
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.preprocessing import StandardScaler, OneHotEncoder

# Select columns by dtype: no hardcoded column names
preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), make_column_selector(dtype_include='number')),
        ('cat', OneHotEncoder(handle_unknown='ignore'),
         make_column_selector(dtype_include='object'))
    ],
    remainder='passthrough'  # Keep unprocessed columns as-is
)

# Works with any DataFrame that has numeric and object columns
X_processed = preprocessor.fit_transform(df)

El parámetro remainder controla qué sucede con las columnas no seleccionadas por ningún transformer. Las opciones incluyen 'drop' (por defecto), 'passthrough' (mantener sin cambios), o un transformer para aplicar a las columnas restantes.

¿Listo para aprobar tus entrevistas de Data Science & ML?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Feature Engineering Dentro de los Pipelines

Los pipelines se extienden más allá del preprocesamiento para incluir pasos de feature engineering. Los transformers personalizados heredan de BaseEstimator y TransformerMixin:

python
# custom_transformer.py
from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np

class DateFeatureExtractor(BaseEstimator, TransformerMixin):
    """Extract year, month, day, and weekday from datetime columns."""
    
    def __init__(self, date_column='date'):
        self.date_column = date_column
    
    def fit(self, X, y=None):
        # No fitting required for date extraction
        return self
    
    def transform(self, X):
        X = X.copy()
        dates = pd.to_datetime(X[self.date_column])
        
        # Extract temporal features
        X['year'] = dates.dt.year
        X['month'] = dates.dt.month
        X['day'] = dates.dt.day
        X['weekday'] = dates.dt.weekday
        X['is_weekend'] = (dates.dt.weekday >= 5).astype(int)
        
        # Drop original date column
        return X.drop(columns=[self.date_column])
    
    def get_feature_names_out(self, input_features=None):
        # Required for Pipeline.get_feature_names_out() to work
        return ['year', 'month', 'day', 'weekday', 'is_weekend']

# Integrate into a full pipeline
full_pipeline = Pipeline([
    ('date_features', DateFeatureExtractor(date_column='signup_date')),
    ('preprocessor', preprocessor),
    ('model', LogisticRegression())
])

Implementar get_feature_names_out permite que la nueva representación HTML de scikit-learn 1.9 muestre los nombres de las features de salida, facilitando la depuración y documentación.

Optimización de Hiperparámetros a Través de los Pasos del Pipeline

GridSearchCV ajusta parámetros a través de todos los pasos del pipeline usando la convención de nomenclatura step__parameter:

python
# pipeline_gridsearch.py
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

# Build pipeline with tunable components
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier(random_state=42))
])

# Define parameter grid: note the double underscore syntax
param_grid = {
    'preprocessor__num__imputer__strategy': ['mean', 'median'],
    'classifier__n_estimators': [100, 200],
    'classifier__max_depth': [10, 20, None]
}

# GridSearchCV handles all combinations automatically
grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,
    scoring='f1_weighted',
    n_jobs=-1
)

grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_:.3f}")

El doble guion bajo (__) accede a parámetros anidados. Para ColumnTransformer, la ruta incluye el nombre del transformer: preprocessor__num__imputer__strategy alcanza el parámetro strategy del imputer dentro del transformer numérico.

Caché de Transformers con el Parámetro memory

El preprocesamiento complejo en datasets grandes puede ser lento. El parámetro memory cachea los transformers ajustados en disco:

python
# cached_pipeline.py
from sklearn.pipeline import Pipeline
from tempfile import mkdtemp
import joblib

# Create a cache directory
cachedir = mkdtemp()

# Pipeline caches intermediate steps
cached_pipeline = Pipeline(
    [
        ('preprocessor', preprocessor),  # Cached after first fit
        ('classifier', RandomForestClassifier())
    ],
    memory=cachedir  # Or use joblib.Memory for more control
)

# First fit: slow (computes all transformations)
cached_pipeline.fit(X_train, y_train)

# Second fit with same X_train: fast (loads cached transformers)
cached_pipeline.fit(X_train, y_train)  # Skips preprocessor fitting

El cacheo es particularmente útil durante la búsqueda de hiperparámetros, donde el mismo preprocesamiento se aplica a múltiples configuraciones de modelos. El estimador final (clasificador o regresor) nunca se cachea, solo los transformers intermedios.

Monitoreo del Entrenamiento con Callbacks (scikit-learn 1.9)

La API de callbacks en la versión 1.9 agrega monitoreo de progreso a los pipelines:

python
# callback_monitoring.py
from sklearn.callback import ProgressBar, ScoringMonitor
from sklearn.linear_model import LogisticRegression

# Display progress during GridSearchCV
with ProgressBar():
    grid_search = GridSearchCV(
        pipeline,
        param_grid,
        cv=5,
        n_jobs=1  # Progress bars require single-threaded execution
    )
    grid_search.fit(X_train, y_train)

# Monitor scoring metrics per iteration
with ScoringMonitor(X_val, y_val, scoring='accuracy') as monitor:
    logreg = LogisticRegression(solver='lbfgs', max_iter=500)
    logreg.fit(X_train, y_train)
    print(f"Scores per iteration: {monitor.scores_}")

Los callbacks funcionan con Pipeline, StandardScaler, LogisticRegression (solver LBFGS) y todas las clases de búsqueda CV. Esta característica experimental proporciona visibilidad en ajustes de larga duración sin código de logging personalizado.

FeatureUnion para Extracción Paralela de Features

FeatureUnion ejecuta múltiples transformers en paralelo y concatena sus salidas horizontalmente:

python
# feature_union_example.py
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest, f_classif

# Combine PCA and univariate selection
feature_union = FeatureUnion([
    ('pca', PCA(n_components=5)),
    ('select', SelectKBest(f_classif, k=10))
])

# Pipeline: preprocess, combine features, classify
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('features', feature_union),
    ('classifier', LogisticRegression())
])

# Output contains 5 PCA components + 10 selected features = 15 features
pipeline.fit(X_train, y_train)

La versión 1.7.2 agregó validación para asegurar que todos los transformers retornen salidas 2D. Esto detecta errores temprano cuando un transformer personalizado accidentalmente retorna un array 1D.

Serialización y Despliegue

Los pipelines se serializan con joblib, preservando todos los parámetros ajustados:

python
# model_deployment.py
import joblib

# Save the entire pipeline (preprocessing + model)
joblib.dump(pipeline, 'churn_predictor.pkl')

# Load in production
loaded_pipeline = joblib.load('churn_predictor.pkl')

# Predict on new data (same preprocessing automatically applied)
new_data = pd.DataFrame({'age': [32], 'salary': [65000], ...})
predictions = loaded_pipeline.predict(new_data)

El archivo serializado contiene todo: lógica de selección de columnas, medias y varianzas ajustadas del scaler, categorías del encoder y pesos del modelo. El despliegue solo requiere scikit-learn y joblib, sin código de preprocesamiento personalizado.

Compatibilidad de Versiones

Los pipelines serializados con scikit-learn 1.8 pueden no cargarse correctamente en 1.9 si usan parámetros deprecados. Se recomienda siempre fijar la versión de scikit-learn en producción y probar las actualizaciones antes del despliegue.

Preguntas de Entrevista sobre Pipelines de Scikit-Learn

P: ¿Qué es la fuga de datos y cómo la previenen los pipelines?

La fuga de datos ocurre cuando información del conjunto de validación o prueba influye en el entrenamiento del modelo. El ejemplo clásico: ajustar un StandardScaler en todo el dataset antes de dividirlo. La media y desviación estándar del scaler incluyen muestras de prueba, haciendo que los scores de validación cruzada sean artificialmente altos.

Los pipelines previenen esto encapsulando el preprocesamiento dentro del bucle de validación cruzada. Cuando cross_val_score llama a pipeline.fit(), cada fold ajusta el scaler solo con datos de entrenamiento. Las estadísticas del fold de prueba nunca se filtran al preprocesamiento.

P: ¿Cómo se accede y modifican los parámetros de los pasos anidados del pipeline?

Se utiliza la sintaxis del doble guion bajo: pipeline.set_params(classifier__n_estimators=200). Para ColumnTransformer, se incluye el nombre del transformer: pipeline.set_params(preprocessor__num__scaler__with_mean=False). El método get_params() retorna todos los parámetros anidados como un diccionario plano, útil para inspección y logging.

P: ¿Cuándo usar FeatureUnion vs ColumnTransformer?

ColumnTransformer aplica diferentes transformers a diferentes columnas de la misma entrada. FeatureUnion aplica diferentes transformers a toda la entrada y concatena horizontalmente. Se debe usar ColumnTransformer para preprocesamiento de columnas heterogéneas (numéricas vs categóricas). FeatureUnion es preferible para aumentación de features, donde múltiples métodos de extracción (PCA, features polinomiales, extractores específicos del dominio) producen features complementarias de los mismos datos.

P: ¿Cómo depurar un pipeline que falla?

Establecer verbose=True en el pipeline permite ver los tiempos de ejecución de los pasos. Se puede acceder a resultados intermedios con pipeline.named_steps['step_name'].transform(X) para un pipeline ajustado. En scikit-learn 1.9, la representación HTML muestra atributos ajustados y nombres de features de salida, simplificando la depuración en notebooks Jupyter. Para inspección más profunda, usar pipeline[:-1].fit_transform(X, y) para obtener la salida justo antes del estimador final.

Checklist de Producción para Pipelines de ML

  • Fijar la versión de scikit-learn en requirements para evitar problemas de serialización
  • Usar make_column_selector en lugar de nombres de columnas codificados cuando los datasets pueden cambiar
  • Establecer handle_unknown='ignore' en OneHotEncoder para manejar nuevas categorías en producción
  • Cachear el preprocesamiento costoso con el parámetro memory durante el desarrollo
  • Validar la forma de salida del pipeline y nombres de features en tests antes del despliegue
  • Almacenar get_feature_names_out() junto al modelo para análisis de importancia de features
  • Usar Pipeline dentro de GridSearchCV, nunca al revés, para garantizar comportamiento CV correcto

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Reto diario

¿Sabrías detectar el bug en Data Science & ML?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 2 de septiembre de 2026

Compartir

Artículos relacionados