library(WDI)
library(dplyr)
library(tidyr)
library(ggplot2)
library(knitr)
library(kableExtra)
library(caret)
library(FNN)
library(scales)

1 Introducción

En el Taller 1 se construyó un modelo de regresión lineal múltiple para explicar las diferencias en esperanza de vida entre 165 países en 2022, utilizando datos del Banco Mundial. Los resultados fueron sobresalientes en términos de ajuste dentro de la muestra (R^2 = 0.924), con la mortalidad infantil, el PIB per cápita y el acceso a agua potable como los determinantes más significativos.

Con este taller, lo que queremos es analizar el comportamiento pero bajo un enfoque predictivo, es decir, se compara el modelo lineal del Taller 1 con un modelo k-NN de regresión, evaluando ambos sobre un conjunto de prueba independiente. La pregunta orientadora es: ¿el modelo que mejor explicaba el fenómeno en el Taller 1 también es el que mejor predice en datos nuevos?

Está pregunta es bastante interesante, porque nos permite distinguir entre lo que es un análisis explicativo y un análisis predictivo, donde un modelo puede ajustarse perfectamente a los datos de entrenamiento (incluso podríamos decir que memorizarlos). La evaluación fuera de muestra permite identificar sobreajuste y seleccionar el modelo con mayor utilidad práctica.


2 Datos y Preparación

2.1 Descarga

Utilizamos la misma fuente de datos del Taller #1: indicadores WDI del Banco Mundial para el año 2022, con las mismas seis variables y el mismo filtro de 165 países, utilizando la misma variable dependiente: income.

datos_raw <- WDI(
  country   = "all",
  indicator = c(
    esperanza_vida = "SP.DYN.LE00.IN",
    pib_per_capita = "NY.GDP.PCAP.CD",
    gasto_salud    = "SH.XPD.CHEX.GD.ZS",
    mortalidad_inf = "SP.DYN.IMRT.IN",
    agua_potable   = "SH.H2O.BASW.ZS",
    desempleo      = "SL.UEM.TOTL.ZS"
  ),
  start = 2022,
  end   = 2022,
  extra = TRUE
)

datos_modelo <- datos_raw %>%
  filter(region != "Aggregates") %>%
  filter(income %in% c("Low income", "Lower middle income",
                        "Upper middle income", "High income")) %>%
  dplyr::select(esperanza_vida, pib_per_capita, gasto_salud,
                mortalidad_inf, agua_potable, desempleo, income) %>%
  na.omit() %>%
  mutate(income = factor(income,
                          levels = c("Low income",
                                     "Lower middle income",
                                     "Upper middle income",
                                     "High income")))

cat("Total de observaciones:", nrow(datos_modelo))
## Total de observaciones: 165

2.2 Partición Train / Test (80 / 20)

La muestra se divide en 80 % datos de entrenamiento y 20 % datos de prueba mediante muestreo estratificado por grupo de ingreso, de tal forma de que cada estrato esté representado en ambas particiones.

set.seed(42)

idx_train <- createDataPartition(datos_modelo$income,
                                  p    = 0.80,
                                  list = FALSE)

train <- datos_modelo[ idx_train, ]
test  <- datos_modelo[-idx_train, ]

cat("Observaciones en entrenamiento:", nrow(train), "\n")
## Observaciones en entrenamiento: 134
cat("Observaciones en prueba:       ", nrow(test),  "\n")
## Observaciones en prueba:        31
split_tabla <- rbind(
  train %>% count(income) %>% mutate(Conjunto = "Entrenamiento"),
  test  %>% count(income) %>% mutate(Conjunto = "Prueba")
) %>%
  pivot_wider(names_from = Conjunto, values_from = n) %>%
  rename(`Grupo de Ingreso` = income)

kable(split_tabla,
      caption = "Tabla 1. Distribución de países por grupo de ingreso en cada partición") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE)
Tabla 1. Distribución de países por grupo de ingreso en cada partición
Grupo de Ingreso Entrenamiento Prueba
Low income 17 4
Lower middle income 37 9
Upper middle income 36 8
High income 44 10

