Apunte 09 — El paquete broom

Analítica de Personas · Semestre otoño 2026 · Semana 3 · Referencia

1. ¿Qué problema resuelve broom?

Los modelos estadísticos en R (lm(), glm(), t.test(), etc.) devuelven objetos complejos con estructuras inconsistentes. broom los convierte en tibbles limpios y predecibles que puedes filtrar, graficar y exportar directamente.

❌ Sin broom

summary(modelo)$coefficients
# Devuelve una matrix
# Columnas con nombres inconsistentes
# No incluye intervalos de confianza
# Los coeficientes están en log-odds
# No se puede usar con ggplot o dplyr

✅ Con broom

tidy(modelo, exponentiate = TRUE,
     conf.int = TRUE)
# Devuelve un tibble
# Columnas estándar siempre iguales
# Con intervalos de confianza
# Odds ratios directos
# Se integra con ggplot y dplyr
library(broom)
# broom se instala automáticamente con tidyverse
# pero hay que cargarlo explícitamente

2. Las tres funciones principales

broom tiene tres funciones que se aplican a prácticamente cualquier modelo en R:

Función¿Qué devuelve?Una fila por...Uso principal
tidy()Coeficientes del modeloCada variable/términoTabla de odds ratios, forest plots
glance()Métricas globales del modeloEl modelo completo (1 fila)Comparar modelos (AIC, BIC, deviance)
augment()Datos originales + prediccionesCada observaciónDiagnósticos, residuos, valores predichos

3. tidy(): coeficientes del modelo

Uso básico

modelo <- glm(rotacion_bin ~ satisfaccion_laboral + edad + antiguedad_anios,
              data = innovaco, family = binomial)

# Coeficientes en log-odds (poco útil para comunicar)
tidy(modelo)
# # A tibble: 4 × 5
#   term                  estimate std.error statistic  p.value
#   (Intercept)             1.234     0.456     2.707   0.0068
#   satisfaccion_laboral   -0.432     0.078    -5.538   3.1e-8
#   edad                   -0.021     0.009    -2.333   0.0196
#   antiguedad_anios       -0.145     0.034    -4.265   2.0e-5

Con odds ratios e intervalos de confianza

# ESTO es lo que quieres para comunicar resultados
tidy(modelo, exponentiate = TRUE, conf.int = TRUE)
# # A tibble: 4 × 7
#   term                  estimate std.error statistic  p.value conf.low conf.high
#   (Intercept)             3.435     0.456     2.707   0.0068    1.405     8.397
#   satisfaccion_laboral    0.649     0.078    -5.538   3.1e-8    0.557     0.757
#   edad                    0.979     0.009    -2.333   0.0196    0.961     0.997
#   antiguedad_anios        0.865     0.034    -4.265   2.0e-5    0.809     0.925

Columnas que devuelve tidy()

ColumnaContenidoCon exponentiate = TRUE
termNombre de la variableIgual
estimateCoeficiente (log-odds)Odds ratio
std.errorError estándarError estándar (del log-OR)
statisticEstadístico z (Wald)Igual
p.valueP-valorIgual
conf.lowIC 95% inferior (log-odds)IC 95% inferior del OR
conf.highIC 95% superior (log-odds)IC 95% superior del OR

Filtrar y transformar con dplyr

# Solo los predictores significativos
tidy(modelo, exponentiate = TRUE, conf.int = TRUE) |>
  filter(term != "(Intercept)",
         p.value < 0.05)

# Ordenar por efecto (de mayor a menor OR)
tidy(modelo, exponentiate = TRUE, conf.int = TRUE) |>
  filter(term != "(Intercept)") |>
  arrange(desc(estimate))

# Crear etiquetas para el gráfico
tidy(modelo, exponentiate = TRUE, conf.int = TRUE) |>
  filter(term != "(Intercept)") |>
  mutate(
    variable = case_when(
      term == "satisfaccion_laboral" ~ "Satisfacción laboral",
      term == "antiguedad_anios"     ~ "Antigüedad (años)",
      term == "edad"                 ~ "Edad",
      TRUE ~ term
    ),
    significativo = p.value < 0.05
  )

Usar tidy() para forest plots con ggplot2

# Forest plot de odds ratios (código completo)
or_datos <- tidy(modelo, exponentiate = TRUE, conf.int = TRUE) |>
  filter(term != "(Intercept)") |>
  mutate(significativo = p.value < 0.05)

