(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Cómo crear un Pipeline en scikit-learn: paso a paso

João Barros 13 de July de 2026 4 min de lectura

Crear un Pipeline en scikit-learn reúne el preprocesamiento y el modelo en un único objeto: lo que se aplica a los datos de entrenamiento se aplica automáticamente a los datos nuevos. Es la forma más sencilla de evitar el data leakage y de garantizar que lo que llega a producción es exactamente lo que validaste.

Requisitos previos

  • Python 3.10 o superior con scikit-learn y pandas instalados (pip install scikit-learn pandas).
  • Un fichero CSV con datos tabulares y una columna objetivo (aquí usamos clientes.csv y la columna churn).
  • Nociones básicas de entrenamiento y prueba de modelos.

Paso 1: Separar entrenamiento y prueba antes de todo

La regla de oro: el conjunto de prueba no puede influir en nada, ni siquiera en la media usada para escalar. Por eso, divide los datos antes de cualquier transformación.

import pandas as pd
from sklearn.model_selection import train_test_split

df = pd.read_csv("clientes.csv")
X = df.drop(columns=["churn"])
y = df["churn"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

Paso 2: Crear el Pipeline más simple

Un Pipeline es una lista de pasos con nombre. Todos los pasos, menos el último, deben transformar los datos; el último suele ser el modelo.

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

pipe = Pipeline(steps=[
    ("scaler", StandardScaler()),
    ("modelo", LogisticRegression(max_iter=1000)),
])

Los nombres ("scaler", "modelo") los eliges tú y serán útiles más adelante.

Paso 3: Entrenar y predecir en una sola línea

Al llamar a fit, el Pipeline aprende la escala con el entrenamiento y entrena el modelo. Al llamar a predict, aplica la misma escala a los datos nuevos sin que tengas que acordarte.

pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)

from sklearn.metrics import accuracy_score
print(accuracy_score(y_test, y_pred))

Paso 4: Tratar columnas numéricas y categóricas con ColumnTransformer

En la vida real las columnas no son todas iguales. ColumnTransformer aplica un camino distinto a cada grupo de columnas y encaja dentro del Pipeline como un paso más.

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer

numericas = ["idade", "mensalidade", "meses_cliente"]
categoricas = ["plano", "regiao"]

pre = ColumnTransformer(transformers=[
    ("num", Pipeline([
        ("imputer", SimpleImputer(strategy="median")),
        ("scaler", StandardScaler()),
    ]), numericas),
    ("cat", OneHotEncoder(handle_unknown="ignore"), categoricas),
])

pipe = Pipeline([
    ("pre", pre),
    ("modelo", LogisticRegression(max_iter=1000)),
])

pipe.fit(X_train, y_train)

handle_unknown="ignore" evita el error clásico de que aparezca en producción una categoría que no existía en el entrenamiento.

Paso 5: Validar y ajustar el Pipeline completo

Como el Pipeline es un estimador normal, puedes pasarlo directamente a la validación cruzada. En cada pliegue el preprocesamiento se reaprende solo con la parte de entrenamiento: eso es justamente lo que elimina el leakage.

from sklearn.model_selection import cross_val_score, GridSearchCV

scores = cross_val_score(pipe, X_train, y_train, cv=5, scoring="f1")
print(scores.mean())

grelha = {"modelo__C": [0.1, 1, 10]}
busca = GridSearchCV(pipe, grelha, cv=5, scoring="f1")
busca.fit(X_train, y_train)
print(busca.best_params_)

Fíjate en el doble guion bajo: modelo__C significa "el parámetro C del paso llamado modelo". Así se ajustan los hiperparámetros dentro de un Pipeline.

Paso 6: Guardar el Pipeline completo

Guarda el Pipeline, no solo el modelo. Así el fichero ya contiene el preprocesamiento y acepta datos en bruto.

import joblib

joblib.dump(busca.best_estimator_, "modelo_churn.joblib")

modelo = joblib.load("modelo_churn.joblib")
print(modelo.predict(X_test.head()))

Comprobar el resultado

Tres comprobaciones rápidas:

  • pipe.named_steps muestra los pasos en el orden correcto.
  • pipe.predict(X_test.head()) funciona con el DataFrame en bruto, sin escalar ni codificar nada a mano. Si funciona, el Pipeline está haciendo todo el trabajo.
  • Si aparece el error could not convert string to float, quedó una columna de texto fuera del ColumnTransformer; si aparece columns are missing, los datos nuevos no tienen las mismas columnas que el entrenamiento.
Compara el resultado de la validación cruzada con y sin Pipeline. Si el valor "mejora" cuando escalas todo antes de dividir, eso no es un modelo mejor: es leakage.

Conclusión

Con media docena de líneas pasaste de unos pasos sueltos a un único objeto validable y listo para servir. El siguiente paso natural es añadir al Pipeline una selección de variables o un TargetEncoder, y registrar el modelo en una plataforma de MLOps. Una pregunta para llevarte: si mañana llega una fila nueva con una categoría nunca vista, ¿tu Pipeline aguanta o se rompe?