IA para Científicos Sociales

Sesión 1.4: Laboratorio - Exploración avanzada y comparación de modelos

Danilo Freire

Departament of Data and Decision Sciences
Emory University

Laboratorio 2: Exploración avanzada

Objetivos del laboratorio

Lo que vamos a hacer:

  1. Trabajar con un nuevo dataset
  2. Análisis exploratorio detallado
  3. Feature engineering básico
  4. Comparar múltiples modelos
  5. Visualizar resultados
  6. Discutir interpretaciones

Lo que vamos a practicar:

  • Decisiones de preprocesamiento
  • Comparación sistemática de modelos
  • Visualización de resultados
  • Interpretación de modelos

Hay menos código predefinido. Ustedes van a escribir más!

¡No se preocupen! Les damos una estructura para que puedan completar. 😉

Parte 1: Nuevo dataset

Cargar los paquetes

# Cargar paquetes
library(tidymodels)
library(tidyverse)

# Configurar tema de ggplot
theme_set(theme_minimal(base_size = 14))

Si no tienen los paquetes instalados, ejecuten primero:

install.packages(c("tidymodels", "tidyverse", "kknn", "rpart"))

El dataset: satisfacción democrática

Vamos a trabajar con datos simulados de satisfacción con la democracia en América Latina:

# Cargar datos
satisfaccion <- read_csv("datos/satisfaccion_democracia.csv")

# Ver estructura
glimpse(satisfaccion) # Ver las variables y tipos
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?

Descripción de las variables

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

Ejercicio 1: Exploración inicial

Tarea: Exploren el dataset y respondan estas preguntas.

  1. ¿Cuántas observaciones hay? ¿Cuántas variables?
  2. ¿Cómo está distribuida la variable objetivo?
  3. ¿Hay valores faltantes?
  4. ¿Cómo se distribuyen las variables numéricas?
# Código para empezar (completen ustedes)
dim(satisfaccion)
satisfaccion |> count(satisfecho)
sum(is.na(satisfaccion))
# ... más exploración

Tómense 10 minutos para explorar.

Ir al Apéndice 1

Visualizar distribuciones

# 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")

Relación con la variable objetivo

# 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")

Ejercicio 2: Más visualizaciones

Tarea: Creen visualizaciones adicionales para entender los datos.

  1. ¿Cómo varía la satisfacción por zona (urbano/rural)?
  2. ¿Cómo varía por país?
  3. ¿Hay diferencias por género?
  4. ¿Qué relación hay entre ingreso y educación?
# Ejemplo: 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")

# Completen con más visualizaciones...

Tómense 10 minutos.

Ir al Apéndice 2

Parte 2: Feature engineering

Preparar los datos

Primero, preparemos los datos básicos:

# Convertir variables categóricas a factores
satisfaccion <- satisfaccion |>
  mutate(
    satisfecho = factor(satisfecho, levels = c("no", "si")),
    zona = factor(zona),
    genero = factor(genero),
    pais = factor(pais)
  )

# Verificar
glimpse(satisfaccion)
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,…

Crear nuevas variables

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

Ejercicio 3: Crear más features

Tarea: Piensen en otras variables que podrían ser útiles.

Ideas para explorar:

  • ¿Un indicador de “educación alta” (más de 12 años)?
  • ¿Una variable que combine zona y género?
  • ¿Grupos de ingreso (bajo, medio, alto)?
  • ¿Consumidor de noticias alto vs. bajo?
# Ejemplo
satisfaccion <- satisfaccion |>
  mutate(
    educacion_alta = if_else(educacion_anos > 12, "alta", "baja"),
    # ... más variables
  )

Creen al menos 2 variables nuevas.

Ir al Apéndice 3

Dividir los datos

# 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/test
  • training() y testing(): Extraen los datasets de train y test
  • vfold_cv(): Crea folds para validación cruzada (5 folds, estratificados por satisfecho)

Parte 3: Comparación de modelos

Definir los modelos a comparar

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
)

Crear una fórmula

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_participacion

Vamos a probar ambas fórmulas.

Función para evaluar un modelo

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)
}
# Evaluar regresión logística
eval_logistico <- evaluar_modelo(modelo_logistico, formula_basica, folds, "Logístico")
eval_logistico
# 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

# 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>

Visualizar la comparación

# 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")

Ejercicio 4: Comparar con features nuevos

Tarea: Evalúen los modelos con la fórmula extendida.

  1. ¿Mejora algún modelo al agregar las nuevas variables?
  2. ¿Cuál modelo es más sensible a los features nuevos?
  3. ¿Vale la pena el esfuerzo del feature engineering?
# Evaluar con fórmula extendida
eval_log_ext <- evaluar_modelo(modelo_logistico, formula_extendida, folds, "Logístico (ext)")
# ... evaluar los otros modelos

# Comparar resultados
# ...

Tómense 5-10 minutos para experimentar.

Ir al Apéndice 4

Modelo final en datos de test

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
# AUC-ROC
pred_test |>
  roc_auc(truth = satisfecho, .pred_si, event_level = "second")