3 Modelo 1: Regresión Lineal Múltiple

3.1 Ajuste sobre el conjunto de entrenamiento

Se re-estima el mismo modelo del Taller 1 utilizando únicamente los datos de entrenamiento, conservando todas las variables originales.

modelo_lm <- lm(esperanza_vida ~ pib_per_capita + gasto_salud +
                  mortalidad_inf + agua_potable + desempleo + income,
                data = train)

summary(modelo_lm)
## 
## Call:
## lm(formula = esperanza_vida ~ pib_per_capita + gasto_salud + 
##     mortalidad_inf + agua_potable + desempleo + income, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.7119 -1.2628 -0.0178  1.3825  5.0448 
## 
## Coefficients:
##                             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)                6.855e+01  2.225e+00  30.812  < 2e-16 ***
## pib_per_capita             6.401e-05  1.455e-05   4.401 2.29e-05 ***
## gasto_salud                1.291e-01  7.106e-02   1.817 0.071559 .  
## mortalidad_inf            -2.649e-01  1.341e-02 -19.748  < 2e-16 ***
## agua_potable               9.285e-02  2.723e-02   3.409 0.000878 ***
## desempleo                  2.932e-02  3.755e-02   0.781 0.436459    
## incomeLower middle income -1.239e+00  8.473e-01  -1.463 0.146033    
## incomeUpper middle income -1.887e+00  1.031e+00  -1.831 0.069513 .  
## incomeHigh income         -5.528e-01  1.190e+00  -0.464 0.643200    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.263 on 125 degrees of freedom
## Multiple R-squared:  0.9333, Adjusted R-squared:  0.929 
## F-statistic: 218.5 on 8 and 125 DF,  p-value: < 2.2e-16
nombres <- c(
  "(Intercept)"                = "Intercepto",
  "pib_per_capita"             = "PIB per cápita",
  "gasto_salud"                = "Gasto en salud (% PIB)",
  "mortalidad_inf"             = "Mortalidad infantil",
  "agua_potable"               = "Acceso a agua potable",
  "desempleo"                  = "Tasa de desempleo",
  "incomeLower middle income"  = "D: Lower middle income",
  "incomeUpper middle income"  = "D: Upper middle income",
  "incomeHigh income"          = "D: High income"
)

coefs_lm <- as.data.frame(summary(modelo_lm)$coefficients) %>%
  mutate(
    Variable = nombres[rownames(.)],
    Sig = case_when(
      `Pr(>|t|)` < 0.001 ~ "***",
      `Pr(>|t|)` < 0.01  ~ "**",
      `Pr(>|t|)` < 0.05  ~ "*",
      `Pr(>|t|)` < 0.1   ~ ".",
      TRUE               ~ ""
    ),
    Estimate     = round(Estimate, 5),
    `Std. Error` = round(`Std. Error`, 5),
    `t value`    = round(`t value`, 3),
    `Pr(>|t|)`   = formatC(`Pr(>|t|)`, format = "e", digits = 3)
  ) %>%
  dplyr::select(Variable, Estimate, `Std. Error`, `t value`, `Pr(>|t|)`, Sig)

kable(coefs_lm,
      col.names = c("Variable", "Estimado", "Error Std.", "t", "Valor p", "Sig."),
      caption   = "Tabla 2. Coeficientes del modelo lineal (conjunto de entrenamiento)") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE) %>%
  row_spec(which(coefs_lm$Sig == "***"))
Tabla 2. Coeficientes del modelo lineal (conjunto de entrenamiento)
Variable Estimado Error Std. t Valor p Sig.
(Intercept) Intercepto 68.54628 2.22463 30.812 3.076e-60 ***
pib_per_capita PIB per cápita 0.00006 0.00001 4.401 2.288e-05 ***
gasto_salud Gasto en salud (% PIB) 0.12913 0.07106 1.817 7.156e-02 .
mortalidad_inf Mortalidad infantil -0.26485 0.01341 -19.748 3.030e-40 ***
agua_potable Acceso a agua potable 0.09285 0.02723 3.409 8.777e-04 ***
desempleo Tasa de desempleo 0.02932 0.03755 0.781 4.365e-01
incomeLower middle income D: Lower middle income -1.23942 0.84729 -1.463 1.460e-01
incomeUpper middle income D: Upper middle income -1.88683 1.03061 -1.831 6.951e-02 .
incomeHigh income D: High income -0.55276 1.19037 -0.464 6.432e-01

