library(tidymodels)
library(tidyverse)
satisfaccion <- read_csv("datos/satisfaccion_democracia.csv")
# Lean el archivo directo desde la web:
# satisfaccion <- read_csv("https://raw.githubusercontent.com/danilofreire/introduccion-ia-ucu/main/clases/dia-01/datos/satisfaccion_democracia.csv")
satisfaccion <- satisfaccion |>
mutate(
satisfecho = factor(satisfecho, levels = c("no", "si")),
zona = factor(zona),
genero = factor(genero),
pais = factor(pais)
)Tarea 2: Comparación de modelos – Respuestas
IA para Científicos Sociales - UCU
1 Instrucciones
Esta es la clave de respuestas de la Tarea 2. Cada pregunta incluye el código R completo y una respuesta escrita.
1.1 Configuración
2 Exploración
2.1 Pregunta 1: Resumen del dataset
Calculen la proporción de satisfechos ("si") por país. ¿Cuál país tiene la proporción más alta? ¿Cuál tiene la más baja?
prop_pais <- satisfaccion |>
count(pais, satisfecho) |>
group_by(pais) |>
mutate(prop = round(n / sum(n), 3)) |>
filter(satisfecho == "si") |>
arrange(desc(prop))
prop_pais# A tibble: 18 × 4
# Groups: pais [18]
pais satisfecho n prop
<fct> <fct> <int> <dbl>
1 México si 17 0.895
2 El Salvador si 23 0.852
3 Honduras si 23 0.852
4 Costa Rica si 26 0.812
5 Brasil si 17 0.81
6 Ecuador si 12 0.8
7 Uruguay si 24 0.774
8 Paraguay si 16 0.696
9 Nicaragua si 22 0.688
10 Venezuela si 26 0.684
11 Argentina si 17 0.68
12 República Dominicana si 20 0.667
13 Colombia si 17 0.654
14 Perú si 17 0.654
15 Panamá si 24 0.632
16 Chile si 19 0.594
17 Bolivia si 16 0.571
18 Guatemala si 17 0.567
Respuesta: México tiene la proporción más alta de satisfechos (~0.90) y Guatemala la más baja (~0.57). La proporción varía bastante entre países, lo cual refleja las diferencias en los factores subyacentes (confianza en el gobierno, educación, etc.) en cada país. Las diferencias entre países son en parte producto de la simulación aleatoria, pero se observa variabilidad que podría existir en datos reales de Latinobarómetro.
2.2 Pregunta 2: Confianza y satisfacción
Creen un boxplot que muestre la distribución de confianza_gobierno separada por satisfecho.
ggplot(satisfaccion, aes(x = satisfecho, y = confianza_gobierno, fill = satisfecho)) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = c("#e74c3c", "#27ae60")) +
labs(
title = "Confianza en el gobierno por nivel de satisfacción",
x = "Satisfecho con la democracia",
y = "Confianza en el gobierno (1-10)"
) +
theme_minimal() +
theme(legend.position = "none")
Respuesta: Los encuestados satisfechos con la democracia tienden a tener mayor confianza en el gobierno. La mediana de confianza es visiblemente más alta en el grupo satisfecho. Esto tiene sentido: quien confía más en el gobierno probablemente evalúa mejor el funcionamiento de la democracia.
2.3 Pregunta 3: Correlaciones
Calculen la matriz de correlaciones entre las 4 variables numéricas principales.
mat_cor <- satisfaccion |>
select(edad, educacion_anos, ingreso_hogar, confianza_gobierno) |>
cor() |>
round(3)
mat_cor edad educacion_anos ingreso_hogar confianza_gobierno
edad 1.000 0.012 0.030 -0.009
educacion_anos 0.012 1.000 -0.035 -0.077
ingreso_hogar 0.030 -0.035 1.000 -0.082
confianza_gobierno -0.009 -0.077 -0.082 1.000
Respuesta: Las correlaciones entre estas variables son muy débiles (todos los valores absolutos por debajo de 0.1). Esto indica que cada variable aporta información casi independiente, lo cual es bueno para un modelo de regresión: prácticamente no hay multicolinealidad. En una encuesta de opinión como esta, no sorprende que edad, educación, ingreso y confianza apenas se correlacionen entre sí.
3 Feature engineering
3.1 Pregunta 4: Crear variables nuevas
satisfaccion <- satisfaccion |>
mutate(
confianza_baja = if_else(confianza_gobierno <= 4, "si", "no"),
joven = if_else(edad < 30, "si", "no"),
ingreso_x_educacion = ingreso_hogar * educacion_anos
)
satisfaccion |>
select(satisfecho, confianza_baja, joven, ingreso_x_educacion) |>
head(10)# A tibble: 10 × 4
satisfecho confianza_baja joven ingreso_x_educacion
<fct> <chr> <chr> <dbl>
1 si no no 6960
2 si no no 19872
3 no no no 12270
4 si si no 2842
5 si no no 4648
6 no si no 3430
7 si no no 4466
8 si si si 13095
9 si no no 2660
10 no no no 4515
3.2 Pregunta 5: Explorar las nuevas variables
# Proporción de satisfechos por confianza baja/alta
satisfaccion |>
count(confianza_baja, satisfecho) |>
group_by(confianza_baja) |>
mutate(prop = round(n / sum(n), 3)) |>
filter(satisfecho == "si")# A tibble: 2 × 4
# Groups: confianza_baja [2]
confianza_baja satisfecho n prop
<chr> <fct> <int> <dbl>
1 no si 194 0.808
2 si si 159 0.612
# Proporción de satisfechos entre jóvenes vs. no jóvenes
satisfaccion |>
count(joven, satisfecho) |>
group_by(joven) |>
mutate(prop = round(n / sum(n), 3)) |>
filter(satisfecho == "si")# A tibble: 2 × 4
# Groups: joven [2]
joven satisfecho n prop
<chr> <fct> <int> <dbl>
1 no si 283 0.711
2 si si 70 0.686
Respuesta: La variable confianza_baja muestra una diferencia mucho mayor en la proporción de satisfechos que joven. Las personas con confianza baja en el gobierno tienen una proporción de satisfacción mucho menor que las de confianza alta. La edad, en cambio, muestra diferencias más modestas. Esto sugiere que confianza_baja será un predictor más útil para el modelo.
4 Modelos
4.1 Pregunta 6: División de datos
set.seed(42)
datos_split <- initial_split(satisfaccion, prop = 0.75, strata = satisfecho)
datos_train <- training(datos_split)
datos_test <- testing(datos_split)
cat("Entrenamiento:", nrow(datos_train), "observaciones\n")Entrenamiento: 374 observaciones
cat("Prueba:", nrow(datos_test), "observaciones\n")Prueba: 126 observaciones
# Verificar proporciones
cat("\nProporciones en entrenamiento:\n")
Proporciones en entrenamiento:
datos_train |>
count(satisfecho) |>
mutate(prop = round(n / sum(n), 3))# A tibble: 2 × 3
satisfecho n prop
<fct> <int> <dbl>
1 no 110 0.294
2 si 264 0.706
cat("\nProporciones en prueba:\n")
Proporciones en prueba:
datos_test |>
count(satisfecho) |>
mutate(prop = round(n / sum(n), 3))# A tibble: 2 × 3
satisfecho n prop
<fct> <int> <dbl>
1 no 37 0.294
2 si 89 0.706
4.2 Pregunta 7: Dos modelos con fórmula básica
# Definir modelos
modelo_logistico <- logistic_reg() |>
set_engine("glm") |>
set_mode("classification")
modelo_arbol <- decision_tree() |>
set_engine("rpart") |>
set_mode("classification")
# Folds
set.seed(42)
folds <- vfold_cv(datos_train, v = 5, strata = satisfecho)
# Fórmula básica
formula_basica <- satisfecho ~ edad + educacion_anos + ingreso_hogar +
confianza_gobierno + consumo_noticias +
participacion_politica + zona
# Evaluar cada modelo con validación cruzada (inline, sin función auxiliar)
eval_log <- fit_resamples(
modelo_logistico, formula_basica, resamples = folds,
metrics = metric_set(accuracy),
control = control_resamples(event_level = "second")
) |>
collect_metrics() |> mutate(modelo = "Logístico")
eval_arb <- fit_resamples(
modelo_arbol, formula_basica, resamples = folds,
metrics = metric_set(accuracy),
control = control_resamples(event_level = "second")
) |>
collect_metrics() |> mutate(modelo = "Árbol")
bind_rows(eval_log, eval_arb) |>
select(modelo, .metric, mean, std_err) |>
arrange(desc(mean))# A tibble: 2 × 4
modelo .metric mean std_err
<chr> <chr> <dbl> <dbl>
1 Logístico accuracy 0.706 0.0114
2 Árbol accuracy 0.628 0.00898
Respuesta: La regresión logística supera al árbol de decisión en este dataset (≈0.71 vs. ≈0.63 de accuracy). Con predictores cuya relación con el outcome es aproximadamente lineal, la regresión logística tiene ventaja; el árbol, que parte el espacio en cortes y umbrales, capta peor esa estructura suave. Nota: la accuracy del logístico (≈0.71) coincide casi con la proporción de la clase mayoritaria, así que conviene mirar también precision, recall y AUC, no solo la accuracy.
4.3 Pregunta 8: Mejor modelo con fórmula extendida
# Fórmula extendida
formula_ext <- satisfecho ~ edad + educacion_anos + ingreso_hogar +
confianza_gobierno + consumo_noticias +
participacion_politica + zona +
confianza_baja + ingreso_x_educacion
# Evaluar el modelo logístico con la fórmula extendida (inline, sin función auxiliar)
eval_log_ext <- fit_resamples(
modelo_logistico, formula_ext, resamples = folds,
metrics = metric_set(accuracy),
control = control_resamples(event_level = "second")
) |>
collect_metrics() |> mutate(modelo = "Logístico (ext)")
# Comparar
cat("Accuracy fórmula básica:\n")Accuracy fórmula básica:
eval_log |> select(modelo, mean, std_err)# A tibble: 1 × 3
modelo mean std_err
<chr> <dbl> <dbl>
1 Logístico 0.706 0.0114
cat("\nAccuracy fórmula extendida:\n")
Accuracy fórmula extendida:
eval_log_ext |> select(modelo, mean, std_err)# A tibble: 1 × 3
modelo mean std_err
<chr> <dbl> <dbl>
1 Logístico (ext) 0.722 0.00821
cat("\nDiferencia:", round(eval_log_ext$mean - eval_log$mean, 4), "\n")
Diferencia: 0.016
Respuesta: La diferencia en accuracy entre la fórmula básica y la extendida es generalmente pequeña. Esto sugiere que las variables originales ya capturan la mayor parte de la información relevante. La variable confianza_baja es una versión discretizada de confianza_gobierno (que ya está en el modelo), por lo que no agrega información nueva. El producto ingreso_x_educacion podría capturar una interacción, pero su efecto es marginal.
5 Evaluación
5.1 Pregunta 9: Matriz de confusión
# Ajustar modelo logístico
ajuste <- modelo_logistico |>
fit(formula_basica, data = datos_train)
# Predicciones
pred_test <- ajuste |>
predict(datos_test) |>
bind_cols(datos_test)
# Matriz de confusión
mc <- conf_mat(pred_test, truth = satisfecho, estimate = .pred_class)
mc Truth
Prediction no si
no 5 5
si 32 84
# Total de errores
tabla <- mc$table
errores <- tabla["si", "no"] + tabla["no", "si"]
cat("Total de errores:", errores, "de", nrow(datos_test), "observaciones\n")Total de errores: 37 de 126 observaciones
cat("Tasa de error:", round(errores / nrow(datos_test), 3), "\n")Tasa de error: 0.294
Respuesta: El modelo comete 37 errores en las 126 observaciones del conjunto de prueba (tasa de error ≈ 0.29), repartidos entre falsos positivos y falsos negativos. Clasifica correctamente la mayoría de las observaciones, aunque no es perfecto. Los errores son esperables dado que la satisfacción democrática depende de muchos factores que no están en el modelo.
5.2 Pregunta 10: Precisión, recall y AUC
# augment() junta predicciones (clases + probabilidades) con los datos
# originales en una sola línea
pred_probs <- ajuste |> augment(datos_test)
# Precision
prec <- pred_probs |>
precision(truth = satisfecho, estimate = .pred_class,
event_level = "second")
# Recall
rec <- pred_probs |>
recall(truth = satisfecho, estimate = .pred_class,
event_level = "second")
# AUC
auc <- pred_probs |>
roc_auc(truth = satisfecho, .pred_si, event_level = "second")
cat("Precision:", round(prec$.estimate, 3), "\n")Precision: 0.724
cat("Recall:", round(rec$.estimate, 3), "\n")Recall: 0.944
cat("AUC:", round(auc$.estimate, 3), "\n")AUC: 0.701
Respuesta: La precisión es ≈0.72, el recall ≈0.94 y el AUC ≈0.70. Un AUC entre 0.70 y 0.80 se considera aceptable, entre 0.80 y 0.90 bueno, y superior a 0.90 excelente: el nuestro queda justo en el límite de lo aceptable. Para un modelo con variables sociodemográficas como las nuestras es un resultado razonable, dado que la satisfacción democrática es un fenómeno complejo que depende de muchos factores no observados. La Pregunta 14 vuelve sobre qué tan bueno es realmente este modelo.
5.3 Pregunta 11: Curva ROC
roc_data <- pred_probs |>
roc_curve(truth = satisfecho, .pred_si, event_level = "second")
autoplot(roc_data) +
labs(title = "Curva ROC - Modelo logístico para satisfacción democrática")
5.4 Pregunta 12: Coeficientes del modelo
coefs <- tidy(ajuste) |>
mutate(odds_ratio = round(exp(estimate), 3)) |>
arrange(p.value)
coefs |>
select(term, estimate, std.error, p.value, odds_ratio)# A tibble: 8 × 5
term estimate std.error p.value odds_ratio
<chr> <dbl> <dbl> <dbl> <dbl>
1 confianza_gobierno 0.256 0.0611 0.0000281 1.29
2 participacion_politica 0.0178 0.00543 0.00103 1.02
3 (Intercept) -1.58 0.658 0.0164 0.206
4 zonaurbano 0.545 0.257 0.0341 1.72
5 edad 0.0144 0.00847 0.0895 1.01
6 consumo_noticias 0.0354 0.0226 0.118 1.04
7 ingreso_hogar -0.000199 0.000159 0.212 1
8 educacion_anos -0.0329 0.0305 0.281 0.968
Respuesta: La variable con el efecto más fuerte es confianza_gobierno (p < 0.001, odds ratio ≈ 1.29): cada punto adicional de confianza multiplica las chances de satisfacción por ~1.29. Las más débiles son educacion_anos e ingreso_hogar (p > 0.2, odds ratios ≈ 1), que no muestran relación clara con la satisfacción una vez controladas las demás variables. El orden de importancia de los predictores tiene sentido teórico: la confianza en el gobierno es el predictor más directo de la satisfacción con la democracia.
6 Reflexión
6.1 Pregunta 13: Árbol vs. logística
Respuesta: La regresión logística y los árboles de decisión tienen fortalezas complementarias:
Regresión logística:
- Mejor cuando la relación entre predictores y outcome es aproximadamente lineal (en la escala logit)
- Produce coeficientes interpretables: cada variable tiene un odds ratio que se puede comunicar a audiencias no técnicas
- Tiende a generalizar mejor cuando hay pocos datos, porque tiene menos parámetros
- Preferible cuando el objetivo es entender qué factores influyen y en qué dirección
Árbol de decisión:
- Captura relaciones no lineales e interacciones de forma automática, sin necesidad de especificarlas
- Muy fácil de interpretar visualmente: se puede mostrar el árbol completo
- Funciona bien con variables categóricas sin necesidad de crear dummies
- Preferible cuando la estructura de los datos es jerárquica (por ejemplo, la edad importa solo para personas con baja educación)
En esta tarea, la regresión logística funciona igual o mejor que el árbol porque la relación entre los predictores y la satisfacción es aproximadamente lineal. El árbol tiende a sobreajustar (cortes arbitrarios) con datasets de este tamaño. Para un organismo que necesita recomendaciones claras de política, la regresión logística es preferible porque los coeficientes indican directamente qué factores modificar.
6.2 Pregunta 14: Una accuracy que engaña
Miren de nuevo la accuracy del modelo logístico (Pregunta 7) y la proporción de personas satisfechas en los datos (el count(satisfecho)). Van a notar que son casi iguales. Sin escribir código nuevo, expliquen: ¿qué significa que la accuracy del modelo sea casi idéntica a la proporción de la clase mayoritaria? ¿El modelo realmente “aprendió” algo? ¿Qué métricas mirarían para responder eso mejor que la accuracy?
Respuesta: Alrededor del 71% de las personas están satisfechas, así que un “modelo” que prediga siempre “satisfecho” ya acertaría el 71% de las veces sin mirar ningún predictor. Que la accuracy del modelo logístico (≈0.71) coincida casi exactamente con esa proporción es una señal de alarma: medido por accuracy, el modelo apenas mejora sobre adivinar la clase mayoritaria.
Eso no significa que el modelo sea inútil, sino que la accuracy es la métrica equivocada para juzgarlo acá. El AUC (≈0.70) muestra que sí hay algo de capacidad para ordenar a las personas por su probabilidad de estar satisfechas, y el recall alto indica que recupera a la mayoría de los satisfechos (a costa de muchos falsos positivos, como se ve en la matriz de confusión). La lección es la de “La paradoja de la precisión”: con clases desbalanceadas, la accuracy hay que leerla siempre contra la línea base, y conviene apoyarse en precisión, recall y AUC para saber si el modelo realmente aprendió.