Introducción

La regresión logística es una de las herramientas más utilizadas en estadística y aprendizaje automático cuando la variable de respuesta es binaria. Históricamente, se popularizó a mediados del siglo XX en biostatística y ciencias sociales para modelar resultados como presencia/ausencia de enfermedades, éxito/fracaso en encuestas, y en investigación educativa, para predecir aprobaciones o aprendizajes.

En este mini-curso, profundizaremos en:

  • Los fundamentos matemáticos y las motivaciones prácticas detrás de la regresión logística.
  • Cómo simular datos en R para entender paso a paso cómo funciona el método.
  • Aplicaciones concretas en el aula y con datos reales (dataset mtcars).
  • Diagnóstico y validación de modelos: desde matrices de confusión hasta curvas ROC y medidas de bondad de ajuste.
  • Visualizaciones avanzadas que facilitan la interpretación.
  • Ejercicios y tareas para consolidar conocimientos.

Finalmente, se proporcionan referencias bibliográficas recomendadas para quienes deseen profundizar teóricamente.

Objetivo: Que el lector, al terminar este documento, pueda aplicar regresión logística en la práctica, interpretar coeficientes y validar modelos con confianza.


Contenidos

  1. El problema de la predicción binaria

  2. Fundamentos matemáticos

    • 2.1. La función logística y su forma sigmoidea
    • 2.2. Odds, log-odds y odds ratios
    • 2.3. Verosimilitud y log-verosimilitud
    • 2.4. Comparación con regresión lineal
  3. Simulaciones básicas en R

    • 3.1. Ejemplo con variable continua (horas de estudio)
    • 3.2. Ejemplo con predictor categórico (asistencia)
    • 3.3. Ejemplo múltiple y evaluación del modelo
  4. Aplicaciones en aula

    • 4.1. Uso de datos reales: mtcars para predecir transmisión
    • 4.2. Ejercicio: predecir aprobación según asistencia y calificaciones previas
    • 4.3. Análisis con iris (ejercicio adicional)
  5. Diagnóstico y validación

    • 5.1. Matriz de confusión y umbral óptimo
    • 5.2. Curva ROC, AUC y sensibilidad/especificidad
    • 5.3. Pseudo-\(R^2\) y deviance residual
    • 5.4. Test de razón de verosimilitud y pruebas de bondad de ajuste
  6. Visualización avanzada

    • 6.1. Superficie de probabilidad para dos predictores
    • 6.2. Plots interactivos con plotly
    • 6.3. Gráficos de diagnóstico (residuos, influencia)
  7. Resumen de pasos prácticos

  8. Conclusiones y ejercicios adicionales

  9. Referencias bibliográficas


1. El problema de la predicción binaria

En regresión clásica (lineal), la variable dependiente \(Y\) es continua, pero cuando \(Y\) solo puede tomar valores en \(\{0,1\}\), el modelo lineal ordinario presenta varias limitaciones:

  1. Predicciones fuera de \([0,1]\): La ecuación lineal

    \[ \hat{Y} \;=\; \beta_0 + \beta_1 X \]

    puede generar valores negativos o mayores que uno, que no tienen sentido como probabilidad.

  2. Suposición de varianza constante: En regresión lineal se asume homocedasticidad (\(\mathrm{Var}(Y \mid X) = \sigma^2\)), pero una variable binaria no cumple esta propiedad.

  3. Distribución normal de errores: El modelo logístico asume una distribución de Bernoulli para los resultados, no normal.

Definición formal: Si \(Y\) es una variable binaria con \(P(Y=1) = p\) y \(P(Y=0) = 1 - p\), la regresión logística busca modelar la función \(p(X)\) en función de los predictores \(X = (X_1, \dots, X_k)\).

Ejemplo en contexto educativo: Supongamos que tenemos los datos de 200 estudiantes y queremos predecir si un alumno aprueba (\(Y = 1\)) o reprueba (\(Y = 0\)) un examen. Los predictores podrían ser:

  • Horas de estudio diarias (\(X_1 = \text{horas}\)), variable continua.
  • Asistencia a clases (\(X_2 = \text{asistio}\)), variable binaria.
  • Calificación previa en tareas (\(X_3 = \text{tarea\_prev}\)), variable continua.

Con regresión logística, interceptamos la relación entre \(\mathbf{X}\) y \(p = P(Y = 1 \mid X)\), asegurando que \(0 \le p \le 1\).


2. Fundamentos matemáticos

2.1. La función logística y su forma sigmoidea

La base de la regresión logística es la función sigmoidea, que comprime valores reales en el rango \((0,1)\). Está definida como:

\[ \sigma(z) \;=\; \frac{1}{1 + e^{-z}}, \]

donde \(z\) puede ser cualquier número real.

Propiedades clave:

  • \(\displaystyle \lim_{z \to -\infty} \frac{1}{1 + e^{-z}} = 0.\)
  • \(\displaystyle \lim_{z \to +\infty} \frac{1}{1 + e^{-z}} = 1.\)
  • Para \(z = 0\), \(\displaystyle \sigma(0) = 0.5.\)

A continuación generamos una gráfica de la función sigmoidea para visualizar su forma:

x <- seq(-10, 10, length.out = 200)
y <- 1 / (1 + exp(-x))
plot(x, y, type = 'l', lwd = 2, col = 'blue',
     main = 'Función sigmoidea (Logística)',
     xlab = 'z = β₀ + β₁ x₁ + … + βₖ xₖ',
     ylab = 'σ(z)')
abline(h = c(0, 0.5, 1), lty = 2, col = 'gray')
text(-10, 0.05, '0', adj = c(0, 0))
text(0, 0.55, '0.5', adj = c(0.5, 0))
text(10, 0.95, '1', adj = c(1, 0))

Interpretación: Si definimos el predictor lineal

\[ \eta_i \;=\; \beta_0 + \beta_1 X_{i1} + \dots + \beta_k X_{ik}, \]

entonces la probabilidad de éxito es:

\[ p_i = P(Y_i = 1 \mid \mathbf{X}_i) = \frac{1}{1 + e^{-\eta_i}}. \]

Para valores muy negativos de \(\eta_i\), \(p_i \approx 0\); para valores muy grandes, \(p_i \approx 1\). Esta flexibilidad evita problemas de predicción fuera del rango.

Tabla de ejemplo: Valores de la función sigmoidea