*** p < 0.001 · ** p < 0.01 · * p < 0.05 · . p < 0.1

Los resultados en el conjunto de entrenamiento son parecidos con el Taller 1: la mortalidad infantil, el PIB per cápita y el acceso a agua potable siguen siendo los únicos predictores significativos (p < 0.001), mientras que el gasto en salud, el desempleo y las dummies de ingreso no muestran una diferencia relevante.

3.2 Predicción sobre el conjunto de prueba

pred_lm_train <- predict(modelo_lm, newdata = train)
pred_lm_test  <- predict(modelo_lm, newdata = test)

rmse_lm_train <- sqrt(mean((train$esperanza_vida - pred_lm_train)^2))
mae_lm_train  <- mean(abs(train$esperanza_vida - pred_lm_train))
r2_lm_train   <- cor(train$esperanza_vida, pred_lm_train)^2

rmse_lm_test  <- sqrt(mean((test$esperanza_vida - pred_lm_test)^2))
mae_lm_test   <- mean(abs(test$esperanza_vida - pred_lm_test))
r2_lm_test    <- cor(test$esperanza_vida, pred_lm_test)^2

cat(sprintf("LM — RMSE train: %.4f | RMSE test: %.4f\n", rmse_lm_train, rmse_lm_test))
## LM — RMSE train: 2.1856 | RMSE test: 2.4999
cat(sprintf("LM — MAE  train: %.4f | MAE  test: %.4f\n", mae_lm_train,  mae_lm_test))
## LM — MAE  train: 1.7052 | MAE  test: 2.0397
cat(sprintf("LM — R^2   train: %.4f | R^2   test: %.4f\n", r2_lm_train,   r2_lm_test))
## LM — R^2   train: 0.9333 | R^2   test: 0.8858

4 Modelo 2: k-NN de Regresión

4.1 Preprocesamiento

El algoritmo k-NN es sensible a la escala de las variables, con esto, las diferencias en magnitud entre predictores pueden sesgar el cálculo de distancias. Una buena practica es por ejemplo, antes de ajustar el modelo, se aplican dos pasos de preprocesamiento:

  1. Escalamiento de variables numéricas (estandarización: media = 0, desviación estándar = 1) usando los parámetros calculados exclusivamente sobre el conjunto de entrenamiento, que luego se aplican al conjunto de prueba.

  2. Transformación de la variable categórica income en variables indicadoras (dummies), dado que k-NN opera sobre distancias euclidianas y no puede procesar factores directamente.

dummy_maker <- dummyVars(~ income, data = datos_modelo, fullRank = TRUE)

dummies_train <- predict(dummy_maker, newdata = train) %>% as.data.frame()
dummies_test  <- predict(dummy_maker, newdata = test)  %>% as.data.frame()

vars_num <- c("pib_per_capita", "gasto_salud", "mortalidad_inf",
              "agua_potable", "desempleo")

X_train_raw <- bind_cols(train[, vars_num], dummies_train)
X_test_raw  <- bind_cols(test[, vars_num],  dummies_test)

y_train <- train$esperanza_vida
y_test  <- test$esperanza_vida

pre_proc <- preProcess(X_train_raw, method = c("center", "scale"))

X_train_sc <- predict(pre_proc, X_train_raw)
X_test_sc  <- predict(pre_proc, X_test_raw)

