Sesión 1.4: Laboratorio - Exploración avanzada y comparación de modelos
Lo que vamos a hacer:
Lo que vamos a practicar:
Hay menos código predefinido. Ustedes van a escribir más!
¡No se preocupen! Les damos una estructura para que puedan completar. 😉
Vamos a trabajar con datos simulados de satisfacción con la democracia en América Latina:
Rows: 500
Columns: 10
$ pais <chr> "Argentina", "Costa Rica", "Panamá", "Perú", "N…
$ edad <dbl> 50, 68, 30, 45, 45, 34, 51, 18, 39, 34, 33, 18,…
$ educacion_anos <dbl> 10, 16, 15, 14, 8, 5, 14, 9, 10, 15, 14, 13, 15…
$ ingreso_hogar <dbl> 696, 1242, 818, 203, 581, 686, 319, 1455, 266, …
$ confianza_gobierno <dbl> 7, 6, 5, 4, 5, 2, 5, 2, 6, 6, 4, 5, 3, 4, 5, 8,…
$ consumo_noticias <dbl> 10.2, 9.6, 14.1, 7.8, 18.3, 13.8, 1.1, 5.5, 15.…
$ participacion_politica <dbl> 88, 43, 35, 62, 23, 63, 11, 16, 25, 49, 55, 50,…
$ zona <chr> "urbano", "urbano", "rural", "urbano", "rural",…
$ genero <chr> "femenino", "femenino", "femenino", "femenino",…
$ satisfecho <chr> "si", "si", "no", "si", "si", "no", "si", "si",…
Variable objetivo: satisfecho (sí/no) - ¿El encuestado está satisfecho con la democracia?
| Variable | Descripción | Tipo |
|---|---|---|
satisfecho |
Satisfacción con la democracia (sí/no) | Categórica |
edad |
Edad del encuestado | Numérica |
educacion_anos |
Años de educación formal | Numérica |
ingreso_hogar |
Ingreso mensual del hogar (USD) | Numérica |
confianza_gobierno |
Confianza en el gobierno (1-10) | Numérica |
consumo_noticias |
Horas semanales de noticias | Numérica |
participacion_politica |
Índice de participación (0-100) | Numérica |
zona |
Urbano/Rural | Categórica |
genero |
Masculino/Femenino/Otro | Categórica |
pais |
País de residencia | Categórica |
Tarea: Exploren el dataset y respondan estas preguntas.
Tómense 10 minutos para explorar.
# Histogramas de variables numéricas
satisfaccion |>
select(where(is.numeric)) |>
pivot_longer(everything(), names_to = "variable", values_to = "valor") |>
ggplot(aes(x = valor)) +
geom_histogram(bins = 30, fill = "#2d4563", alpha = 0.7) +
facet_wrap(~variable, scales = "free") +
labs(title = "Distribución de variables numéricas")# Boxplots por satisfacción
satisfaccion |>
select(satisfecho, confianza_gobierno, participacion_politica, edad) |>
pivot_longer(-satisfecho, names_to = "variable", values_to = "valor") |>
ggplot(aes(x = satisfecho, y = valor, fill = satisfecho)) +
geom_boxplot(alpha = 0.7) +
facet_wrap(~variable, scales = "free_y") +
scale_fill_manual(values = c("#e74c3c", "#27ae60")) +
labs(title = "Variables numéricas por nivel de satisfacción") +
theme(legend.position = "none")Tarea: Creen visualizaciones adicionales para entender los datos.
Tómense 10 minutos.
Primero, preparemos los datos básicos:
Rows: 500
Columns: 10
$ pais <fct> Argentina, Costa Rica, Panamá, Perú, Nicaragua,…
$ edad <dbl> 50, 68, 30, 45, 45, 34, 51, 18, 39, 34, 33, 18,…
$ educacion_anos <dbl> 10, 16, 15, 14, 8, 5, 14, 9, 10, 15, 14, 13, 15…
$ ingreso_hogar <dbl> 696, 1242, 818, 203, 581, 686, 319, 1455, 266, …
$ confianza_gobierno <dbl> 7, 6, 5, 4, 5, 2, 5, 2, 6, 6, 4, 5, 3, 4, 5, 8,…
$ consumo_noticias <dbl> 10.2, 9.6, 14.1, 7.8, 18.3, 13.8, 1.1, 5.5, 15.…
$ participacion_politica <dbl> 88, 43, 35, 62, 23, 63, 11, 16, 25, 49, 55, 50,…
$ zona <fct> urbano, urbano, rural, urbano, rural, urbano, u…
$ genero <fct> femenino, femenino, femenino, femenino, masculi…
$ satisfecho <fct> si, si, no, si, si, no, si, si, si, no, si, si,…
El feature engineering puede mejorar el rendimiento del modelo:
# Crear nuevas variables
satisfaccion <- satisfaccion |>
mutate(
# Grupos de edad
grupo_edad = cut(edad,
breaks = c(0, 30, 50, 70, 100),
labels = c("joven", "adulto", "mayor", "anciano")),
# Ingreso per cápita (asumiendo hogar promedio de 3.5 personas)
ingreso_percapita = ingreso_hogar / 3.5,
# Indicador de alta participación
alta_participacion = if_else(participacion_politica > 50, "alta", "baja"),
# Interacción: confianza × participación
confianza_x_participacion = confianza_gobierno * participacion_politica / 100
)
# Ver las nuevas variables
satisfaccion |> select(grupo_edad, ingreso_percapita, alta_participacion, confianza_x_participacion) |> head()# A tibble: 6 × 4
grupo_edad ingreso_percapita alta_participacion confianza_x_participacion
<fct> <dbl> <chr> <dbl>
1 adulto 199. alta 6.16
2 mayor 355. baja 2.58
3 joven 234. baja 1.75
4 adulto 58 alta 2.48
5 adulto 166 baja 1.15
6 adulto 196 alta 1.26
Tarea: Piensen en otras variables que podrían ser útiles.
Ideas para explorar:
Creen al menos 2 variables nuevas.
# Fijar semilla
set.seed(2026)
# Dividir datos
datos_split <- initial_split(satisfaccion, prop = 0.75, strata = satisfecho)
datos_train <- training(datos_split)
datos_test <- testing(datos_split)
# Crear folds para validación cruzada
folds <- vfold_cv(datos_train, v = 5, strata = satisfecho)
cat("Train:", nrow(datos_train), "| Test:", nrow(datos_test))Train: 374 | Test: 126
initial_split(): Divide el dataset en train/testtraining() y testing(): Extraen los datasets de train y testvfold_cv(): Crea folds para validación cruzada (5 folds, estratificados por satisfecho)Vamos a comparar tres modelos diferentes:
# 1. Regresión logística
modelo_logistico <- logistic_reg() |>
set_engine("glm") |>
set_mode("classification")
# 2. Árbol de decisión
modelo_arbol <- decision_tree() |>
set_engine("rpart") |>
set_mode("classification")
# 3. K-Nearest Neighbors (k vecinos más cercanos)
modelo_knn <- nearest_neighbor(neighbors = 5) |>
set_engine("kknn") |>
set_mode("classification")
# Lista de modelos
modelos <- list(
logistico = modelo_logistico,
arbol = modelo_arbol,
knn = modelo_knn
)Decidamos qué variables usar:
# Fórmula con variables originales
formula_basica <- satisfecho ~ edad + educacion_anos + ingreso_hogar +
confianza_gobierno + consumo_noticias +
participacion_politica + zona
# Fórmula con features nuevas
formula_extendida <- satisfecho ~ edad + educacion_anos + ingreso_hogar +
confianza_gobierno + consumo_noticias +
participacion_politica + zona +
confianza_x_participacionVamos a probar ambas fórmulas.
Creemos una función que facilite la evaluación:
evaluar_modelo <- function(modelo, formula, folds, nombre = "modelo") {
# Validación cruzada
# event_level = "second" porque "si" es el segundo nivel del factor
resultados <- fit_resamples(
modelo,
formula,
resamples = folds,
metrics = metric_set(accuracy, precision, recall, roc_auc),
control = control_resamples(event_level = "second")
)
# Extraer métricas
collect_metrics(resultados) |>
mutate(modelo = nombre)
}# A tibble: 4 × 7
.metric .estimator mean n std_err .config modelo
<chr> <chr> <dbl> <int> <dbl> <chr> <chr>
1 accuracy binary 0.695 5 0.0249 pre0_mod0_post0 Logístico
2 precision binary 0.468 5 0.117 pre0_mod0_post0 Logístico
3 recall binary 0.182 5 0.0431 pre0_mod0_post0 Logístico
4 roc_auc binary 0.691 5 0.0477 pre0_mod0_post0 Logístico
# Evaluar todos los modelos con la fórmula básica
eval_arbol <- evaluar_modelo(modelo_arbol, formula_basica, folds, "Árbol")
eval_knn <- evaluar_modelo(modelo_knn, formula_basica, folds, "KNN")
# Combinar resultados
resultados <- bind_rows(eval_logistico, eval_arbol, eval_knn)
# Mostrar comparación
resultados |>
select(modelo, .metric, mean, std_err) |>
pivot_wider(names_from = .metric, values_from = c(mean, std_err))# A tibble: 3 × 9
modelo mean_accuracy mean_precision mean_recall mean_roc_auc std_err_accuracy
<chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Logíst… 0.695 0.468 0.182 0.691 0.0249
2 Árbol 0.636 0.327 0.227 0.576 0.0134
3 KNN 0.642 0.377 0.327 0.625 0.0159
# ℹ 3 more variables: std_err_precision <dbl>, std_err_recall <dbl>,
# std_err_roc_auc <dbl>
# Gráfico de barras con error estándar
resultados |>
ggplot(aes(x = modelo, y = mean, fill = modelo)) +
geom_col(alpha = 0.8) +
geom_errorbar(aes(ymin = mean - std_err, ymax = mean + std_err),
width = 0.2) +
facet_wrap(~.metric, scales = "free_y") +
scale_fill_manual(values = c("#2d4563", "#e74c3c", "#27ae60")) +
labs(title = "Comparación de modelos", y = "Valor", x = "") +
theme(legend.position = "none")Tarea: Evalúen los modelos con la fórmula extendida.
Tómense 5-10 minutos para experimentar.
Una vez elegido el mejor modelo, evaluamos en test:
# Elegimos el modelo logístico (por ejemplo)
ajuste_final <- modelo_logistico |>
fit(formula_basica, data = datos_train)
# Predicciones en test
pred_test <- ajuste_final |>
predict(datos_test, type = "prob") |>
bind_cols(predict(ajuste_final, datos_test)) |>
bind_cols(datos_test)
# Métricas finales
pred_test |>
metrics(truth = satisfecho, estimate = .pred_class)# A tibble: 2 × 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 accuracy binary 0.730
2 kap binary 0.182
# A tibble: 1 × 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 roc_auc binary 0.642
Escenario: Un organismo regional quiere identificar qué factores predicen mejor la insatisfacción con la democracia para diseñar políticas públicas.
Su tarea:
Tienen 10 minutos. Trabajen en grupos de 2-3.
Paso 1: Definir la pregunta
Paso 2: Seleccionar variables
Paso 3: Modelar y evaluar
Paso 4: Interpretar y recomendar
# 1. Seleccionar y preparar variables
datos_proyecto <- satisfaccion |>
select(satisfecho, ...) |> # Sus variables elegidas
mutate(...) # Sus variables derivadas
# 2. Dividir datos
set.seed(2026)
split_proy <- initial_split(datos_proyecto, prop = 0.75, strata = satisfecho)
train_proy <- training(split_proy)
test_proy <- testing(split_proy)
folds_proy <- vfold_cv(train_proy, v = 5, strata = satisfecho)
# 3. Definir y evaluar modelos
# ...
# 4. Evaluar en test
# ...
# 5. Interpretar coeficientes (si es regresión logística)
ajuste_final |> tidy() |> arrange(p.value)# --- Ejemplo: análisis para el organismo regional ---
# 1. Preparar datos con variables seleccionadas + feature nuevo
datos_proy <- satisfaccion |>
mutate(
confianza_baja = if_else(confianza_gobierno <= 4, "si", "no"),
educacion_alta = if_else(educacion_anos > 12, "si", "no")
) |>
select(satisfecho, edad, confianza_gobierno, participacion_politica,
ingreso_hogar, zona, confianza_baja, educacion_alta)
# 2. Dividir y crear folds
set.seed(2026)
sp <- initial_split(datos_proy, prop = 0.75, strata = satisfecho)
train_p <- training(sp); test_p <- testing(sp)
folds_p <- vfold_cv(train_p, v = 5, strata = satisfecho)
# 3. Evaluar regresión logística y árbol de decisión
formula_proy <- satisfecho ~ edad + confianza_gobierno +
participacion_politica + ingreso_hogar + zona + confianza_baja
eval_log <- evaluar_modelo(modelo_logistico, formula_proy, folds_p, "Logístico")
eval_arb <- evaluar_modelo(modelo_arbol, formula_proy, folds_p, "Árbol")
bind_rows(eval_log, eval_arb) |>
select(modelo, .metric, mean) |>
pivot_wider(names_from = .metric, values_from = mean)
# 4. Interpretar coeficientes del mejor modelo
fit_proy <- modelo_logistico |> fit(formula_proy, data = train_p)
tidy(fit_proy) |> mutate(odds_ratio = exp(estimate)) |> arrange(p.value)Copien, adapten y personalicen este código para su propio análisis.
Al final del laboratorio, cada grupo presentará (3 minutos):
Preparen un resumen verbal!
Sesión 1.1: ¿Qué es la IA?
Sesión 1.2: Fundamentos de ML
Sesión 1.3: Laboratorio 1
Sesión 1.4: Laboratorio 2
Día 2: Aprendizaje supervisado
Descansen y vengan con energía mañana! 😄
Documentación oficial:
Tutoriales recomendados:
Todos estos recursos están disponibles gratuitamente en línea.
[1] 500 14
# A tibble: 2 × 3
satisfecho n prop
<fct> <int> <dbl>
1 no 147 0.294
2 si 353 0.706
pais edad educacion_anos
0 0 0
ingreso_hogar confianza_gobierno consumo_noticias
0 0 0
participacion_politica zona genero
0 0 0
satisfecho grupo_edad ingreso_percapita
0 0 0
alta_participacion confianza_x_participacion
0 0
edad educacion_anos ingreso_hogar confianza_gobierno
Min. :18.00 Min. : 0.000 Min. : 100.0 Min. : 1.00
1st Qu.:32.00 1st Qu.: 7.000 1st Qu.: 372.5 1st Qu.: 3.00
Median :42.00 Median :10.000 Median : 683.0 Median : 4.00
Mean :42.47 Mean : 9.984 Mean : 850.7 Mean : 4.51
3rd Qu.:53.00 3rd Qu.:13.000 3rd Qu.:1068.0 3rd Qu.: 6.00
Max. :80.00 Max. :20.000 Max. :6103.0 Max. :10.00
consumo_noticias participacion_politica ingreso_percapita
Min. : 0.000 Min. : 0.00 Min. : 28.57
1st Qu.: 4.400 1st Qu.: 18.00 1st Qu.: 106.43
Median : 8.350 Median : 35.00 Median : 195.14
Mean : 8.416 Mean : 36.23 Mean : 243.07
3rd Qu.:12.000 3rd Qu.: 53.00 3rd Qu.: 305.14
Max. :28.900 Max. :100.00 Max. :1743.71
confianza_x_participacion
Min. :0.000
1st Qu.:0.500
Median :1.340
Mean :1.635
3rd Qu.:2.465
Max. :9.000
# A tibble: 18 × 2
pais n
<fct> <int>
1 Panamá 38
2 Venezuela 38
3 Chile 32
4 Costa Rica 32
5 Nicaragua 32
6 Uruguay 31
7 Guatemala 30
8 República Dominicana 30
9 Bolivia 28
10 El Salvador 27
11 Honduras 27
12 Colombia 26
13 Perú 26
14 Argentina 25
15 Paraguay 23
16 Brasil 21
17 México 19
18 Ecuador 15
# A tibble: 2 × 2
zona n
<fct> <int>
1 rural 143
2 urbano 357
# 1. Satisfacción por zona
satisfaccion |>
count(zona, satisfecho) |>
group_by(zona) |>
mutate(prop = n / sum(n)) |>
ggplot(aes(x = zona, y = prop, fill = satisfecho)) +
geom_col(position = "dodge", alpha = 0.8) +
scale_fill_manual(values = c("#e74c3c", "#27ae60")) +
labs(title = "Satisfacción por zona", y = "Proporción")# 2. Satisfacción por país
satisfaccion |>
count(pais, satisfecho) |>
group_by(pais) |>
mutate(prop = n / sum(n)) |>
filter(satisfecho == "si") |>
ggplot(aes(x = reorder(pais, prop), y = prop)) +
geom_col(fill = "#27ae60", alpha = 0.8) +
coord_flip() +
labs(title = "Proporción de satisfechos por país", x = "", y = "Proporción")# 3. Satisfacción por género
satisfaccion |>
count(genero, satisfecho) |>
group_by(genero) |>
mutate(prop = n / sum(n)) |>
ggplot(aes(x = genero, y = prop, fill = satisfecho)) +
geom_col(position = "dodge", alpha = 0.8) +
scale_fill_manual(values = c("#e74c3c", "#27ae60")) +
labs(title = "Satisfacción por género", y = "Proporción")# Crear variables adicionales
satisfaccion <- satisfaccion |>
mutate(
# Educación alta: más de 12 años
educacion_alta = if_else(educacion_anos > 12, "alta", "baja"),
# Combinación zona + género
zona_genero = paste(zona, genero, sep = "_"),
# Grupos de ingreso (terciles)
grupo_ingreso = cut(ingreso_hogar,
breaks = quantile(ingreso_hogar, c(0, 1/3, 2/3, 1)),
labels = c("bajo", "medio", "alto"),
include.lowest = TRUE),
# Consumidor alto de noticias (más de 5 horas semanales)
noticias_alto = if_else(consumo_noticias > 5, "alto", "bajo")
)
# Verificar las nuevas variables
satisfaccion |>
count(educacion_alta)# A tibble: 2 × 2
educacion_alta n
<chr> <int>
1 alta 137
2 baja 363
# A tibble: 3 × 2
grupo_ingreso n
<fct> <int>
1 bajo 167
2 medio 166
3 alto 167
# A tibble: 2 × 2
noticias_alto n
<chr> <int>
1 alto 359
2 bajo 141
# Evaluar los 3 modelos con la fórmula extendida
eval_log_ext <- evaluar_modelo(
modelo_logistico, formula_extendida, folds, "Logístico (ext)")
eval_arbol_ext <- evaluar_modelo(
modelo_arbol, formula_extendida, folds, "Árbol (ext)")
eval_knn_ext <- evaluar_modelo(
modelo_knn, formula_extendida, folds, "KNN (ext)")
# Combinar todos los resultados
todos <- bind_rows(
eval_logistico, eval_arbol, eval_knn,
eval_log_ext, eval_arbol_ext, eval_knn_ext
)
# Comparar accuracy: básica vs. extendida
todos |>
filter(.metric == "accuracy") |>
select(modelo, mean, std_err) |>
arrange(desc(mean))# A tibble: 6 × 3
modelo mean std_err
<chr> <dbl> <dbl>
1 Logístico 0.695 0.0249
2 Logístico (ext) 0.692 0.0233
3 Árbol (ext) 0.652 0.0219
4 KNN (ext) 0.644 0.0115
5 KNN 0.642 0.0159
6 Árbol 0.636 0.0134
La mejora por agregar confianza_x_participacion suele ser modesta. El feature engineering vale la pena cuando las variables nuevas capturan relaciones no lineales o interacciones que el modelo no puede descubrir por sí solo.