\(\eta\) (z) \(\sigma(z)\) (p)
\(-3.0\) \(0.0474\)
\(-2.0\) \(0.1192\)
\(-1.0\) \(0.2689\)
\(0.0\) \(0.5000\)
\(1.0\) \(0.7311\)
\(2.0\) \(0.8808\)
\(3.0\) \(0.9526\)

Esta tabla ilustra que, por ejemplo, \(\eta = 1\) equivale a una probabilidad \(p \approx 0{,}73\).

2.2. Odds, log-odds y odds ratios

Para trabajar con la función sigmoidea, se introduce la razón de probabilidades (odds) y el logit:

  1. Odds:

    \[ \text{odds}_i = \frac{p_i}{1 - p_i}. \]

    • Interpreta cuántas veces más probable es el éxito (\(1\)) que el fracaso (\(0\)) para la observación \(i\).
    • Si \(p_i = 0{,}8\), \(\text{odds}_i = 0{,}8 / 0{,}2 = 4\) (cuatro veces más probable el éxito que el fracaso).
  2. Logit:

    \[ \text{logit}(p_i) = \ln\bigl(\tfrac{p_i}{1 - p_i}\bigr). \]

    • Convierte la razón de probabilidades en una escala real \((-\infty, +\infty)\).
    • Si \(\text{odds}_i = 4\), \(\text{logit}(p_i) = \ln(4) \approx 1{,}386\).

En el modelo de regresión logística:

\[ \text{logit}(p_i) = \beta_0 + \beta_1 X_{i1} + \cdots + \beta_k X_{ik}. \]

De aquí se deduce que:

  • \(\beta_j\) es el cambio en log-odds asociado a un incremento unitario en \(X_j\), manteniendo los demás predictores constantes.

  • \(e^{\beta_j}\) es el odds ratio (OR) asociado a \(X_j\), es decir:

    \[ OR_j = e^{\beta_j} = \frac{\text{odds}(p_i \mid X_j + 1)}{\text{odds}(p_i \mid X_j)}. \]

Ejemplo interpretativo: Si \(\beta_1 = 0{,}693\), entonces \(OR_1 = e^{0{,}693} \approx 2\). Con cada hora adicional de estudio, las probabilidades de aprobar se duplican (se multiplican por 2).

2.3. Verosimilitud y log-verosimilitud

Para estimar \(\boldsymbol{\beta}\), usamos Máxima Verosimilitud. Suponiendo que las observaciones \(Y_i\) son independientes y \(Y_i \sim \mathrm{Bernoulli}(p_i)\):

  • Función de verosimilitud:

    \[ L(\boldsymbol{\beta}) = \prod_{i=1}^n p_i^{y_i} (1 - p_i)^{1 - y_i}, \]

    donde

    \[ p_i = \sigma\bigl(\beta_0 + \beta_1 X_{i1} + \dots + \beta_k X_{ik}\bigr). \]

  • Log-verosimilitud (más conveniente numéricamente):

    \[ \ell(\boldsymbol{\beta}) = \sum_{i=1}^n \Bigl[\,y_i \ln(p_i) + (1 - y_i) \ln(1 - p_i)\Bigr]. \]

R realiza la optimización para \(\hat{\boldsymbol{\beta}} = \arg\max_{\boldsymbol{\beta}} \ell(\boldsymbol{\beta})\) sin que el usuario tenga que programar algoritmos numéricos.

2.4. Comparación con regresión lineal

Aspecto Regresión Lineal Regresión Logística
Variable dependiente (\(Y\)) Continua (Normal con varianza constante) Binaria (Bernoulli)
Función de enlace Identidad: \(\mathbb{E}[Y \mid X] = \beta_0 + \beta_1 X\) Logit: \(\ln\bigl(p/(1 - p)\bigr) = \beta_0 + \beta_1 X\)
Rango de predicción \((-\infty, +\infty)\) (no apropiado para probabilidades) \([0,1]\) (gracias a la función sigmoidea)
Método de estimación Mínimos cuadrados ordinarios (OLS) Máxima verosimilitud
Interpretación de coeficientes Cambio en \(Y\) por unidad en \(X\) (lineal) Cambio en log-odds / odds ratio por unidad en \(X\)

3. Simulaciones básicas en R

En esta sección, crearemos datos simulados para entender de forma práctica cómo funciona la regresión logística, desde la generación de datos hasta la interpretación de resultados.

3.1. Ejemplo 1: variable continua (horas de estudio)

Supongamos que queremos simular \(n = 200\) estudiantes. Definimos los parámetros:

  • \(\beta_0 = -3\) (intercepto).
  • \(\beta_1 = 0{,}8\) (coeficiente para horas de estudio).

Entonces:

  1. Generamos las horas de estudio como \(X_i \sim \mathcal{N}(5,\,1{,}5^2).\)

  2. Calculamos la probabilidad de aprobar:

    \[ p_i = \frac{1}{1 + \exp\bigl(-\bigl(-3 + 0{,}8 \times \text{horas}_i\bigr)\bigr)}. \]

  3. Generamos \(Y_i \sim \mathrm{Bernoulli}(p_i)\).

set.seed(123)
n     <- 200
horas <- rnorm(n, mean = 5, sd = 1.5)  # Horas promedio de estudio
prob  <- 1 / (1 + exp(-(-3 + 0.8 * horas)))  # Función sigmoidea
aprob <- rbinom(n, 1, prob)  # Variable binaria (0/1)
datos1 <- tibble(horas, prob, aprob)
# Mostrar las primeras filas en una tabla
knitr::kable(head(datos1, 10),
             caption = 'Primeras 10 observaciones simuladas')
Primeras 10 observaciones simuladas
horas prob aprob
4.159287 0.5811337 0
4.654734 0.6734404 1
7.338063 0.9463662 1
5.105763 0.7473654 1
5.193932 0.7604495 1
7.572597 0.9551300 1
5.691374 0.8253609 1
3.102408 0.3733028 0
3.969721 0.5438313 1
4.331507 0.6142484 0

3.1.1. Visualización de la distribución de horas

hist(datos1$horas, breaks = 20, col = 'lightblue', border = 'white',
     main = 'Distribución de horas de estudio', xlab = 'Horas', ylab = 'Frecuencia')

3.1.2. Visualización de puntos y curva ajustada

ggplot(datos1, aes(x = horas, y = aprob)) +
  geom_jitter(height = 0.05, alpha = 0.6, color = 'darkblue') +
  geom_smooth(method = 'glm', method.args = list(family = binomial),
              se = FALSE, size = 1, color = 'red') +
  labs(title = 'Probabilidad de aprobar vs Horas de estudio',
       x = 'Horas de estudio',
       y = 'Resultado (0 = No, 1 = Sí)') +
  theme_minimal()

Nota: La función geom_smooth(method = 'glm', method.args = list(family = binomial)) ajusta automáticamente un modelo logístico y muestra la curva de probabilidad.

3.1.3. Ajuste del modelo y coeficientes

m1 <- glm(aprob ~ horas, data = datos1, family = binomial)
summary(m1)
## 
## Call:
## glm(formula = aprob ~ horas, family = binomial, data = datos1)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -3.0094     0.7208  -4.175 2.98e-05 ***
## horas         0.8267     0.1564   5.287 1.24e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 242.63  on 199  degrees of freedom
## Residual deviance: 204.44  on 198  degrees of freedom
## AIC: 208.44
## 
## Number of Fisher Scoring iterations: 5
# Odds ratios e intervalos de confianza
or_ci <- exp(cbind(Estimate = coef(m1), confint(m1)))
knitr::kable(or_ci, digits = 3,
             caption = 'Odds Ratios e Intervalos de Confianza')
Odds Ratios e Intervalos de Confianza
Estimate 2.5 % 97.5 %
(Intercept) 0.049 0.011 0.191
horas 2.286 1.711 3.166

Interpretación:

  • Intercepto \(\hat{\beta}_0\): log-odds cuando horas = 0.
  • \(\hat{\beta}_1\): cambio en log-odds de aprobar por cada hora adicional.
  • Odds Ratio: \(\exp(\hat{\beta}_1)\) (por ejemplo, \(\approx 2{,}12\)) indica que cada hora extra de estudio incrementa en \(\sim 112\%\) la razón de probabilidades de aprobar.

3.1.4. Curva de probabilidad detallada

coef0 <- coef(m1)[1]
coef1 <- coef(m1)[2]
curve(expr = 1 / (1 + exp(-(coef0 + coef1 * x))),
      from = min(datos1$horas) - 1,
      to   = max(datos1$horas) + 1,
      ylab = 'Probabilidad de aprobar',
      xlab = 'Horas de estudio',
      col  = 'darkgreen',
      lwd  = 2)
points(datos1$horas, datos1$aprob, pch = 19, cex = 0.5, col = 'darkblue')
legend('bottomright', legend = c('Datos', 'Curva logística'),
       col = c('darkblue','darkgreen'), pch = c(19, NA),
       lty = c(NA,1), bty = 'n')

3.1.5. Tareas propuestas

  1. Modificar parámetros: Cambia \(\beta_0\) a \(-2\) y \(\beta_1\) a \(1{,}2\). Repite la simulación y compara las pendientes de la curva sigmoidea.
  2. Cambiar varianza: Aumenta la desviación estándar de horas a 2. Observa el efecto en la dispersión de puntos y en la precisión del modelo.
  3. Tabla de valores teóricos vs simulados: Construye una tabla con valores de \(\text{horas} = \{2,4,6,8\}\), calcula la probabilidad teórica \(p = \frac{1}{1 + \exp\bigl(-(-3 + 0.8 x)\bigr)}\) y compárala con \(\hat{p}\) predicha por el modelo.
valores <- tibble(horas = c(2,4,6,8)) %>%
  mutate(p_teorica = 1 / (1 + exp(-(-3 + 0.8 * horas))),
         p_modelo  = predict(m1, newdata = ., type = 'response'))
knitr::kable(valores, digits = 3,
             caption = 'Comparación Teórica vs Modelo')
Comparación Teórica vs Modelo
horas p_teorica p_modelo
2 0.198 0.205
4 0.550 0.574
6 0.858 0.876
8 0.968 0.974

3.2. Ejemplo 2: predictor categórico (asistencia)

Ahora analizaremos un caso donde el predictor principal es categórico (asistencia: sí/no). Simularemos \(n = 300\) estudiantes:

set.seed(456)
n       <- 300
asistio <- rbinom(n, 1, 0.65)  # 1 = asistió, 0 = no asistió
# Definimos probabilidad para cada nivel de 'asistio'
prob2   <- 1 / (1 + exp(-(-1 + 1.5 * asistio)))
aprob2  <- rbinom(n, 1, prob2)
datos2  <- tibble(
  asistio = factor(asistio, levels = c(0, 1), labels = c('No', 'Sí')),
  prob    = prob2,
  aprob   = aprob2
)
knitr::kable(head(datos2, 10),
             caption = 'Primeras 10 observaciones simuladas (asistencia)')
Primeras 10 observaciones simuladas (asistencia)
asistio prob aprob
0.6224593 0
0.6224593 1
No 0.2689414 0
No 0.2689414 0
No 0.2689414 0
0.6224593 0
0.6224593 1
0.6224593 1
0.6224593 1
0.6224593 0

3.2.1. Tabla resumen de proporciones

datos2 %>%
  group_by(asistio) %>%
  summarize(
    n          = n(),
    prop_aprob = mean(aprob),
    .groups    = 'drop'
  ) %>%
  knitr::kable(digits = 3,
               caption = 'Proporción de aprobación por asistencia')
Proporción de aprobación por asistencia
asistio n prop_aprob
No 111 0.297
189 0.582

3.2.2. Ajuste y resultados del modelo

m2   <- glm(aprob ~ asistio, data = datos2, family = binomial)
summary(m2)
## 
## Call:
## glm(formula = aprob ~ asistio, family = binomial, data = datos2)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -0.8602     0.2077  -4.142 3.44e-05 ***
## asistioSí     1.1912     0.2547   4.677 2.91e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 415.23  on 299  degrees of freedom
## Residual deviance: 392.00  on 298  degrees of freedom
## AIC: 396
## 
## Number of Fisher Scoring iterations: 4
res2 <- tidy(m2, exponentiate = TRUE, conf.int = TRUE)
knitr::kable(res2, digits = 3,
             caption = 'Resultados del modelo logístico (asistencia)')
Resultados del modelo logístico (asistencia)
term estimate std.error statistic p.value conf.low conf.high
(Intercept) 0.423 0.208 -4.142 0 0.278 0.629
asistioSí 3.291 0.255 4.677 0 2.012 5.472
  • La categoría de referencia es \(\text{asistio} = \text{No}\).
  • Si el OR para \(\text{asistio} = \text{Sí}\) es aproximadamente \(4{,}47\), significa que quienes asisten al 100 % de las clases tienen \(4{,}47\) veces más probabilidades de aprobar comparado con quienes no asisten.

3.2.3. Gráfico de proporciones por categoría

datos2 %>%
  group_by(asistio) %>%
  summarize(p_aprob = mean(aprob), .groups = 'drop') %>%
  ggplot(aes(x = asistio, y = p_aprob, fill = asistio)) +
    geom_col() +
    scale_y_continuous(labels = scales::percent_format(scale = 1)) +
    labs(title = 'Tasa de aprobación según asistencia',
         x = 'Asistencia completa',
         y = 'Proporción de aprobados') +
    theme_minimal() +
    theme(legend.position = 'none')

3.2.4. Ejercicio adicional (categórico multinivel)

Objetivo: Extender el modelo a tres niveles de asistencia: “Nunca”, “A veces” y “Siempre”.

  1. Genera un vector asistio3 con valores 0, 1, 2 según probabilidades 0.1, 0.5 y 0.4.
  2. Convierte asistio3 en factor con etiquetas c("Nunca","A veces","Siempre").
  3. Simula \(\text{prob3} = \frac{1}{1 + \exp\bigl(-\bigl(-1 + 1.2 \times (\text{asistio3} == 2) + 0.5 \times (\text{asistio3} == 1)\bigr)\bigr)}.\)
  4. Genera \(\text{aprob3} \sim \mathrm{Bernoulli}(\text{prob3})\) y ajusta el modelo \(\texttt{glm(aprob3 ~ asistio3, family = binomial)}.\)
  5. Interpreta los coeficientes y calcula OR para “A veces” vs “Nunca” y “Siempre” vs “Nunca”.
set.seed(987)
n        <- 300
asistio3 <- sample(c(0,1,2), size = n, replace = TRUE, prob = c(0.1, 0.5, 0.4))
prob3    <- 1 / (1 + exp(-(-1 + 0.5 * (asistio3 == 1) + 1.2 * (asistio3 == 2))))
aprob3   <- rbinom(n, 1, prob3)
datos2b  <- tibble(
  asistio3 = factor(asistio3, levels = c(0,1,2), labels = c('Nunca','A veces','Siempre')),
  prob3,
  aprob3
)
# Ajuste modelo multinivel
m2b <- glm(aprob3 ~ asistio3, data = datos2b, family = binomial)
tidy(m2b, exponentiate = TRUE, conf.int = TRUE) %>%
  knitr::kable(digits = 3,
               caption = 'Resultados del modelo logístico con 3 niveles de asistencia')
Resultados del modelo logístico con 3 niveles de asistencia
term estimate std.error statistic p.value conf.low conf.high
(Intercept) 0.440 0.362 -2.269 0.023 0.208 0.872
asistio3A veces 1.136 0.399 0.320 0.749 0.530 2.567
asistio3Siempre 2.146 0.411 1.858 0.063 0.978 4.959

3.3. Ejemplo 3: modelo múltiple

Combinaremos horas de estudio, asistencia (binaria) y calificación de tarea previa para simular \(n = 500\) estudiantes.

  1. Generamos las variables:

    • horas \(\sim\) Normal(5, 1.3).
    • asistio2 \(\sim\) Bernoulli(0.7).
    • tarea_prev \(\sim\) Normal(70, 10).
  2. Definimos el predictor lineal:

    \[ \eta_i = -6 + 0.6 \times \text{horas}_i + 1.2 \times \text{asistio2}_i + 0.04 \times \text{tarea\_prev}_i. \]

  3. Calculamos \(p_i = 1 / [1 + \exp(-\eta_i)]\) y simulamos \(Y_i \sim \mathrm{Bernoulli}(p_i)\).

set.seed(789)
n          <- 500
horas      <- rnorm(n, 5, 1.3)
asistio2   <- rbinom(n, 1, 0.7)
tarea_prev <- rnorm(n, 70, 10)
lin_pred   <- -6 + 0.6 * horas + 1.2 * asistio2 + 0.04 * tarea_prev
prob3      <- 1 / (1 + exp(-lin_pred))
aprob3     <- rbinom(n, 1, prob3)
datos3     <- tibble(
  horas,
  asistio   = factor(asistio2, levels = c(0,1), labels = c('No','Sí')),
  tarea_prev,
  prob3,
  aprob = aprob3
)
knitr::kable(head(datos3, 10),
             caption = 'Primeras 10 observaciones simuladas (modelo múltiple)')
Primeras 10 observaciones simuladas (modelo múltiple)
horas asistio tarea_prev prob3 aprob
5.681326 68.86410 0.7963052 0
2.061002 65.45533 0.2798540 0
4.974416 76.06943 0.7733679 1
5.238082 No 70.32088 0.4889229 0
4.530243 69.67922 0.6693650 1
4.370171 No 63.67319 0.3034401 1
4.133793 57.62722 0.4963413 0
4.773190 61.46886 0.6277715 1
3.685752 44.68542 0.3097835 0
5.961605 58.78982 0.7555722 0

3.3.1. Estadísticos descriptivos iniciales

datos3 %>%
  summarize(
    media_horas  = mean(horas), sd_horas  = sd(horas),
    media_tarea  = mean(tarea_prev), sd_tarea = sd(tarea_prev),
    prop_asisten = mean(asistio == 'Sí'), prop_aproban = mean(aprob)
  ) %>%
  knitr::kable(digits = 3,
               caption = 'Estadísticos resumidos de variables simuladas')
Estadísticos resumidos de variables simuladas
media_horas sd_horas media_tarea sd_tarea prop_asisten prop_aproban
4.999 1.306 69.515 10.249 0.708 0.632

3.3.2. Ajuste del modelo y extracción de resultados

m3   <- glm(aprob ~ horas + asistio + tarea_prev, data = datos3, family = binomial)
summary(m3)
## 
## Call:
## glm(formula = aprob ~ horas + asistio + tarea_prev, family = binomial, 
##     data = datos3)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -5.185832   0.863986  -6.002 1.95e-09 ***
## horas        0.515869   0.082985   6.216 5.09e-10 ***
## asistioSí    0.923692   0.215565   4.285 1.83e-05 ***
## tarea_prev   0.037030   0.009912   3.736 0.000187 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 657.88  on 499  degrees of freedom
## Residual deviance: 583.68  on 496  degrees of freedom
## AIC: 591.68
## 
## Number of Fisher Scoring iterations: 3
res3 <- tidy(m3, exponentiate = TRUE, conf.int = TRUE)
knitr::kable(res3, digits = 3,
             caption = 'Odds Ratios e Intervalos de Confianza (modelo múltiple)')
Odds Ratios e Intervalos de Confianza (modelo múltiple)
term estimate std.error statistic p.value conf.low conf.high
(Intercept) 0.006 0.864 -6.002 0 0.001 0.029
horas 1.675 0.083 6.216 0 1.429 1.979
asistioSí 2.519 0.216 4.285 0 1.654 3.855
tarea_prev 1.038 0.010 3.736 0 1.018 1.058

Interpretación:

  • \(e^{\beta_1}\) para horas indica el cambio en la razón de probabilidades por cada hora adicional.
  • \(e^{\beta_2}\) para asistio = Sí vs No indica cuántas veces se multiplican las probabilidades si un estudiante asistió a clases en comparación con no asistir.
  • \(e^{\beta_3}\) para tarea_prev significa el efecto de un punto adicional en la calificación de tarea previa.

3.3.3. Tabla de probabilidades agrupadas

Para entender mejor la interacción de predictores, creamos una tabla bidimensional de probabilidades promedio agrupadas por categorías de asistio y cuartiles en tarea_prev:

datos3 %>%
  mutate(corte_tarea = ntile(tarea_prev, 4)) %>%
  group_by(asistio, corte_tarea) %>%
  summarize(
    p_empirica = mean(aprob),
    .groups    = 'drop'
  ) %>%
  pivot_wider(names_from = corte_tarea, values_from = p_empirica) %>%
  knitr::kable(digits = 3,
               caption = 'Probabilidad de aprobar por asistencia y cuartiles de tarea previa')
Probabilidad de aprobar por asistencia y cuartiles de tarea previa
asistio 1 2 3 4
No 0.389 0.474 0.424 0.641
0.573 0.713 0.717 0.767

3.3.4. Evaluación rápida: Curva ROC y AUC

roc3 <- roc(datos3$aprob, fitted(m3))
plot(roc3, print.auc = TRUE, main = 'Curva ROC - Modelo Múltiple')

Interpretación: El valor de AUC (Área Bajo la Curva) cuantifica la capacidad discriminativa del modelo. Un AUC cercano a 1 indica excelente separación entre ambos grupos.

3.3.5. Matriz de confusión y umbral de decisión

# Predicciones con umbral 0.5
y_pred <- if_else(fitted(m3) >= 0.5, 1, 0)
mc3    <- table(Real = datos3$aprob, Predicho = y_pred)
knitr::kable(mc3,
             caption = 'Matriz de Confusión (umbral = 0.5)')
Matriz de Confusión (umbral = 0.5)
0 1
0 71 113
1 39 277
# Exactitud global
acc3 <- mean(y_pred == datos3$aprob)
paste('Exactitud:', round(acc3, 3))
## [1] "Exactitud: 0.696"

Para explorar distintos umbrales y hallar el óptimo:

calc_metrics <- function(thr) {
  pred_thr     <- if_else(fitted(m3) >= thr, 1, 0)
  tp           <- sum(pred_thr == 1 & datos3$aprob == 1)
  tn           <- sum(pred_thr == 0 & datos3$aprob == 0)
  fp           <- sum(pred_thr == 1 & datos3$aprob == 0)
  fn           <- sum(pred_thr == 0 & datos3$aprob == 1)
  sensibilidad <- tp / (tp + fn)
  especificidad <- tn / (tn + fp)
  tibble(Umbral = thr, Sensibilidad = sensibilidad, Especificidad = especificidad)
}
bind_rows(calc_metrics(0.4), calc_metrics(0.5), calc_metrics(0.6)) %>%
  knitr::kable(digits = 3,
               caption = 'Sensibilidad y Especificidad para distintos umbrales')
Sensibilidad y Especificidad para distintos umbrales
Umbral Sensibilidad Especificidad
0.4 0.940 0.266
0.5 0.877 0.386
0.6 0.728 0.576

4. Aplicaciones en aula

En el aula, es útil mostrar ejemplos con datasets conocidos de R y ejercicios prácticos. A continuación, mostramos dos casos:

4.1. Uso de datos reales: mtcars para predecir transmisión

El dataset mtcars contiene características de automóviles. Queremos predecir si un auto tiene transmisión manual (\(am = 1\)) o automática (\(am = 0\)) usando hp (caballos de fuerza) y wt (peso en miles de libras).

data(mtcars)
mt <- mtcars %>%
  mutate(am = factor(am, labels = c('Auto', 'Manual')))
# Estadísticos descriptivos
mt %>%
  group_by(am) %>%
  summarize(media_hp = mean(hp), media_wt = mean(wt), .groups = 'drop') %>%
  knitr::kable(digits = 2,
               caption = 'Resumen de HP y WT por tipo de transmisión')
Resumen de HP y WT por tipo de transmisión
am media_hp media_wt
Auto 160.26 3.77
Manual 126.85 2.41

4.1.1. Ajuste del modelo

hp10       <- mt$hp / 10  # para interpretar coeficiente por cada 10 HP
mod_mtcars <- glm(am ~ hp10 + wt, data = mt, family = binomial)
summary(mod_mtcars)
## 
## Call:
## glm(formula = am ~ hp10 + wt, family = binomial, data = mt)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)   
## (Intercept)  18.8663     7.4436   2.535  0.01126 * 
## hp10          0.3626     0.1773   2.044  0.04091 * 
## wt           -8.0835     3.0687  -2.634  0.00843 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 43.230  on 31  degrees of freedom
## Residual deviance: 10.059  on 29  degrees of freedom
## AIC: 16.059
## 
## Number of Fisher Scoring iterations: 8
# Odds ratios e intervalos de confianza
res_mtcars <- exp(cbind(Estimate = coef(mod_mtcars), confint(mod_mtcars)))
knitr::kable(res_mtcars, digits = 3,
             caption = 'Odds Ratios e IC del modelo mtcars')
