Machine learning desde cero · Lección 4.2 · 21 de 33 lecciones listas · 12 min
Datos faltantes
En pocas palabras
Elimina las filas con huecos y quedan 333 de 344 pingüinos. Si a los más pesados les falta el IMC, la media en los huecos debilita la relación con la diabetes.
Calentamiento Dos preguntas de lecciones anteriores. Un minuto, y no bloquea la lección.
En la lección 4.1 miramos los datos antes de construir un modelo. Hoy, lo que los datos no contienen. Un dato faltante es una celda vacía de la tabla: alguien no midió, no lo anotó o no quiso decirlo. La mayoría de los modelos no acepta una celda vacía, así que hay que hacer algo con ella. Hay dos salidas sencillas: eliminar la fila o escribir algún valor en el hueco, lo que se llama imputar los valores faltantes.
Elige una respuesta y tu grado de seguridad.
Responde para desbloquear el experimento.
Experimento interactivo
Este experimento interactivo funciona en un navegador con JavaScript activado. El texto que lo rodea describe lo que muestra.
Ver la respuesta
Respuesta correcta: 333, porque los 2 pingüinos sin medidas están entre los 11 a los que falta el sexo
Quedan 333. Las celdas vacías de las medidas están en las mismas 2 filas, y a esos pingüinos también les falta el sexo. Así que eliminas 11 filas, no 19 ni 13. Antes de contar las pérdidas, mira en qué filas están los huecos.
Pruébalo
- Empieza por los pingüinos. ¿A cuántos les falta el sexo y a cuántos las medidas? ¿Son las mismas filas?
- El modelo debe predecir la masa a partir de las otras tres medidas. ¿Necesita el sexo? ¿Cuántos pingüinos quedan si eliminas solo las filas sin las columnas del modelo?
- Pasa a la diabetes. Aquí falta el IMC de los pacientes más pesados. Rellena el hueco con la media: ¿la relación entre el IMC y la progresión de la enfermedad se debilita o se refuerza?
- Añade la columna «falta» y compara el R² en validación cruzada. Luego cambia a pacientes al azar. ¿Qué cambia?
Cuando lo hayas comprobado en el lab, pongamos nombre a lo que ves.
La idea en palabras sencillas
Eliminar una fila con un hueco es sencillo, pero cuesta. En los pingüinos, las filas con algún hueco son 11 de 344, así que quedan 333. Un modelo que predice la masa a partir de las medidas del pico y la aleta nunca lee el sexo. Si eliminas solo las filas a las que faltan columnas del modelo, quedan 342. Elimina filas por las columnas que de verdad usas.
La pregunta más importante es a quién le faltan los datos. En el conjunto de diabetes los 442 pacientes tienen su IMC, así que el lab lo oculta a propósito en 110 de ellos. Una vez al azar, otra en los 110 más pesados, es decir, en todos los que tienen un IMC de al menos 29,4. Ese hueco no es aleatorio. Los pacientes sin IMC tienen una progresión media de la enfermedad de 209, y los demás de 133. El simple hecho de que falte el IMC dice algo del paciente.
Cuando escribes en el hueco la media de los demás pacientes (24,3), las 110 personas con más progresión caen en un IMC medio. La correlación del IMC con la progresión de la enfermedad baja de 0,586 a 0,352: la relación se debilita. La recta del modelo no se vuelve más plana, porque la fijan los pacientes con IMC; empeora el ajuste, porque los 110 puntos rellenados quedan lejos de la recta. El R² de una regresión lineal en validación cruzada baja de 0,328 a 0,106. Ayuda una columna extra 0/1 «faltaba el IMC». El modelo da entonces al propio hueco un peso de 76 puntos de progresión, y el R² vuelve a 0,284. Con huecos aleatorios esta columna no aporta nada: 0,264 frente a 0,267 con solo la media.
Jak to policzono
- Pingüinos: Palmer Penguins (Gorman, Williams y Fraser, 2014; paquete palmerpenguins: Horst, Hill y Gorman, 2020), licencia CC0. 344 filas, huecos reales: el sexo en 11 (Adelie 6, Gentoo 5), las cuatro medidas en los mismos 2 pingüinos.
- Diabetes: Diabetes (Efron, Hastie, Johnstone y Tibshirani, 2004), incluido en scikit-learn con licencia BSD-3-Clause. 442 pacientes, progresión de la enfermedad al cabo de un año. El IMC está en los datos para todos; los huecos de 110 pacientes los hicimos nosotros: al azar o en los 110 con el IMC más alto.
- Modelo: regresión lineal de la progresión de la enfermedad sobre el IMC (con la columna de hueco, sobre el IMC y esa columna), validación cruzada en 5 partes repetida 10 veces. La media para rellenar se calcula con los pacientes que tienen IMC; en la validación cruzada, en cada partición, solo con la parte de entrenamiento, como
SimpleImputeren un pipeline.
Por qué funciona
La media rellenada está en el centro del eje del IMC, pero pertenece a pacientes de su extremo derecho. Su progresión es alta, así que al rellenar aparece una columna vertical de puntos altos sobre un IMC medio. Esa columna no añade nada a la variación conjunta del IMC y la progresión. Frente a los pacientes que tienen IMC (correlación 0,459), solo añade variación a la progresión, así que la correlación baja a 0,352. Frente a los datos completos (0,586), estos 110 pacientes pierden su parte de la variación conjunta: tenían un IMC alto y una progresión alta, y ahora están en el centro del eje. La desviación estándar del IMC se reduce de 4,4 a 2,3, porque 110 valores están en un solo punto.
Eliminar a esos pacientes tampoco arregla nada. Queda una nube sin su extremo derecho, y la correlación en los 332 restantes es 0,459 en vez de 0,586. Además, para 110 pacientes el modelo no da ninguna respuesta. La columna de hueco funciona porque da al modelo un peso propio para el grupo cuyo IMC no conocemos. Ese grupo difiere en progresión de la enfermedad, así que el peso lleva esa diferencia a las predicciones. Cuando el hueco es aleatorio, el grupo sin IMC no difiere del resto (156 frente a 151) y el peso no tiene nada que llevar.
Un error frecuente
Verdadero o falso: «rellena los huecos con la media y listo». Falso. La media reduce la dispersión de la columna y finge que el hueco nunca existió. Cuando el hueco depende del propio valor, como aquí en los más pesados, la media deforma la relación: la correlación baja de 0,586 a 0,352 y el R² de 0,328 a 0,106. La columna «falta» devuelve el R² a 0,284. En el código de abajo gana a la media sola en 48 de 50 partes de validación.
La media y eliminar filas están bien cuando los huecos son pocos y aleatorios. Pero antes comprueba si las filas con hueco difieren del resto.
Test
Tres preguntas para comprobar lo aprendido. Elige una respuesta o despliega la solución bajo cada pregunta. Con el teclado: las flechas eligen, Enter o Espacio confirma.
Ideas clave
- Elimina filas por las columnas que usas: quedan 342 pingüinos en vez de 333.
- Antes de hacer nada con los huecos, comprueba si las filas con hueco difieren del resto.
- La media en los huecos reduce la dispersión y, con huecos no aleatorios, debilita la relación: una correlación de 0,586 baja a 0,352.
- Una columna 0/1 «falta» deja al modelo usar la información que lleva el propio hueco: R² 0,284 en vez de 0,106.
Más a fondo: fórmula, código, práctica
Las matemáticas
Rellenar con la media: x̃ᵢ = x̄_obs para los pacientes con hueco, x̃ᵢ = xᵢ para el resto. Los puntos rellenados tienen x̃ᵢ − x̄ = 0, así que no añaden nada a la covarianza Σ (x̃ᵢ − x̄)(yᵢ − ȳ) ni a la varianza Σ (x̃ᵢ − x̄)², pero añaden (yᵢ − ȳ)² a la varianza de y. Frente a solo los pacientes con IMC, la suma Σ (x̃ᵢ − x̄)(yᵢ − ȳ) no cambia mientras la varianza de y crece, así que la correlación r = cov(x̃, y) / (s_x̃ · s_y) baja (0,459 → 0,352). Frente a los datos completos, la varianza de y es la misma; baja la covarianza, porque los puntos rellenados ya no aportan sus términos grandes y positivos (0,586 → 0,352).
La columna de hueco: ŷ = a + b · x̃ + c · m, donde m = 1 cuando faltaba el IMC y m = 0 en otro caso. Para un paciente con hueco el modelo predice a + b · x̄_obs + c, así que c desplaza a todo el grupo sin IMC. Aquí c = 76 con los huecos en los más pesados.
En código (Python)
El mismo experimento en pandas y scikit-learn: los huecos de la tabla de pingüinos, luego el IMC oculto en 110 pacientes al azar y en los 110 más pesados, la media en los huecos y la media con una columna de hueco en validación cruzada 10 × 5.
import numpy as np
import pandas as pd
from sklearn.datasets import load_diabetes
from sklearn.model_selection import cross_val_score, RepeatedKFold
from sklearn.linear_model import LinearRegression
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
df = pd.read_csv("data/raw/penguins.csv")
gaps = df.isna().sum()
print(f"{len(df)} penguins; missing: " + ", ".join(f"{c} {n}" for c, n in gaps[gaps > 0].items()))
measured = df.dropna(subset=["bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"]) # elimina solo las filas sin las columnas del modelo
print(f"dropna() leaves {len(df.dropna())}; dropping only rows without measurements leaves {len(measured)}")
X, y = load_diabetes(return_X_y=True, as_frame=True, scaled=False)
bmi, y = X.bmi.to_numpy(), y.to_numpy()
heavy = bmi >= np.sort(bmi)[-110] # los 110 más pesados: IMC de al menos 29,4
rnd = np.zeros(len(bmi), bool)
rnd[np.random.default_rng(0).choice(len(bmi), 110, replace=False)] = True # 110 al azar
cv = RepeatedKFold(n_splits=5, n_repeats=10, random_state=0)
r2 = lambda model, Z: cross_val_score(model, Z, y, cv=cv, scoring="r2")
print(f"{len(bmi)} patients, BMI vs progression: r {np.corrcoef(bmi, y)[0, 1]:.3f}, CV R2 {r2(LinearRegression(), bmi[:, None]).mean():.3f}")
for name, miss in [("random", rnd), ("heaviest", heavy)]:
hidden = np.where(miss, np.nan, bmi)[:, None]
filled = np.where(miss, bmi[~miss].mean(), bmi) # la media solo de los pacientes con IMC
mean_r2 = r2(make_pipeline(SimpleImputer(), LinearRegression()), hidden) # la media se aprende en cada partición, solo con su parte de entrenamiento
flag_r2 = r2(make_pipeline(SimpleImputer(add_indicator=True), LinearRegression()), hidden) # segunda columna: 1 = faltaba el IMC
print(f"BMI hidden for 110 {name}: progression {y[miss].mean():.0f} vs {y[~miss].mean():.0f}; mean fill: r {np.corrcoef(filled, y)[0, 1]:.3f}, CV R2 {mean_r2.mean():.3f}; mean + flag: CV R2 {flag_r2.mean():.3f}")
print(f"heaviest: mean + flag beats the mean alone in {(flag_r2 > mean_r2).sum()} of 50 folds")
Resultado
344 penguins; missing: bill_length_mm 2, bill_depth_mm 2, flipper_length_mm 2, body_mass_g 2, sex 11
dropna() leaves 333; dropping only rows without measurements leaves 342
442 patients, BMI vs progression: r 0.586, CV R2 0.332
BMI hidden for 110 random: progression 159 vs 150; mean fill: r 0.503, CV R2 0.241; mean + flag: CV R2 0.239
BMI hidden for 110 heaviest: progression 209 vs 133; mean fill: r 0.352, CV R2 0.112; mean + flag: CV R2 0.289
heaviest: mean + flag beats the mean alone in 48 of 50 folds
Los pacientes al azar y las particiones son distintos de los del lab, así que los números también difieren un poco. La imagen es la misma: quedan 333 o 342 pingüinos, la media en los más pesados baja la correlación a 0,352 y la columna de hueco sube el R² de 0,112 a 0,289. Con huecos aleatorios la columna no ayuda: 0,239 frente a 0,241.
Descargar el notebook (.ipynb). Ábrelo en Jupyter o súbelo a Google Colab; descarga por sí solo los datos que falten.
Para profesionales
- En scikit-learn:
SimpleImputer(strategy="mean")o"median", y conadd_indicator=Trueobtienes la columna de hueco de una vez. Mételo enmake_pipeline, para que la media se calcule solo con la parte de entrenamiento de cada partición. df.isna().sum()ydf[df.x.isna()].describe()muestran cuántos huecos hay y si las filas con hueco difieren del resto. Es lo primero que hay que comprobar antes de elegir un método.IterativeImputeryKNNImputerpredicen el valor que falta a partir de otras columnas. Ayudan cuando los huecos dependen de otras columnas, pero no recuperan un hueco que depende del propio valor oculto; entonces la columna de hueco importa más.- Los árboles de
HistGradientBoostingClassifiermanejan los huecos por sí mismos: en cada división aprenden hacia qué lado enviar las filas con hueco.
¿Te ha sido útil?
¡Gracias! Nos ayuda a mejorar.
Profundiza en el Atlas
Entradas de la enciclopedia sobre las ideas de esta lección: