Saltar al contenido

# machine-learning-clasico-con-python

Machine Learning con scikit-learn y XGBoost

Volver a la portada del curso
Notebook

Notebook: del CSV sucio a un Pipeline sin fuga de datos

Sección 2 · Preparación de datos y pipelines

Del CSV sucio a un Pipeline sin fuga de datos

Este notebook integra todo el módulo de preparación de datos. Parte del CSV sucio de PyRitmo y termina en un Pipeline que imputa, codifica, escala y entrena sin dejar que el test se cuele en ningún paso. Esta guía acompaña al notebook csv-sucio-a-pipeline: qué hace cada sección y qué cifra debes encontrar.

1. El CSV sucio

Con generar_canciones (semilla 42) y ensuciar (semilla 7) se obtienen 1525 filas: 25 repetidas, 86 faltantes en tempo_bpm y 60 en seguidores_miles, 17 escrituras distintas para 5 géneros y 19 duraciones que vinieron en milisegundos.

2. Limpieza que no aprende de los datos

Quitar duplicados, unificar "POP" y "pop " y dividir entre 1000 las duraciones imposibles son reglas fijas: no calculan nada a partir de los datos y se pueden aplicar antes de partir, igual que en la lectura de datos sucios. Quedan 1500 canciones, 5 géneros y los faltantes reales (85 y 59), que no se rellenan aquí.

3. La regla de oro: fit en train, transform en el resto

Todo lo que aprende algo de los datos (la mediana para imputar, la media y la desviación para escalar, las categorías del one-hot) se ajusta solo con train y después se aplica a test y a las canciones futuras. La forma de cumplirlo sin pensar en cada paso es ponerlos dentro de un pipeline.

Para imputar las numéricas se usa la mediana: en seguidores_miles la media de train (231.9 mil) es 2.6 veces la mediana (88.2 mil) por culpa de unos pocos artistas enormes. Para el género, la categoría más frecuente.

4. El objeto pre y el pipeline

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

numericas = Pipeline([("imputar", SimpleImputer(strategy="median")),
                      ("escalar", StandardScaler())])
categoricas = Pipeline([("imputar", SimpleImputer(strategy="most_frequent")),
                        ("onehot", OneHotEncoder(handle_unknown="ignore", sparse_output=False))])
pre = ColumnTransformer([("num", numericas, NUMERICAS), ("cat", categoricas, CATEGORICAS)])
pipe = Pipeline([("pre", pre), ("modelo", LogisticRegression(max_iter=2000))])
pipe.fit(X_tr, y_tr)

Qué buscar: accuracy en test de 0.808 contra 0.725 del Dummy, y AUC de 0.847. El test tenía faltantes y el pipeline los rellenó con las medianas aprendidas en train (103 bpm y 88.2 mil seguidores), que se leen con named_steps. handle_unknown="ignore" evita que un género nuevo rompa la predicción.

5. La fuga, medida

Una celda imputa y escala con todo el dataset antes de partir y compara contra el pipeline correcto: AUC 0.8476 contra 0.8474. Aquí la diferencia es mínima, porque con 1500 canciones la mediana y la desviación casi no cambian por incluir el test. El procedimiento sigue siendo incorrecto: en producción no tendrás el test para calcular nada, y la cifra reportada debe salir del mismo proceso que usarás después.

La fuga se vuelve grave cuando el paso que se ajusta con todo aprende de y. El notebook lo muestra con 1000 columnas de puro ruido: elegir las 20 «mejores» con SelectKBest usando todo el dataset da un AUC de validación cruzada de 0.688, como si el ruido predijera el Top 50; con la selección dentro del pipeline da 0.54, cerca del azar, que es la verdad.

6. Nombres de columnas y salida en pandas

get_feature_names_out() devuelve las 14 columnas que recibe el modelo: 9 numéricas escaladas y 5 de género. Con set_output(transform="pandas"), transform devuelve un DataFrame con esos nombres, útil para revisar qué le llega al modelo. Este mismo pre se reutiliza en los notebooks siguientes.

7. Errores comunes

  • Llamar fit_transform sobre test: el escalador aprendería las medias del test.
  • Imputar o escalar antes de partir.
  • Elegir columnas con todo el dataset y después validar.
  • Usar la media para imputar columnas con atípicos fuertes.

8. Ejercicios

  1. Agregar SelectKBest(f_classif, k=8) entre pre y la logística: accuracy 0.808 (con 5 columnas baja a 0.803).
  2. Imputar las numéricas con la media: 0.811. Unas milésimas de diferencia son ruido en 375 canciones.
  3. Predecir una canción con género "cumbia", que no existía en train: sus 5 columnas de género quedan en cero y el modelo no se rompe.

Las soluciones están al final del notebook.

Recursos