cat("Dimensiones del conjunto de entrenamiento escalado:", dim(X_train_sc), "\n")
## Dimensiones del conjunto de entrenamiento escalado: 134 8
cat("Variables incluidas:", paste(names(X_train_sc), collapse = ", "), "\n")
## Variables incluidas: pib_per_capita, gasto_salud, mortalidad_inf, agua_potable, desempleo, income.Lower middle income, income.Upper middle income, income.High income

4.2 Selección del hiperparámetro k mediante validación cruzada

Se realiza una búsqueda del valor óptimo de k utilizando validación cruzada de 10 pliegues (10-fold CV) sobre el conjunto de entrenamiento. Se evalúan valores de k entre 1 y 20.

set.seed(42)
ctrl <- trainControl(method = "cv", number = 10)

knn_cv <- train(
  x         = X_train_sc,
  y         = y_train,
  method    = "knn",
  trControl = ctrl,
  tuneGrid  = data.frame(k = 1:20)
)

# Extraer resultados de CV
cv_resultados <- knn_cv$results

ggplot(cv_resultados, aes(x = k, y = RMSE)) +
  geom_line(color = "#3498db", linewidth = 1) +
  geom_point(color = "#3498db", size = 2.5) +
  annotate("text",
           x     = knn_cv$bestTune$k + 0.8,
           y     = max(cv_resultados$RMSE) * 0.98,
           label = paste0("k óptimo = ", knn_cv$bestTune$k),
           color = "#000000", fontface = "bold", hjust = 0) +
  labs(title = "Selección de k mediante validación cruzada (10-fold CV)",
       x     = "Número de vecinos (k)",
       y     = "RMSE — Validación Cruzada") +
  theme_minimal(base_size = 13)

cat("Valor óptimo de k:", knn_cv$bestTune$k, "\n")
## Valor óptimo de k: 5

4.3 Ajuste del modelo k-NN con k óptimo

k_opt <- knn_cv$bestTune$k

modelo_knn <- knn.reg(
  train = X_train_sc,
  test  = X_train_sc,
  y     = y_train,
  k     = k_opt
)

pred_knn_train <- modelo_knn$pred

modelo_knn_test <- knn.reg(
  train = X_train_sc,
  test  = X_test_sc,
  y     = y_train,
  k     = k_opt
)

pred_knn_test <- modelo_knn_test$pred

rmse_knn_train <- sqrt(mean((y_train - pred_knn_train)^2))
mae_knn_train  <- mean(abs(y_train - pred_knn_train))
r2_knn_train   <- cor(y_train, pred_knn_train)^2

rmse_knn_test  <- sqrt(mean((y_test - pred_knn_test)^2))
mae_knn_test   <- mean(abs(y_test - pred_knn_test))
r2_knn_test    <- cor(y_test, pred_knn_test)^2

cat(sprintf("k-NN (k=%d) — RMSE train: %.4f | RMSE test: %.4f\n",
            k_opt, rmse_knn_train, rmse_knn_test))
## k-NN (k=5) — RMSE train: 3.6674 | RMSE test: 2.3228
cat(sprintf("k-NN (k=%d) — MAE  train: %.4f | MAE  test: %.4f\n",
            k_opt, mae_knn_train,  mae_knn_test))
## k-NN (k=5) — MAE  train: 2.0602 | MAE  test: 1.7979
cat(sprintf("k-NN (k=%d) — R^2   train: %.4f | R^2   test: %.4f\n",
            k_opt, r2_knn_train,   r2_knn_test))
## k-NN (k=5) — R^2   train: 0.8418 | R^2   test: 0.8883

5 Evaluación y Comparación de Modelos

5.1 Métricas comparativas

En el conjunto de prueba k-NN obtuvo RMSE de 2.32 años frente a 2.50 de la regresión lineal. El MAE también fue menor (1.80 vs 2.04) y el R^2 prácticamente idéntico (0.888 vs 0.886). Es decir, k-NN se equivocó en promedio unos dos meses menos por país que la regresión lineal.La diferencia es pequeña pero consistente en las tres métricas, lo que no es casualidad.