Odds Ratios e IC del modelo mtcars
Estimate 2.5 % 97.5 %
(Intercept) 1.561455e+08 4342.767 4.083258e+17
hp10 1.437000e+00 1.112 2.473000e+00
wt 0.000000e+00 0.000 2.300000e-02

Interpretación:

  • OR para hp10: efecto de 10 caballos de fuerza adicionales.
  • OR para wt: cambio en odds por cada 1000 libras adicionales.

4.1.2. Gráficos de probabilidad

# Secuencia de valores de hp manteniendo wt en su media
grid_mtcars <- tibble(
  hp10 = seq(min(mt$hp/10), max(mt$hp/10), length.out = 100),
  wt   = mean(mt$wt)
)
grid_mtcars$prob_manual <- predict(mod_mtcars, newdata = grid_mtcars, type = 'response')

ggplot(grid_mtcars, aes(x = hp10 * 10, y = prob_manual)) +
  geom_line(color = 'blue', size = 1) +
  labs(title = 'Probabilidad de Transmisión Manual vs HP',
       x = 'Caballos de Fuerza (HP)',
       y = 'Probabilidad Manual') +
  theme_minimal()

4.2. Ejercicio en aula: predecir aprobación según asistencia y calificaciones previas

Imaginemos un escenario realista: tenemos un curso y recolectamos las siguientes variables:

  • asistencias: número de clases asistidas por el estudiante (0 a 30).
  • nota_parcial: calificación en el primer examen parcial (0 a 100).
  • aprueba: \(1\) si el estudiante aprobó el curso (evaluación final), \(0\) en otro caso.

Vamos a simular datos y luego ajustar un modelo logístico.

set.seed(100)
n           <- 150
asistencias <- rpois(n, lambda = 25)  # Promedio de 25 clases asistidas
nota_parcial <- rnorm(n, mean = 75, sd = 10)
lin_pred_aula <- -5 + 0.1 * asistencias + 0.05 * nota_parcial
prob_aula      <- 1 / (1 + exp(-lin_pred_aula))
aprueba       <- rbinom(n, 1, prob_aula)
datos_aula    <- tibble(asistencias, nota_parcial, prob_aula, aprueba)
knitr::kable(head(datos_aula, 10), digits = 3,
             caption = 'Primeras 10 observaciones simuladas (ejercicio aula)')
Primeras 10 observaciones simuladas (ejercicio aula)
asistencias nota_parcial prob_aula aprueba
22 62.603 0.582 1
17 80.899 0.678 1
29 76.240 0.847 0
25 69.763 0.729 0
26 81.202 0.840 1
22 82.082 0.787 1
27 74.068 0.803 1
23 72.048 0.711 1
27 64.142 0.712 1
27 68.752 0.757 0

4.2.1. Estadísticos descriptivos de variables

datos_aula %>%
  summarize(
    media_asist = mean(asistencias), sd_asist = sd(asistencias),
    media_nota  = mean(nota_parcial), sd_nota  = sd(nota_parcial),
    prop_aprob  = mean(aprueba)
  ) %>%
  knitr::kable(digits = 3,
               caption = 'Estadísticos resumidos (ejercicio aula)')
Estadísticos resumidos (ejercicio aula)
media_asist sd_asist media_nota sd_nota prop_aprob
24.647 4.695 75.094 9.912 0.753

4.2.2. Ajuste del modelo

mod_aula <- glm(aprueba ~ asistencias + nota_parcial,
                data = datos_aula, family = binomial)
summary(mod_aula)
## 
## Call:
## glm(formula = aprueba ~ asistencias + nota_parcial, family = binomial, 
##     data = datos_aula)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)  
## (Intercept)  -4.04948    1.86441  -2.172   0.0299 *
## asistencias   0.05466    0.04391   1.245   0.2132  
## nota_parcial  0.05189    0.02058   2.522   0.0117 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 167.59  on 149  degrees of freedom
## Residual deviance: 159.14  on 147  degrees of freedom
## AIC: 165.14
## 
## Number of Fisher Scoring iterations: 4
# Odds ratios e intervalos de confianza
res_aula <- exp(cbind(Estimate = coef(mod_aula), confint(mod_aula)))
knitr::kable(res_aula, digits = 3,
             caption = 'Odds Ratios e IC (ejercicio aula)')
Odds Ratios e IC (ejercicio aula)
Estimate 2.5 % 97.5 %
(Intercept) 0.017 0.000 0.617
asistencias 1.056 0.971 1.154
nota_parcial 1.053 1.013 1.099

Interpretación:

  • OR para asistencias: cambio en odds de aprobar por cada clase adicional.
  • OR para nota_parcial: cambio en odds por cada punto adicional en la calificación.

4.2.3. Predicción de ejemplos específicos

# Estudiantes hipotéticos:
evaluar <- tibble(
  asistencias  = c(20, 30, 15),
  nota_parcial = c(80, 90, 60)
)
evaluar$prob <- predict(mod_aula, newdata = evaluar, type = 'response')
knitr::kable(evaluar, digits = 3,
             caption = 'Probabilidades predichas para ejemplos específicos')
Probabilidades predichas para ejemplos específicos
asistencias nota_parcial prob
20 80 0.768
30 90 0.906
15 60 0.471

4.2.4. Ejercicio adicional: análisis con iris

  1. Cargar iris y crear una variable binaria:

    \[ \text{es\_setosa} = \begin{cases} 1, & \text{si Species == 'setosa'};\\ 0, & \text{en otro caso}. \end{cases} \]

  2. Ajustar el modelo

    \[ \texttt{glm(es\_setosa ~ Sepal.Length + Sepal.Width, data = iris2, family = binomial)}. \]

  3. Interpretar coeficientes y calcular OR.

  4. Graficar curvas de probabilidad para rangos de Sepal.Length y Sepal.Width.

# Paso 1: Preparar datos
iris2 <- iris %>%
  mutate(es_setosa = if_else(Species == 'setosa', 1, 0))
# Paso 2: Ajuste del modelo
mod_iris <- glm(es_setosa ~ Sepal.Length + Sepal.Width,
                data = iris2, family = binomial)