ggplot(or_datos, aes(x = estimate, y = reorder(term, estimate))) +
  geom_vline(xintercept = 1, linetype = "dashed", color = "gray50") +
  geom_point(aes(color = significativo), size = 3) +
  geom_errorbarh(aes(xmin = conf.low, xmax = conf.high,
                      color = significativo), height = 0.2) +
  scale_color_manual(values = c("TRUE" = "#B85042", "FALSE" = "gray60")) +
  labs(x = "Odds Ratio", y = NULL,
       title = "Factores de riesgo de rotación") +
  theme_minimal()

4. glance(): métricas globales del modelo

glance(modelo)
# # A tibble: 1 × 8
#   null.deviance df.null logLik   AIC   BIC deviance df.residual  nobs
#         1423.      1199  -652.  1312. 1332.   1304.       1196  1200

Columnas que devuelve glance()

ColumnaSignificadoUso práctico
null.devianceDeviance del modelo sin predictoresReferencia: cuánto "error" hay sin explicar nada
df.nullGL del modelo nulon − 1
logLikLog-verosimilitudBase para AIC, BIC; más alto = mejor ajuste
AICAkaike Information CriterionComparar modelos: menor = mejor
BICBayesian Information CriterionSimilar al AIC pero penaliza más la complejidad
devianceDeviance del modelo ajustadoMenor que null.deviance si los predictores ayudan
df.residualGL residualesn − k − 1
nobsNúmero de observacionesVerificación

Comparar modelos con glance()

# Crear tabla comparativa de varios modelos
bind_rows(
  glance(mod_nulo) |> mutate(modelo = "Nulo"),
  glance(mod_1)    |> mutate(modelo = "Satisfacción"),
  glance(mod_3)    |> mutate(modelo = "Sat + Antig + Edad"),
  glance(mod_ext)  |> mutate(modelo = "Extendido")
) |>
  select(modelo, AIC, BIC, deviance, df.residual) |>
  arrange(AIC)

5. augment(): datos + predicciones

augment(modelo, type.predict = "response")
# Devuelve el data frame original + columnas nuevas:
#   .fitted    = probabilidad predicha (con type.predict = "response")
#   .resid     = residuos del modelo
#   .hat       = leverage (influencia de cada observación)
#   .cooksd    = distancia de Cook (observaciones influyentes)
#   .std.resid = residuos estandarizados

Uso práctico: agregar predicciones al dataset

# Agregar probabilidades predichas al dataset original
innovaco_con_pred <- augment(modelo, newdata = innovaco,
                             type.predict = "response")

# Ahora puedes usar .fitted como probabilidad de rotación
innovaco_con_pred |>
  select(id_empleado, nombre, departamento, rotacion, .fitted) |>
  arrange(desc(.fitted)) |>
  head(20)

Diagnósticos del modelo

# Identificar observaciones influyentes
diagnosticos <- augment(modelo)

# Puntos con alta distancia de Cook (influyentes)
diagnosticos |>
  filter(.cooksd > 4 / nrow(innovaco)) |>
  arrange(desc(.cooksd))

# Gráfico de residuos vs. fitted
ggplot(diagnosticos, aes(x = .fitted, y = .resid)) +
  geom_point(alpha = 0.3) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  labs(title = "Residuos vs. Valores ajustados")

6. tidy() con otros tipos de modelos

broom no es solo para glm(). Funciona con la mayoría de los modelos estadísticos en R:

# Con prueba t
tidy(t.test(satisfaccion_laboral ~ rotacion, data = innovaco))

# Con chi-cuadrado
tidy(chisq.test(table(innovaco$departamento, innovaco$rotacion)))

# Con correlación
tidy(cor.test(innovaco$satisfaccion_laboral, innovaco$edad))

# Con regresión lineal
tidy(lm(ingreso_mensual ~ antiguedad_anios + educacion, data = innovaco))

# Con ANOVA
tidy(aov(satisfaccion_laboral ~ departamento, data = innovaco))
Regla de oro Cada vez que ejecutes una prueba estadística o ajustes un modelo, envuelve el resultado en tidy(). Obtendrás un tibble limpio que puedes usar inmediatamente con dplyr y ggplot2, sin tener que extraer componentes manualmente con $.

7. Resumen rápido

FunciónInputOutputUso típico
tidy(modelo)Cualquier modeloTibble: 1 fila por términoTabla de coeficientes, forest plot
tidy(modelo, exponentiate = TRUE)glm binomialTibble con odds ratiosOdds ratios para el VP
tidy(modelo, conf.int = TRUE)Cualquier modeloTibble con IC 95%Forest plots con barras de error
glance(modelo)Cualquier modeloTibble: 1 fila totalComparar AIC entre modelos
augment(modelo)Cualquier modeloDatos + prediccionesDiagnósticos, residuos, leverage
augment(modelo, type.predict = "response")glm binomialDatos + probabilidadesRanking de riesgo de rotación