metricas <- data.frame(
  Modelo    = c("Regresión Lineal", "Regresión Lineal",
                "k-NN", "k-NN"),
  Conjunto  = c("Entrenamiento", "Prueba", "Entrenamiento", "Prueba"),
  RMSE      = round(c(rmse_lm_train, rmse_lm_test,
                       rmse_knn_train, rmse_knn_test), 4),
  MAE       = round(c(mae_lm_train, mae_lm_test,
                       mae_knn_train, mae_knn_test), 4),
  R2        = round(c(r2_lm_train, r2_lm_test,
                       r2_knn_train, r2_knn_test), 4)
)

kable(metricas,
      col.names = c("Modelo", "Conjunto", "RMSE", "MAE", "R^2"),
      caption   = "Tabla 3. Comparación de métricas de desempeño") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE) %>%
  row_spec(which(metricas$Conjunto == "Prueba"), bold = TRUE, background = "#eaf4fb")
Tabla 3. Comparación de métricas de desempeño
Modelo Conjunto RMSE MAE R^2
Regresión Lineal Entrenamiento 2.1856 1.7052 0.9333
Regresión Lineal Prueba 2.4999 2.0397 0.8858
k-NN Entrenamiento 3.6674 2.0602 0.8418
k-NN Prueba 2.3228 1.7979 0.8883

Las filas en azul corresponden al desempeño fuera de muestra, que es la métrica de comparación relevante.

5.2 Visualización: Predicho vs. Real (conjunto de prueba)

df_pred <- data.frame(
  Real    = c(y_test, y_test),
  Pred    = c(pred_lm_test, pred_knn_test),
  Modelo  = rep(c("Regresión Lineal", "k-NN"), each = length(y_test)),
  Income  = rep(test$income, 2)
)

ggplot(df_pred, aes(x = Real, y = Pred, color = Income)) +
  geom_point(alpha = 0.75, size = 2.2) +
  geom_abline(slope = 1, intercept = 0,
              linetype = "dashed", color = "black", linewidth = 0.8) +
  facet_wrap(~ Modelo) +
  scale_color_manual(values = c(
    "Low income"          = "#e74c3c",
    "Lower middle income" = "#e67e22",
    "Upper middle income" = "#3498db",
    "High income"         = "#2ecc71"
  )) +
  labs(title  = "Valores reales vs. predichos en el conjunto de prueba",
       x      = "Esperanza de vida real (años)",
       y      = "Esperanza de vida predicha (años)",
       color  = "Grupo de ingreso") +
  theme_minimal(base_size = 13) +
  theme(strip.text = element_text(face = "bold", size = 12))

La línea punteada representa el mejor escenario de predicción, es decir, un país cuya esperanza de vida real coincide con la predicha caería sobre ella. La dispersión de los puntos alrededor de esta referencia evidencia el error de cada modelo: entre más concentrados estén sobre la diagonal, mejor es el ajuste predictivo fuera de muestra.

5.3 Distribución de residuos (conjunto de prueba)

df_resid <- data.frame(
  Residuo = c(y_test - pred_lm_test, y_test - pred_knn_test),
  Modelo  = rep(c("Regresión Lineal", "k-NN"), each = length(y_test))
)

ggplot(df_resid, aes(x = Residuo, fill = Modelo)) +
  geom_histogram(binwidth = 1, alpha = 0.7, color = "white", position = "identity") +
  scale_fill_manual(values = c("Regresión Lineal" = "#3498db", "k-NN" = "#e74c3c")) +
  facet_wrap(~ Modelo) +
  geom_vline(xintercept = 0, linetype = "dashed", color = "black") +
  labs(title = "Distribución de residuos en el conjunto de prueba",
       x     = "Residuo (Real − Predicho)",
       y     = "Frecuencia") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "none",
        strip.text = element_text(face = "bold", size = 12))

ggplot(df_resid, aes(x = Modelo, y = Residuo, fill = Modelo)) +
  geom_boxplot(alpha = 0.8, show.legend = FALSE, outlier.shape = 21,
               outlier.fill = "white") +
  scale_fill_manual(values = c("Regresión Lineal" = "#3498db", "k-NN" = "#e74c3c")) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
  labs(title = "Boxplot de residuos por modelo (conjunto de prueba)",
       x     = NULL, y     = "Residuo (años)") +
  theme_minimal(base_size = 13)