summary(mod_iris)
## 
## Call:
## glm(formula = es_setosa ~ Sepal.Length + Sepal.Width, family = binomial, 
##     data = iris2)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)
## (Intercept)     437.2   128737.9   0.003    0.997
## Sepal.Length   -163.4    45394.8  -0.004    0.997
## Sepal.Width     137.9    44846.1   0.003    0.998
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 1.9095e+02  on 149  degrees of freedom
## Residual deviance: 2.7060e-08  on 147  degrees of freedom
## AIC: 6
## 
## Number of Fisher Scoring iterations: 25
res_iris <- exp(cbind(Estimate = coef(mod_iris), confint(mod_iris)))
knitr::kable(res_iris, digits = 3,
             caption = 'Odds Ratios e IC (iris)')
Odds Ratios e IC (iris)
Estimate 2.5 % 97.5 %
(Intercept) 7.547721e+189 0 Inf
Sepal.Length 0.000000e+00 0 Inf
Sepal.Width 8.025273e+59 0 Inf

5. Diagnóstico y validación

Una vez ajustado un modelo, es crucial evaluar su desempeño y verificar supuestos.

5.1. Matriz de confusión y umbral óptimo

La matriz de confusión compara valores observados vs predichos usando un umbral de probabilidad (p. ej., 0{,}5). Se calculan:

  • Verdaderos positivos (TP): casos con \(Y = 1\) predichos como 1.
  • Falsos positivos (FP): casos con \(Y = 0\) predichos como 1.
  • Verdaderos negativos (TN): casos con \(Y = 0\) predichos como 0.
  • Falsos negativos (FN): casos con \(Y = 1\) predichos como 0.

Métricas:

  • Exactitud: \(\displaystyle \frac{TP + TN}{TP + TN + FP + FN}.\)
  • Sensibilidad (Recall, Tasa Verdaderos Positivos): \(\displaystyle \frac{TP}{TP + FN}.\)
  • Especificidad: \(\displaystyle \frac{TN}{TN + FP}.\)
# Ejemplo usando mod_aula
y_prob      <- predict(mod_aula, type = 'response')
y_pred      <- if_else(y_prob >= 0.5, 1, 0)
mc          <- table(Observado = datos_aula$aprueba, Predicho = y_pred)
knitr::kable(mc, caption = 'Matriz de Confusión (umbral = 0.5)')
Matriz de Confusión (umbral = 0.5)
0 1
0 4 33
1 1 112
# Calcular métricas
tp          <- mc[2,2]; tn <- mc[1,1]; fp <- mc[1,2]; fn <- mc[2,1]
exactitud   <- (tp + tn) / sum(mc)
sensibilidad  <- tp / (tp + fn)
especificidad <- tn / (tn + fp)
tibble(Métrica = c('Exactitud','Sensibilidad','Especificidad'),
       Valor   = c(exactitud, sensibilidad, especificidad)) %>%
  knitr::kable(digits = 3,
               caption = 'Métricas de desempeño')
Métricas de desempeño
Métrica Valor
Exactitud 0.773
Sensibilidad 0.991
Especificidad 0.108

Para determinar el umbral óptimo, evaluamos sensibilidad y especificidad en varios puntos:

nums    <- seq(0.1, 0.9, by = 0.1)
metrics <- map_dfr(nums, function(u) {
  pred_thr     <- if_else(y_prob >= u, 1, 0)
  tp           <- sum(pred_thr == 1 & datos_aula$aprueba == 1)
  tn           <- sum(pred_thr == 0 & datos_aula$aprueba == 0)
  fp           <- sum(pred_thr == 1 & datos_aula$aprueba == 0)
  fn           <- sum(pred_thr == 0 & datos_aula$aprueba == 1)
  sensibilidad <- tp / (tp + fn)
  especificidad <- tn / (tn + fp)
  tibble(Umbral = u, Sensibilidad = sensibilidad, Especificidad = especificidad)
})
knitr::kable(metrics, digits = 3,
             caption = 'Sensibilidad y Especificidad en diferentes umbrales')
Sensibilidad y Especificidad en diferentes umbrales
Umbral Sensibilidad Especificidad
0.1 1.000 0.000
0.2 1.000 0.000
0.3 1.000 0.000
0.4 1.000 0.027
0.5 0.991 0.108
0.6 0.938 0.162
0.7 0.761 0.378
0.8 0.407 0.703
0.9 0.053 1.000

5.2. Curva ROC, AUC y trade-off

La Curva ROC (Receiver Operating Characteristic) muestra la relación entre la tasa de verdaderos positivos (sensibilidad) y la tasa de falsos positivos \((1 - \text{especificidad})\) para todos los umbrales posibles.

roc_obj <- roc(datos_aula$aprueba, y_prob)
plot(roc_obj, print.auc = TRUE, main = 'Curva ROC (Ejercicio Aula)')

  • AUC (Área Bajo la Curva) mide la capacidad global del modelo para distinguir entre clases.

    • AUC = 0.5: modelo equivalente al azar.
    • AUC = 1: perfecta separación.

5.3. Pseudo-\(R^2\) y deviance residual

En modelos lineales, usamos \(R^2\) para medir la proporción de varianza explicada. En regresión logística, empleamos alternativas:

  1. Deviance:

    • Deviance nula (\(D_0\)): se calcula con un modelo que incluye solo el intercepto.
    • Deviance residual (\(D_f\)): deviance del modelo final.
    • Menor deviance indica mejor ajuste.
  2. Pseudo-\(R^2\) de McFadden:

    \[ R^2_{\text{McF}} = 1 - \frac{\ell(\hat{\boldsymbol{\beta}})}{\ell(\hat{\boldsymbol{\beta}}_0)}, \]

    donde \(\ell(\hat{\boldsymbol{\beta}})\) es la log-verosimilitud del modelo completo y \(\ell(\hat{\boldsymbol{\beta}}_0)\) la del modelo solo con intercepto.

ll_full <- logLik(mod_aula)
mod_null <- update(mod_aula, . ~ 1)
ll_null <- logLik(mod_null)
pseudoR2 <- 1 - as.numeric(ll_full / ll_null)
pseudoR2
## [1] 0.05044384
  1. Prueba de razón de verosimilitud: Compara deviance nula y residual para contrastar si al incluir predictores se mejora significativamente el ajuste.
