---
title: "IA para Científicos Sociales"
subtitle: "Sesión 2.4: Laboratorio - Regresión y regularización"
author:
- name: Danilo Freire
orcid: 0000-0002-4712-6810
email: danilofreire@gmail.com
affiliations: "Departament of Data and Decision Sciences
Emory University"
format:
clean-revealjs:
self-contained: true
footer: "[Sesión 2.4](https://danilofreire.github.io/introduccion-ia-ucu/clases/dia-02/06-laboratorio-04.html)"
transition: slide
transition-speed: default
scrollable: true
execute:
echo: true
revealjs-plugins:
- multimodal
engine: knitr
editor:
render-on-save: true
lang: es
---
```{r setup, include=FALSE}
options(htmltools.dir.version = FALSE)
library(knitr)
opts_chunk$set(
prompt = FALSE,
fig.align = "center",
dpi = 300,
cache = T,
engine.opts = list(bash = "-l")
)
knit_hooks$set(
prompt = function(before, options, envir) {
options(
prompt = if (options$engine %in% c("sh", "bash", "zsh")) "$ " else "R> ",
continue = if (options$engine %in% c("sh", "bash", "zsh")) "$ " else "+ "
)
}
)
options(repos = c(CRAN = "https://cran.rstudio.com/"))
if (!require("fontawesome", character.only = TRUE)) {
install.packages("fontawesome", dependencies = TRUE)
library(fontawesome, character.only = TRUE)
}
```
# Laboratorio 4: Regresión y regularización {background-color="#2d4563"}
## Objetivos del laboratorio
:::{style="margin-top: 30px; font-size: 28px;"}
:::{.columns}
:::{.column width=50%}
**Lo que vamos a hacer:**
1. Predecir satisfacción con la vida
2. Baseline con OLS
3. Ajustar LASSO con validación cruzada
4. Interpretar coeficientes y selección
5. Contrastar con Ridge en una slide
6. Cerrar con una tabla comparativa final
:::
:::{.column width=50%}
**Lo que vamos a aprender:**
- Cuándo usar [regularización]{.alert}
- Diferencias prácticas entre LASSO y Ridge
- [Tuning]{.alert} de penalty con CV
- Trade-off interpretabilidad vs. rendimiento
[Usaremos el mismo dataset de Latinobarómetro (datos simulados).]{.alert}
:::
:::
:::
# Parte 1: Preparación y baseline {background-color="#2d4563"}
## Cargar los paquetes necesarios
:::{style="margin-top: 30px; font-size: 22px;"}
```{r}
#| label: cargar-paquetes
#| message: false
#| warning: false
#| eval: true
# Paquetes necesarios
paquetes <- c("tidyverse", "tidymodels", "glmnet", "ranger", "vip")
for (pkg in paquetes) {
if (!require(pkg, character.only = TRUE)) {
install.packages(pkg, dependencies = TRUE)
library(pkg, character.only = TRUE)
}
}
library(tidymodels)
library(glmnet) # Motor para LASSO, Ridge, Elastic Net
set.seed(2026)
# Cargar el dataset
datos <- read_csv("datos/latinobarometro_sim.csv", show_col_types = FALSE)
glimpse(datos)
```
:::
## Cargar y explorar los datos
:::{style="margin-top: 30px; font-size: 20px;"}
```{r}
#| label: cargar-datos
#| eval: true
# Convertir variables categóricas
datos <- datos |>
mutate(
pais = factor(pais),
zona = factor(zona),
genero = factor(genero),
uso_internet = factor(uso_internet, levels = c("nunca", "semanal", "diario"))
)
# Nuestra variable objetivo
summary(datos$satisfaccion_vida)
# Histograma
ggplot(datos, aes(x = satisfaccion_vida)) +
geom_histogram(binwidth = 1, fill = "#2d4563", color = "white") +
labs(title = "Distribución de satisfacción con la vida",
x = "Satisfacción (1-10)", y = "Frecuencia") +
theme_minimal()
```
:::
## Dividir los datos
:::{style="margin-top: 30px; font-size: 22px;"}
```{r}
#| label: dividir-datos
#| eval: true
# División train/test
division <- initial_split(datos, prop = 0.75)
datos_train <- training(division)
datos_test <- testing(division)
cat("Observaciones en train:", nrow(datos_train), "\n")
cat("Observaciones en test:", nrow(datos_test), "\n")
# Media de la variable objetivo
cat("\nMedia satisfacción (train):", mean(datos_train$satisfaccion_vida))
cat("\nMedia satisfacción (test):", mean(datos_test$satisfaccion_vida))
```
:::
## Receta de preprocesamiento
:::{style="margin-top: 30px; font-size: 20px;"}
```{r}
#| label: crear-receta
#| eval: true
# Definir predictores (excluimos voto y pais)
receta <- recipe(satisfaccion_vida ~ edad + educacion_anios + ingreso_hogar +
zona + genero + confianza_gobierno + confianza_justicia +
satisfaccion_democracia + percepcion_economia +
uso_internet + interes_politica,
data = datos_train) |>
# Dummies para categóricas
step_dummy(all_nominal_predictors()) |>
# Normalizar (importante para regularización)
step_normalize(all_numeric_predictors()) |>
# Eliminar varianza cero
step_zv(all_predictors())
# Verificar
receta |> prep() |> juice() |> glimpse()
```
:::
## Modelo baseline: OLS
:::{style="margin-top: 30px; font-size: 20px;"}
```{r}
#| label: modelo-ols
#| eval: true
# Regresión lineal simple
modelo_ols <- linear_reg() |>
set_engine("lm") |>
set_mode("regression")
# Workflow
wf_ols <- workflow() |>
add_recipe(receta) |>
add_model(modelo_ols)
# Ajustar
ajuste_ols <- fit(wf_ols, data = datos_train)
# Coeficientes
tidy(ajuste_ols) |>
arrange(desc(abs(estimate)))
```
:::
## Evaluar OLS en test
:::{style="margin-top: 30px; font-size: 22px;"}
```{r}
#| label: evaluar-ols
#| eval: true
# Predicciones en test
pred_ols <- predict(ajuste_ols, datos_test) |>
bind_cols(datos_test |> select(satisfaccion_vida))
# Métricas
metricas_ols <- pred_ols |>
metrics(truth = satisfaccion_vida, estimate = .pred)
metricas_ols
# Visualizar predicciones vs. reales
ggplot(pred_ols, aes(x = satisfaccion_vida, y = .pred)) +
geom_point(alpha = 0.5) +
geom_abline(color = "red", linetype = "dashed") +
labs(title = "Predicciones OLS vs. valores reales",
x = "Satisfacción real", y = "Satisfacción predicha") +
theme_minimal()
```
:::
# Parte 2: LASSO con tuning {background-color="#2d4563"}
## ¿Por qué regularizar?
:::{style="margin-top: 30px; font-size: 24px;"}
:::{.columns}
:::{.column width=55%}
**Nuestro caso:**
- 11 predictores, ~375 observaciones en train
- La ratio n/p no es tan extrema
- Pero algunas variables podrían ser irrelevantes
**¿Cuándo regularizar?**
- Muchos predictores relativos a las observaciones
- Sospecha de multicolinealidad
- Queremos [selección automática]{.alert} de variables
- Prevenir sobreajuste
:::
:::{.column width=45%}
:::{style="margin-top: 30px;"}
:::{.callout-note}
**En este ejemplo**
Usamos regularización principalmente para:
1. [Demostrar el método]{.alert}
2. Identificar variables que no aportan
3. Comparar con OLS
En la práctica, con p = 11 y n = 375, OLS probablemente está bien.
:::
:::
:::
:::
:::
## Definir LASSO con penalty a ajustar
:::{style="margin-top: 30px; font-size: 20px;"}
```{r}
#| label: lasso-spec
#| eval: true
# LASSO: mixture = 1
modelo_lasso <- linear_reg(
penalty = tune(), # λ a ajustar
mixture = 1 # 1 = LASSO puro
) |>
set_engine("glmnet") |>
set_mode("regression")
# Workflow
wf_lasso <- workflow() |>
add_recipe(receta) |>
add_model(modelo_lasso)
# Grilla de valores de penalty (escala logarítmica)
grilla_lambda <- grid_regular(
penalty(range = c(-4, 0)), # 10^-4 a 10^0 = 0.0001 a 1
levels = 30
)
head(grilla_lambda)
```
:::
## Validación cruzada para LASSO
:::{style="margin-top: 30px; font-size: 20px;"}
```{r}
#| label: lasso-cv
#| eval: true
#| cache: true
# 10-fold CV
folds <- vfold_cv(datos_train, v = 10)
# Ajustar todas las lambdas
resultados_lasso <- tune_grid(
wf_lasso,
resamples = folds,
grid = grilla_lambda,
metrics = metric_set(rmse, rsq, mae)
)
# Ver resultados
resultados_lasso |>
collect_metrics() |>
filter(.metric == "rmse") |>
arrange(mean) |>
head(10)
```
:::
## Visualizar el tuning de LASSO
:::{style="margin-top: 30px; font-size: 22px;"}
```{r}
#| label: lasso-viz
#| eval: true
#| fig-width: 10
#| fig-height: 5
# Gráfico de RMSE vs. penalty
autoplot(resultados_lasso) +
scale_x_log10() +
theme_minimal() +
labs(title = "Tuning de LASSO: RMSE vs. penalty (λ)")
```
:::
## Ejercicio 1: Efecto del λ en la selección {#sec:exercise01}
:::{style="margin-top: 30px; font-size: 22px;"}
**Instrucciones:**
1. Ajustar LASSO con varios valores de λ: 0.001, 0.01, 0.1, 0.5 y 1
2. Contar cuántas variables se eliminan en cada caso
3. ¿Qué pasa con un λ muy grande? ¿Y con uno muy pequeño?
*Pista:* usen `map_df()` con una función que ajuste el workflow para cada lambda y cuente los coeficientes iguales a cero con `tidy()`.
[Tómense 5 minutos para experimentar.]{.alert}
[[Apéndice 1: Solución]{.button}](#sec:appendix01)
:::
## Seleccionar λ y ajustar LASSO final
:::{style="margin-top: 30px; font-size: 18px;"}
```{r}
#| label: lasso-final
#| eval: true
# λ mínimo vs. λ 1SE (modelo más simple dentro de 1 error estándar)
lambda_min <- select_best(resultados_lasso, metric = "rmse")
lambda_1se <- select_by_one_std_err(resultados_lasso, metric = "rmse",
desc(penalty))
cat("Lambda mínimo:", lambda_min$penalty,
" | Lambda 1SE:", lambda_1se$penalty, "\n")
# Usamos lambda mínimo y ajustamos con todo el training
wf_lasso_final <- finalize_workflow(wf_lasso, lambda_min)
ajuste_lasso <- fit(wf_lasso_final, data = datos_train)
# Coeficientes y variables eliminadas
coef_lasso <- tidy(ajuste_lasso) |>
filter(term != "(Intercept)") |>
arrange(desc(abs(estimate)))
coef_lasso
cat("\nVariables eliminadas:", sum(coef_lasso$estimate == 0),
"de", nrow(coef_lasso))
```
:::
## Comparar coeficientes OLS vs. LASSO
:::{style="margin-top: 30px; font-size: 18px;"}
```{r}
#| label: comparar-coef
#| eval: true
#| fig-width: 10
#| fig-height: 5
# Extraer coeficientes de ambos
coef_ols <- tidy(ajuste_ols) |>
filter(term != "(Intercept)") |>
select(term, estimate_ols = estimate)
coef_lasso_comp <- tidy(ajuste_lasso) |>
filter(term != "(Intercept)") |>
select(term, estimate_lasso = estimate)
# Combinar
comparacion <- left_join(coef_ols, coef_lasso_comp, by = "term") |>
pivot_longer(cols = starts_with("estimate"),
names_to = "modelo", values_to = "coef") |>
mutate(modelo = ifelse(modelo == "estimate_ols", "OLS", "LASSO"))
# Visualizar
ggplot(comparacion, aes(x = reorder(term, abs(coef)), y = coef, fill = modelo)) +
geom_col(position = "dodge") +
coord_flip() +
scale_fill_manual(values = c("OLS" = "#3498DB", "LASSO" = "#E74C3C")) +
labs(title = "Comparación de coeficientes: OLS vs. LASSO",
x = "Variable", y = "Coeficiente (normalizado)") +
theme_minimal()
```
:::
## Evaluar LASSO en test
:::{style="margin-top: 30px; font-size: 22px;"}
```{r}
#| label: evaluar-lasso
#| eval: true
# Predicciones
pred_lasso <- predict(ajuste_lasso, datos_test) |>
bind_cols(datos_test |> select(satisfaccion_vida))
# Métricas
metricas_lasso <- pred_lasso |>
metrics(truth = satisfaccion_vida, estimate = .pred)
# Comparar con OLS
cat("OLS:\n")
print(metricas_ols)
cat("\nLASSO:\n")
print(metricas_lasso)
```
:::
# Parte 3: Ridge y cierre {background-color="#2d4563"}
## Ridge regression
:::{style="margin-top: 30px; font-size: 18px;"}
```{r}
#| label: ridge
#| eval: true
#| cache: true
# Ridge: mixture = 0 (LASSO usa mixture = 1)
modelo_ridge <- linear_reg(penalty = tune(), mixture = 0) |>
set_engine("glmnet") |>
set_mode("regression")
wf_ridge <- workflow() |>
add_recipe(receta) |>
add_model(modelo_ridge)
# Reutilizamos los folds y la grilla de lambda de LASSO
resultados_ridge <- tune_grid(
wf_ridge, resamples = folds, grid = grilla_lambda,
metrics = metric_set(rmse)
)
lambda_ridge <- select_best(resultados_ridge, metric = "rmse")
ajuste_ridge <- finalize_workflow(wf_ridge, lambda_ridge) |>
fit(data = datos_train)
# Predicciones y métricas en test
pred_ridge <- predict(ajuste_ridge, datos_test) |>
bind_cols(datos_test |> select(satisfaccion_vida))
metricas_ridge <- pred_ridge |>
metrics(truth = satisfaccion_vida, estimate = .pred)
# Ridge nunca elimina variables (coefs se achican, no se anulan)
cat("Variables con coef = 0 en Ridge:",
sum(tidy(ajuste_ridge)$estimate[-1] == 0), "\n\n")
metricas_ridge
```
- LASSO [selecciona]{.alert} (achica y anula); Ridge [encoge]{.alert} todos los coeficientes
- Para una versión intermedia (Elastic Net), ver [Apéndice 4](#sec:appendix04)
:::
# Parte 4: Comparación final {background-color="#2d4563"}
## Tabla comparativa en test
:::{style="margin-top: 30px; font-size: 20px;"}
```{r}
#| label: tabla-final
#| eval: true
# Combinar métricas de los tres modelos lineales
tabla_comparacion <- bind_rows(
metricas_ols |> mutate(modelo = "OLS"),
metricas_lasso |> mutate(modelo = "LASSO"),
metricas_ridge |> mutate(modelo = "Ridge")
) |>
select(modelo, .metric, .estimate) |>
pivot_wider(names_from = .metric, values_from = .estimate) |>
arrange(rmse)
tabla_comparacion
```
:::{.callout-note}
Random Forest y Elastic Net están en los apéndices con código listo para correr en casa. En nuestras pruebas, RF reduce el RMSE ~0.15 puntos; Elastic Net se parece mucho a LASSO.
:::
:::
## Visualización de predicciones
:::{style="margin-top: 30px; font-size: 18px;"}
```{r}
#| label: viz-pred
#| eval: true
#| fig-width: 12
#| fig-height: 4.5
# Combinar predicciones de los tres modelos lineales
todas_pred <- bind_rows(
pred_ols |> mutate(modelo = "OLS"),
pred_lasso |> mutate(modelo = "LASSO"),
pred_ridge |> mutate(modelo = "Ridge")
)
ggplot(todas_pred, aes(x = satisfaccion_vida, y = .pred)) +
geom_point(alpha = 0.4, color = "#2d4563") +
geom_abline(color = "red", linetype = "dashed") +
facet_wrap(~modelo, nrow = 1) +
labs(title = "Predicciones vs. valores reales",
x = "Satisfacción real", y = "Satisfacción predicha") +
theme_minimal()
```
:::
## Discusión: ¿Qué modelo elegir?
:::{style="margin-top: 30px; font-size: 24px;"}
:::{.columns}
:::{.column width=50%}
**Observaciones:**
- Los tres modelos lineales dan resultados muy similares
- LASSO eliminó algunas variables sin perder RMSE
- La mejora de regularización sobre OLS es [marginal]{.alert}
**¿Por qué?**
- El dataset tiene relaciones aproximadamente lineales
- No hay muchas variables irrelevantes
- n/p no es extremo
:::
:::{.column width=50%}
**Recomendación:**
- Si quiero [interpretabilidad]{.alert}: OLS o LASSO
- Si quiero [selección de variables]{.alert}: LASSO
- Si quiero [mejor predicción]{.alert}: ver Random Forest en el [Apéndice 5](#sec:appendix05)
[En este caso, OLS y LASSO dan casi el mismo error. La regularización brilla cuando p/n es grande o hay colinealidad fuerte.]{.alert}
:::
:::
:::
## Resumen del laboratorio
:::{style="margin-top: 30px; font-size: 24px;"}
:::{.columns}
:::{.column width=50%}
**Lo que practicamos:**
- Regularización con LASSO y Ridge
- Tuning de λ con CV
- Comparación con OLS en test
- Interpretación de coeficientes
:::
:::{.column width=50%}
**Conceptos clave:**
- [mixture = 1]{.alert}: LASSO (selecciona)
- [mixture = 0]{.alert}: Ridge (reduce)
- [Validación cruzada]{.alert} para elegir λ
- [λ 1SE]{.alert} para modelos más simples
- Apéndices: Elastic Net, Random Forest, ejercicios extra
:::
:::
[En el próximo día veremos aprendizaje no supervisado y análisis de texto.]{.alert}
:::
# Apéndice: Soluciones {background-color="#2d4563"}
## Apéndice 1: Efecto del λ {#sec:appendix01}
:::{style="margin-top: 20px; font-size: 20px;"}
```{r ap1-sol, prompt=FALSE, echo=TRUE, eval=TRUE}
# Probar varios valores de lambda
lambdas <- c(0.001, 0.01, 0.1, 0.5, 1)
# Para cada lambda, ajustar y contar variables eliminadas
resultados_lambda <- map_df(lambdas, function(l) {
modelo <- linear_reg(penalty = l, mixture = 1) |>
set_engine("glmnet") |>
set_mode("regression")
ajuste <- workflow() |>
add_recipe(receta) |>
add_model(modelo) |>
fit(data = datos_train)
coefs <- tidy(ajuste) |> filter(term != "(Intercept)")
n_eliminadas <- sum(coefs$estimate == 0)
tibble(lambda = l, vars_eliminadas = n_eliminadas, vars_total = nrow(coefs))
})
resultados_lambda
```
- Con [λ muy pequeño]{.alert} (0.001), LASSO se comporta casi como OLS: no elimina variables
- Con [λ muy grande]{.alert} (1), LASSO elimina la mayoría o todas las variables
- El λ óptimo balancea entre sesgo (simplificación) y varianza (sobreajuste)
[[Volver al ejercicio]{.button}](#sec:exercise01)
:::
## Apéndice 2: Interacciones {#sec:appendix02}
:::{style="margin-top: 20px; font-size: 20px;"}
```{r ap2-sol, prompt=FALSE, echo=TRUE, eval=TRUE}
# Receta con interacciones
receta_interact <- recipe(satisfaccion_vida ~ edad + educacion_anios + ingreso_hogar +
zona + genero + confianza_gobierno + confianza_justicia +
satisfaccion_democracia + percepcion_economia +
uso_internet + interes_politica,
data = datos_train) |>
step_dummy(all_nominal_predictors()) |>
step_interact(terms = ~ edad:educacion_anios) |>
step_normalize(all_numeric_predictors()) |>
step_zv(all_predictors())
# Ajustar LASSO con la receta de interacciones
wf_lasso_interact <- workflow() |>
add_recipe(receta_interact) |>
add_model(modelo_lasso)
resultados_interact <- tune_grid(
wf_lasso_interact, resamples = folds,
grid = grilla_lambda, metrics = metric_set(rmse)
)
# Mejor lambda y modelo final
mejor_interact <- select_best(resultados_interact, metric = "rmse")
ajuste_interact <- finalize_workflow(wf_lasso_interact, mejor_interact) |>
fit(data = datos_train)
# ¿La interaccion sobrevive?
tidy(ajuste_interact) |>
filter(term != "(Intercept)") |>
arrange(desc(abs(estimate)))
```
- Si la interacción tiene [coeficiente distinto de cero]{.alert}, LASSO la considera útil
- La mejora en RMSE suele ser pequeña porque la relación edad-educación ya se captura parcialmente por separado
- Agregar interacciones aumenta el número de predictores, lo que da más trabajo al regularizador
:::
## Apéndice 3: Países {#sec:appendix03}
:::{style="margin-top: 20px; font-size: 20px;"}
```{r ap3-sol, prompt=FALSE, echo=TRUE, eval=TRUE}
# Receta que incluye pais
receta_pais <- recipe(satisfaccion_vida ~ ., data = datos_train) |>
step_rm(voto) |>
step_dummy(all_nominal_predictors()) |>
step_normalize(all_numeric_predictors()) |>
step_zv(all_predictors())
# Ajustar LASSO
wf_lasso_pais <- workflow() |>
add_recipe(receta_pais) |>
add_model(modelo_lasso)
resultados_pais <- tune_grid(
wf_lasso_pais, resamples = folds,
grid = grilla_lambda, metrics = metric_set(rmse)
)
mejor_pais <- select_best(resultados_pais, metric = "rmse")
ajuste_pais <- finalize_workflow(wf_lasso_pais, mejor_pais) |>
fit(data = datos_train)
# ¿Qué países sobreviven?
coef_pais <- tidy(ajuste_pais) |>
filter(str_detect(term, "pais"), estimate != 0) |>
arrange(desc(abs(estimate)))
cat("Países con coeficiente distinto de cero:", nrow(coef_pais), "\n")
coef_pais
```
- LASSO [selecciona automáticamente]{.alert} los países que aportan información predictiva
- Con muchas dummies de país, la regularización es más útil que en el modelo base
- Los países con coeficientes positivos tienen mayor satisfacción (controlando por las otras variables)
:::
## Apéndice 4: Elastic Net {#sec:appendix04}
:::{style="margin-top: 20px; font-size: 18px;"}
```{r ap4-enet, prompt=FALSE, echo=TRUE, eval=TRUE, cache=TRUE}
# Elastic Net: combina L1 (LASSO) y L2 (Ridge)
# Ajustamos penalty y mixture simultáneamente
modelo_enet <- linear_reg(penalty = tune(), mixture = tune()) |>
set_engine("glmnet") |>
set_mode("regression")
wf_enet <- workflow() |>
add_recipe(receta) |>
add_model(modelo_enet)
# Grilla 2D: 15 lambdas x 5 valores de mixture
grilla_enet <- grid_regular(
penalty(range = c(-4, 0)),
mixture(range = c(0, 1)),
levels = c(15, 5)
)
resultados_enet <- tune_grid(
wf_enet, resamples = folds, grid = grilla_enet,
metrics = metric_set(rmse)
)
mejor_enet <- select_best(resultados_enet, metric = "rmse")
mejor_enet
# Ajustar modelo final
ajuste_enet <- finalize_workflow(wf_enet, mejor_enet) |>
fit(data = datos_train)
pred_enet <- predict(ajuste_enet, datos_test) |>
bind_cols(datos_test |> select(satisfaccion_vida))
pred_enet |> metrics(truth = satisfaccion_vida, estimate = .pred)
```
- Elastic Net se sitúa entre LASSO (mixture = 1) y Ridge (mixture = 0)
- Útil cuando hay [grupos de predictores correlacionados]{.alert}: LASSO tiende a elegir solo uno, Ridge los encoge a todos, Elastic Net captura un balance
- En este dataset el RMSE queda casi idéntico al de LASSO; el método brilla con dimensiones mucho más altas
:::
## Apéndice 5: Random Forest para regresión {#sec:appendix05}
:::{style="margin-top: 20px; font-size: 18px;"}
```{r ap5-rf, prompt=FALSE, echo=TRUE, eval=TRUE, cache=TRUE}
# Random Forest para regresión (mode = "regression")
modelo_rf <- rand_forest(
trees = 500,
mtry = tune(),
min_n = tune()
) |>
set_engine("ranger") |>
set_mode("regression")
wf_rf <- workflow() |>
add_recipe(receta) |>
add_model(modelo_rf)
grilla_rf <- grid_regular(
mtry(range = c(2, 8)),
min_n(range = c(5, 20)),
levels = c(4, 4)
)
resultados_rf <- tune_grid(
wf_rf, resamples = folds, grid = grilla_rf,
metrics = metric_set(rmse)
)
mejor_rf <- select_best(resultados_rf, metric = "rmse")
ajuste_rf <- finalize_workflow(wf_rf, mejor_rf) |>
fit(data = datos_train)
pred_rf <- predict(ajuste_rf, datos_test) |>
bind_cols(datos_test |> select(satisfaccion_vida))
metricas_rf <- pred_rf |>
metrics(truth = satisfaccion_vida, estimate = .pred)
metricas_rf
```
- RF [reduce el RMSE]{.alert} unas décimas frente a los modelos lineales
- El costo es interpretabilidad: sin coeficientes, hay que usar VIP y PDP
- En ciencias sociales, pesa la pregunta: ¿importa entender los coeficientes o predecir lo mejor posible?
:::
# Fin del día 2! 🤓 {background-color="#2d4563"}