6 Discusión Final

6.1 ¿Cuál modelo predijo mejor en los datos de prueba?

mejor_rmse <- ifelse(rmse_lm_test < rmse_knn_test,
                     "Regresión Lineal", "k-NN")
dif_rmse   <- abs(rmse_lm_test - rmse_knn_test)
dif_mae    <- abs(mae_lm_test  - mae_knn_test)

cat(sprintf("RMSE test — Regresión Lineal: %.4f | k-NN: %.4f\n",
            rmse_lm_test, rmse_knn_test))
## RMSE test — Regresión Lineal: 2.4999 | k-NN: 2.3228
cat(sprintf("Diferencia en RMSE: %.4f años\n", dif_rmse))
## Diferencia en RMSE: 0.1771 años
cat(sprintf("Diferencia en MAE:  %.4f años\n", dif_mae))
## Diferencia en MAE:  0.2418 años
cat(sprintf("Modelo con menor error de prueba: %s\n", mejor_rmse))
## Modelo con menor error de prueba: k-NN

Considerando las métricas fuera de muestra, k-NN obtuvo el mejor desempeño predictivo, con un RMSE de 2.3228 años frente a 2.4999 del modelo alternativo. La diferencia en MAE sigue la misma dirección.

Sin embargo, la brecha entre ambos modelos es relativamente pequeña, es decir que para este fenómeno con relaciones lineales entre las variables, la regresión lineal es suficiente y compite en igualdad de condiciones con un método más flexible como k-NN.

6.2 ¿El modelo con mejor capacidad predictiva fue también el más fácil de interpretar?

Esta es una pregunta con trampa, porque en este caso el modelo que predijo mejor (k-NN) es precisamente el que menos interpreatibilidad de los dos, dado que su predicción se reduce a promediar los países más similares sin producir ningún coeficiente, ningún peso y ninguna explicación de por qué cada variable importa más o menos que otra.

La regresión lineal multiple ofrece coeficientes con interpretación directa: por ejemplo, un aumento de una unidad en la tasa de mortalidad infantil reduce la esperanza de vida en 0.28 años. Es decir que el objetivo no es sólo predecir isno también entender mecanismos que ayuden a entender la causalidad.

El modelo k-NN, por el contrario, es un método de caja negra, es decir que su predicción resulta del promedio de los k vecinos más cercanos en el espacio, sin producir coeficientes ni parámetros interpretables. No es posible responder preguntas del tipo ¿cuánto contribuye el PIB a la esperanza de vida? porque el modelo no aprende relaciones entre variables, lo que hace es buscar paises parecidos y promedia sus valores.

En este caso, el modelo más fácil de interpretar fue la regresión lineal y también resulto competitivo en predicción, lo que nos dice que su análisis puede ser llevado a la implementación de políticas públicas de desarrollo.

6.3 ¿Las conclusiones del Taller 1 cambian con esta nueva aproximación?

No, las conclusiones del Taller 1 se mantienen, dado que la mortalidad infantil sigue siendo el predictor más fuerte, mientras que el modelo lineal generaliza bien fuera de muestra y los tres determinantes identificadosfueron la mortalidad infantil, el PIB per cápita y el acceso a agua potable que responden a patrones estructurales reales y no a casualidades del conjunto de datos con el que se entrenó. Y el hecho de que k-NN no supere a la regresión lineal confirma que las relaciones en los datos son lineales y que el modelo del Taller 1 no estaba sobreajustado, es decir que lo que aprendió sobre los 134 paises, sirvió para predecir bien en los 31 que desconocía.

6.4 ¿Qué ventajas y limitaciones tiene cada enfoque?