# A tibble: 1 × 3
  .metric .estimator .estimate
  <chr>   <chr>          <dbl>
1 roc_auc binary         0.642

Parte 4: Mini-proyecto guiado

El desafío

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:

  1. Elegir las variables más relevantes (justifiquen)
  2. Crear al menos una variable derivada útil
  3. Entrenar y comparar al menos 2 modelos
  4. Presentar resultados con recomendaciones de política

Tienen 10 minutos. Trabajen en grupos de 2-3.

Guía para el mini-proyecto

Paso 1: Definir la pregunta

  • ¿Qué queremos predecir exactamente?
  • ¿Qué métricas son más relevantes para el organismo?

Paso 2: Seleccionar variables

  • ¿Qué variables tienen sentido teórico?
  • ¿Cuáles mostraron relación en la exploración?

Paso 3: Modelar y evaluar

  • Entrenar modelos con validación cruzada
  • Comparar métricas

Paso 4: Interpretar y recomendar

  • ¿Qué variables son más importantes?
  • ¿Qué recomiendanción dar al organismo?

Estructura sugerida para el código

# 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)

Un ejemplo completo

# --- 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.

Presentación de resultados

Al final del laboratorio, cada grupo presentará (3 minutos):

  1. Variables seleccionadas: ¿Por qué eligieron esas?
  2. Modelo elegido: ¿Cuál funcionó mejor?
  3. Hallazgos principales: ¿Qué factores predicen insatisfacción?
  4. Recomendación de política: ¿Qué debería hacer el organismo?

Preparen un resumen verbal!

Cierre del Día 1

Lo que cubrimos hoy

Sesión 1.1: ¿Qué es la IA?

  • Definición y tipos de IA
  • Historia: de Turing a GPT
  • Tipos de aprendizaje automático
  • Ética básica

Sesión 1.2: Fundamentos de ML

  • Flujo de trabajo de ML
  • División y validación
  • Sobreajuste y regularización
  • Métricas de evaluación

Sesión 1.3: Laboratorio 1

  • Ecosistema tidymodels
  • Primer modelo de clasificación
  • Matriz de confusión y métricas

Sesión 1.4: Laboratorio 2

  • Exploración avanzada
  • Feature engineering
  • Comparación de modelos
  • Mini-proyecto aplicado

Para mañana

Día 2: Aprendizaje supervisado

  • Sesión 2.1: Métodos de clasificación
    • Regresión logística en detalle
    • Árboles de decisión
    • Random Forests
  • Sesión 2.2: Regresión y predicción
    • Regularización: LASSO, Ridge, Elastic Net
    • Ingeniería de variables para datos sociales
  • Sesiones 2.3 y 2.4: Laboratorios con datos de Latinobarómetro

Descansen y vengan con energía mañana! 😄

Recursos adicionales


Todos estos recursos están disponibles gratuitamente en línea.

Fin del Día 1 🥳

Apéndices

Apéndice 1: Exploración inicial

# 1. Dimensiones
dim(satisfaccion)
[1] 500  14
# 2. Distribución de la variable objetivo
satisfaccion |>
  count(satisfecho) |>
  mutate(prop = round(n / sum(n), 3))
# A tibble: 2 × 3
  satisfecho     n  prop
  <fct>      <int> <dbl>
1 no           147 0.294
2 si           353 0.706
# 3. Valores faltantes por variable
colSums(is.na(satisfaccion))
                     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 
# 4. Resumen de variables numéricas
satisfaccion |>
  select(where(is.numeric)) |>
  summary()
      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            
# 5. Distribución por país y zona
satisfaccion |> count(pais, sort = TRUE)
# 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
satisfaccion |> count(zona)
# A tibble: 2 × 2
  zona       n
  <fct>  <int>
1 rural    143
2 urbano   357

Volver al Ejercicio 1

Apéndice 2: Más visualizaciones

# 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")

Apéndice 2 (cont.)

# 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")

Apéndice 2 (cont.)

# 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")

# 4. Ingreso vs. educación
ggplot(satisfaccion, aes(x = educacion_anos, y = ingreso_hogar, color = satisfecho)) +
  geom_point(alpha = 0.4) +
  geom_smooth(method = "lm", se = FALSE) +
  scale_color_manual(values = c("#e74c3c", "#27ae60")) +
  labs(title = "Ingreso vs. educación por satisfacción")

Volver al Ejercicio 2

Apéndice 3: Crear más features

# 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
satisfaccion |>
  count(grupo_ingreso)
# A tibble: 3 × 2
  grupo_ingreso     n
  <fct>         <int>
1 bajo            167
2 medio           166
3 alto            167
satisfaccion |>
  count(noticias_alto)
# A tibble: 2 × 2
  noticias_alto     n
  <chr>         <int>
1 alto            359
2 bajo            141

Volver al Ejercicio 3

Apéndice 4: Comparar con features nuevos

# 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.

Volver al Ejercicio 4