# Prueba de razón de verosimilitud entre modelos
test_lr <- anova(mod_null, mod_aula, test = 'Chisq')
test_lr
## Analysis of Deviance Table
## 
## Model 1: aprueba ~ 1
## Model 2: aprueba ~ asistencias + nota_parcial
##   Resid. Df Resid. Dev Df Deviance Pr(>Chi)  
## 1       149     167.59                       
## 2       147     159.14  2    8.454   0.0146 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

5.4. Gráficos de diagnóstico (residuos e influencia)

  1. Residuos de Pearson y residuos deviance: para detectar observaciones mal ajustadas.
  2. Valores de influencia (distancia de Cook) para identificar casos atípicos.
par(mfrow = c(1,2))
# Residuos deviance vs fitted
def_resid   <- residuals(mod_aula, type = 'deviance')
fitted_vals <- fitted(mod_aula)
plot(fitted_vals, def_resid,
     xlab = 'Predicción (fitted)', ylab = 'Residuos deviance',
     main = 'Residuos deviance vs fitted')
abline(h = 0, lty = 2)
# Diagrama de influencia (distancia de Cook)
cook_d <- cooks.distance(mod_aula)
plot(cook_d, type = 'h',
     xlab = 'Índice de observación', ylab = 'Distancia de Cook',
     main = 'Distancia de Cook')
abline(h = 4 / length(cook_d), col = 'red', lty = 2)


6. Visualización avanzada

Más allá de los gráficos básicos, podemos aprovechar plotly para crear visualizaciones interactivas.

6.1. Superficie de probabilidad para dos predictores

# Creamos una malla de valores para asistencias y nota_parcial (ejemplo aula)
grid <- expand.grid(
  asistencias   = seq(min(datos_aula$asistencias), max(datos_aula$asistencias), length.out = 50),
  nota_parcial = seq(min(datos_aula$nota_parcial), max(datos_aula$nota_parcial), length.out = 50)
)
grid$prob <- predict(mod_aula, newdata = grid, type = 'response')
# Convertimos en matriz
mat_prob <- matrix(grid$prob, nrow = 50, ncol = 50)
# Gráfico 3D interactivo
plot_ly(x = ~unique(grid$asistencias),
        y = ~unique(grid$nota_parcial),
        z = ~mat_prob) %>%
  add_surface() %>%
  layout(title = 'Superficie de Probabilidad - Ejercicio Aula',
         scene = list(
           xaxis = list(title = 'Asistencias'),
           yaxis = list(title = 'Nota parcial'),
           zaxis = list(title = 'Probabilidad de aprobar')
         ))

6.2. Plots interactivos con plotly

  • Podemos rotar, hacer “zoom” y acercar detalles.
  • Útil para presentaciones en clase o para exploración de datos por parte de estudiantes.

7. Resumen de pasos prácticos

  1. Verificar variable dependiente: Asegurarse de que sea binaria.

  2. Exploración inicial: Tablas de frecuencia, histogramas para variables continuas, boxplots.

  3. Construir modelo:

    glm(Y ~ X1 + X2 + ..., family = binomial)
  4. Interpretar coeficientes:

    • Observar signos y magnitudes de \(\hat{\beta}\).
    • Calcular \(\exp(\hat{\beta})\) para obtener odds ratios.
    • Revisar intervalos de confianza.
  5. Validación interna:

    • Curva ROC y AUC.
    • Matriz de confusión en un umbral (inicial ≃ 0{,}5 y explorar otros).
    • Cálculo de sensibilidad y especificidad.
    • Cálculo de pseudo-\(R^2\) y pruebas de verosimilitud.
  6. Diagnóstico:

    • Gráficos de residuos y leverage.
    • Identificar casos influyentes (distancia de Cook).
  7. Visualización:

    • Curvas de probabilidad en función de un predictor.
    • Superficies de probabilidad interactivas.
  8. Comunicación:

    • Presentar resultados en lenguaje comprensible: “aumentar \(X\) en una unidad multiplica las probabilidades por \(OR\)”.
    • Utilizar gráficas y tablas que resuman hallazgos.
  9. Ejercicios adicionales: Proponer a los estudiantes que experimenten con diferentes conjuntos de datos y escenarios.


8. Conclusiones y ejercicios adicionales

  • La regresión logística es la herramienta de elección cuando la variable respuesta es binaria.
  • La transformación logit-odds garantiza predicciones válidas entre 0 y 1.
  • Interpretar coeficientes en términos de Odds Ratios facilita comunicar el impacto de las variables.
  • Validar el modelo con curva ROC, AUC, matriz de confusión y prueba de verosimilitud asegura un análisis robusto.
  • Las visualizaciones interactivas con plotly enriquecen el aprendizaje en el aula.

Ejercicios adicionales sugeridos:

  1. Predicción de setosa vs no-setosa (iris): Ajusta un modelo logístico con Sepal.Length y Sepal.Width. Compara AUC y matriz de confusión.
  2. Regularización L1 (LASSO): Usar glmnet para un modelo con penalización L1. Comparar rendimiento vs modelo sin penalización.
  3. Deserción estudiantil: Simular variables como horas de estudio, ingreso familiar, acceso a internet y predecir probabilidad de deserción.
  4. Modelo ordinal: Explorar casos donde la variable respuesta tiene más de dos categorías (Regresión logística multinomial con nnet::multinom).

9. Referencias bibliográficas

  1. Agresti, A. (2018). Statistical Methods for the Social Sciences (5ª ed.). Pearson.
  2. Hosmer, D. W., Lemeshow, S., & Sturdivant, R. X. (2013). Applied Logistic Regression (3ª ed.). Wiley.
  3. Menard, S. (2002). Applied Logistic Regression Analysis (2ª ed.). SAGE.
  4. Kleinbaum, D. G., & Klein, M. (2010). Logistic Regression: A Self-Learning Text (3ª ed.). Springer.
  5. James, G., Witten, D., Hastie, T., & Tibshirani, R. (2021). An Introduction to Statistical Learning (2ª ed.). Springer. (Capítulo 4: Clasificación).
  6. Harrell, F. E. (2015). Regression Modeling Strategies: With Applications to Linear Models, Logistic and Ordinal Regression, and Survival Analysis (2ª ed.). Springer.
  7. Wickham, H. (2016). ggplot2: Elegant Graphics for Data Analysis. Springer.
  8. Tidyverse. (2023). R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. O’Reilly Media.