# introduccion-a-la-ciencia-de-datos-con-python
Introducción a la Ciencia de Datos con Python
Volver a la portada del cursoAnálisis predictivo con scikit-learn
Sección 4 · Modelado y Análisis Predictivo
Análisis predictivo con scikit-learn
Con solo el logaritmo del PIB per cápita, una regresión lineal explica el 73.6 % de la variación de la esperanza de vida en 2007 en datos que no vio al entrenar. ¿Es mucho o poco? ¿Mejora con más variables o con un modelo más flexible? ¿Y cómo saber si la cifra es confiable y no un golpe de suerte de la partición? El notebook modelado-predictivo responde esas preguntas paso a paso; esta lección es su guía.
1. La API común: fit, predict, score
Todos los estimadores de scikit-learn se usan igual: se crean con sus hiperparámetros, se entrenan con fit(X, y), predicen con predict(X) y se evalúan con score(X, y), que en regresión devuelve el $R^2$. X es una tabla de variables (aunque tenga una sola columna) e y el objetivo. El objetivo es lifeExp de 2007 y la variable principal es log_pib = log10(gdpPercap); con train_test_split(test_size=0.25, random_state=42) quedan 106 países para entrenar y 36 para probar.
2. Línea base y métricas
Antes de cualquier modelo se mide uno tonto: DummyRegressor predice siempre la media del entrenamiento. Todo modelo se compara contra él.
import numpy as np
import plotly.express as px
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, root_mean_squared_error, r2_score
gap = px.data.gapminder().drop(columns=["iso_alpha", "iso_num"])
gap["log_pib"] = np.log10(gap["gdpPercap"])
g07 = gap[gap["year"] == 2007]
X_tr, X_te, y_tr, y_te = train_test_split(g07[["log_pib"]], g07["lifeExp"],
test_size=0.25, random_state=42)
for m in (DummyRegressor(), LinearRegression()):
pred = m.fit(X_tr, y_tr).predict(X_te)
print(round(mean_absolute_error(y_te, pred), 2), round(root_mean_squared_error(y_te, pred), 2),
round(r2_score(y_te, pred), 3))
Tres métricas, tres lecturas:
- MAE, error absoluto medio, en años: la línea base se equivoca 10.65 años en promedio; la lineal, 4.61.
- RMSE, raíz del error cuadrático medio, también en años, pero castiga más los errores grandes: 12.41 contra 6.37.
- $R^2$, la fracción de la variación que el modelo explica: −0.001 contra 0.736.
$$R^2 = 1 - \frac{\sum (y_i - \hat{y}_i)^2}{\sum (y_i - \bar{y})^2}$$
Un $R^2$ de 0 equivale a predecir la media; puede ser negativo si el modelo es peor que eso. El coeficiente de la lineal es 16.21: cada vez que el PIB per cápita se multiplica por 10, la vida esperada sube unos 16 años.
3. Variables categóricas en un Pipeline
El continente es texto y un modelo lineal necesita números. La codificación one-hot crea una columna 0/1 por categoría; con drop="first" se quita una (Africa) para que sirva de referencia. Hacerlo dentro de un Pipeline con ColumnTransformer garantiza que la transformación se aprende solo con el entrenamiento y se aplica igual al probar:
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.pipeline import Pipeline
X = g07[["log_pib", "continent"]]
X_tr, X_te, y_tr, y_te = train_test_split(X, g07["lifeExp"], test_size=0.25, random_state=42)
prep = ColumnTransformer([("cont", OneHotEncoder(drop="first"), ["continent"])],
remainder="passthrough")
modelo = Pipeline([("prep", prep), ("lin", LinearRegression())])
modelo.fit(X_tr, y_tr)
print(round(modelo.score(X_te, y_te), 3)) # 0.802
El $R^2$ sube a 0.802 y el MAE baja a 4.0 años. El coeficiente de log_pib cae de 16.21 a 9.96: parte de lo que antes se atribuía a la riqueza era diferencia entre continentes. A igual PIB, un país de Europe tiene 12.03 años más que uno de Africa en este modelo. pd.get_dummies(..., drop_first=True) da el mismo resultado, pero fuera del pipeline es fácil aplicarlo distinto en entrenamiento y prueba.
4. Un modelo más flexible
Cambiar LinearRegression() por RandomForestRegressor(n_estimators=300, random_state=42) en el mismo pipeline da $R^2$ de 0.81 y MAE de 3.8: casi igual que la lineal, con 300 árboles en vez de 5 coeficientes que se leen en voz alta. Con 106 países de entrenamiento, la flexibilidad extra apenas aporta.
5. Validación cruzada
Con 36 países de prueba, el $R^2$ depende de qué países tocaron. cross_val_score(modelo, X, y, cv=5, scoring="r2") parte los datos en 5 pliegues y usa cada uno como prueba una vez: 0.717, 0.805, 0.700, 0.781 y 0.732, media 0.747. El 0.802 de la partición única era de los optimistas. Reporta la media y el rango de los pliegues, no el mejor.
6. Fuga entre años y GroupKFold
Con todos los años (1704 filas, y year como variable), el mismo país aparece 12 veces. Si Mexico 1997 está en entrenamiento y Mexico 2002 en prueba, el modelo «ya conoce» a Mexico y la evaluación se infla. GroupKFold(n_splits=5) con groups=gap["country"] mete todas las filas de un país en el mismo pliegue: $R^2$ medio de 0.787. Con KFold aleatorio la lineal da 0.797, apenas más, porque no ve el nombre del país; un random forest de 300 árboles con las mismas variables pasa de 0.746 con grupos a 0.777 sin ellos. Cuanto más flexible el modelo, más aprovecha la fuga.
7. Leer los residuos
Los mayores errores en prueba son Tunisia (real 73.9, predicho 60.7), Comoros (65.2 contra 52.2) y Lesotho (42.6 contra 54.2). Lesotho vive mucho menos de lo que su PIB y su continente sugieren: es la epidemia de VIH que el modelo no conoce. Un residuo grande indica una variable que falta.
Trampas comunes
- No medir la línea base: sin ella, un MAE de 4.61 no dice nada.
- Codificar fuera del pipeline: columnas distintas o en otro orden entre entrenamiento y prueba.
- Confiar en una sola partición con pocos datos.
- Repartir filas del mismo país entre entrenamiento y prueba.
- Leer coeficientes como causas: el modelo predice; no dice qué pasaría si un país se enriquece.
Cierre
Para cada modelo del notebook, anota cuatro números: MAE de la línea base, MAE del modelo, $R^2$ con validación cruzada y el rango de los pliegues. Si el modelo complejo no supera al simple en esos números, quédate con el simple.