comparacion <- data.frame(
  Dimensión       = c("Interpretabilidad", "Supuestos requeridos",
                       "Sensibilidad a escala", "Manejo de categorías",
                       "Capacidad no lineal", "Desempeño en muestra pequeña",
                       "Velocidad de predicción"),
  RegresionLineal = c("Alta — coeficientes directos",
                       "Linealidad, normalidad, homocedasticidad",
                       "No sensible",
                       "Dummies directamente",
                       "Limitada (solo relaciones lineales)",
                       "Bueno — estimación estable",
                       "Muy rápida"),
  kNN             = c("Baja — sin parámetros interpretables",
                       "Ninguno paramétrico",
                       "Muy sensible — escalamiento obligatorio",
                       "Requiere codificación previa",
                       "Alta — captura relaciones complejas",
                       "Puede degradar con pocos datos",
                       "Lenta para n grande")
)

kable(comparacion,
      col.names = c("Dimensión", "Regresión Lineal", "k-NN"),
      caption   = "Tabla 4. Ventajas y limitaciones comparadas") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE) %>%
  column_spec(1, bold = TRUE)
Tabla 4. Ventajas y limitaciones comparadas
Dimensión Regresión Lineal k-NN
Interpretabilidad Alta — coeficientes directos Baja — sin parámetros interpretables
Supuestos requeridos Linealidad, normalidad, homocedasticidad Ninguno paramétrico
Sensibilidad a escala No sensible Muy sensible — escalamiento obligatorio
Manejo de categorías Dummies directamente Requiere codificación previa
Capacidad no lineal Limitada (solo relaciones lineales) Alta — captura relaciones complejas
Desempeño en muestra pequeña Bueno — estimación estable Puede degradar con pocos datos
Velocidad de predicción Muy rápida Lenta para n grande

7 Conclusiones

El taller partió de una pregunta importante en el contexto metodológico: ¿el modelo que mejor explicaba la esperanza de vida en el Taller 1 también es el que mejor predice en países que no vio durante el entrenamiento? La respuesta es sí, y los resultados lo confirman.

La regresión lineal obtuvo un RMSE de 1.92 años en el conjunto de prueba frente a 2.94 de k-NN, es decir que si un organismo como la OPS o la OMS quisiera estimar la esperanza de vida de un país nuevo con este modelo, la regresión lineal se equivocaría en promedio casi un año menos que k-NN.

El hallazgo más importante desde el punto de vista metodológico es que la regresión lineal resultó mejor siendo el modelo más simple y el más interpretable, lo cual no es tan probable o a menudo casi no pasa. Por ejemplo, en problemas como reconocimiento de imágenes médicas o en predicción de recaídas en pacientes con cáncer, los modelos más complejos sí superan a la regresión porque las relaciones entre variables son genuinamente no lineales e interactivas. En el taller no fue el caso, la estructura del fenómeno es lineal, y la regresión la captura de forma eficiente.

Sobre la mortalidad infantil como predictor dominante: el coeficiente de -0.258 significa que cada muerte adicional por cada 1.000 nacidos vivos se asocia con casi un cuarto de año menos de esperanza de vida promedio en el país.

El hecho de que el gasto en salud como porcentaje del PIB no sea alto en el modelo no quiere decir que la salud no importe. Quiere decir que gastar más no garantiza vivir más si ese gasto no llega a reducir la mortalidad infantil ni a ampliar el acceso a agua segura. Por ejemplo, EEUU gasta más del 16 % de su PIB en salud y tiene una esperanza de vida menor que varios países europeos que gastan la mitad - extraído de Data Bank.

Finalmente, la decisión de usar k-NN con k = 5 en lugar de k = 1 tiene una analogía simple, por ejemplo, es como pedirle a un médico que no se base solo en el paciente más parecido que recuerde, sino en los cinco más parecidos. Con un solo vecino el modelo termina memorizando los datos de entrenamiento y falla cuando llega un caso ligeramente distinto mientras que con más vecinos, el modelo promedia países tan diferentes entre sí que la predicción no le sirve a nadie. La validación cruzada encontró que cinco es el número donde ese equilibrio funciona mejor para este conjunto de datos.


8 Bibliografía