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

<br>

[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
```

<br>

:::{.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)

<br>

[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
:::
:::

<br>

[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"}
