Introducción

El presente informe tiene como propósito realizar un análisis de regresión y ANOVA sobre un conjunto de datos simulados que evalúan factores asociados al rendimiento académico de estudiantes. Teniendo en cuenta lo anterior, se implementarán Modelos de Regresión Lineal Múltiple y Modelos de Regresión Logística Simple y Múltiple.

Regresión Lineal Múltiple

En esta sección se realizará un análisis de regresión lineal múltiple. Se utilizarán variables cuantitativas relacionadas con hábitos de estudio y características personales. El objetivo es identificar cuáles de estos factores tienen un impacto significativo sobre la calificación final (Exam_Score).

2. Carga de librerías y datos

Se cargan las librerías necesarias y se importa la base de datos StudentPerformanceFactors.csv.

# Cargar librerías necesarias
library(readr)    # Para leer archivos CSV
library(dplyr)    # Para manipulación de datos
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# Leer la base de datos
datos <- read_csv("StudentPerformanceFactors.csv")
## Rows: 6607 Columns: 20
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (13): Parental_Involvement, Access_to_Resources, Extracurricular_Activit...
## dbl  (7): Hours_Studied, Attendance, Sleep_Hours, Previous_Scores, Tutoring_...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Ver las primeras filas
head(datos)
## # A tibble: 6 × 20
##   Hours_Studied Attendance Parental_Involvement Access_to_Resources
##           <dbl>      <dbl> <chr>                <chr>              
## 1            23         84 Low                  High               
## 2            19         64 Low                  Medium             
## 3            24         98 Medium               Medium             
## 4            29         89 Low                  Medium             
## 5            19         92 Medium               Medium             
## 6            19         88 Medium               Medium             
## # ℹ 16 more variables: Extracurricular_Activities <chr>, Sleep_Hours <dbl>,
## #   Previous_Scores <dbl>, Motivation_Level <chr>, Internet_Access <chr>,
## #   Tutoring_Sessions <dbl>, Family_Income <chr>, Teacher_Quality <chr>,
## #   School_Type <chr>, Peer_Influence <chr>, Physical_Activity <dbl>,
## #   Learning_Disabilities <chr>, Parental_Education_Level <chr>,
## #   Distance_from_Home <chr>, Gender <chr>, Exam_Score <dbl>
# Ver estructura de la base
str(datos)
## spc_tbl_ [6,607 × 20] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ Hours_Studied             : num [1:6607] 23 19 24 29 19 19 29 25 17 23 ...
##  $ Attendance                : num [1:6607] 84 64 98 89 92 88 84 78 94 98 ...
##  $ Parental_Involvement      : chr [1:6607] "Low" "Low" "Medium" "Low" ...
##  $ Access_to_Resources       : chr [1:6607] "High" "Medium" "Medium" "Medium" ...
##  $ Extracurricular_Activities: chr [1:6607] "No" "No" "Yes" "Yes" ...
##  $ Sleep_Hours               : num [1:6607] 7 8 7 8 6 8 7 6 6 8 ...
##  $ Previous_Scores           : num [1:6607] 73 59 91 98 65 89 68 50 80 71 ...
##  $ Motivation_Level          : chr [1:6607] "Low" "Low" "Medium" "Medium" ...
##  $ Internet_Access           : chr [1:6607] "Yes" "Yes" "Yes" "Yes" ...
##  $ Tutoring_Sessions         : num [1:6607] 0 2 2 1 3 3 1 1 0 0 ...
##  $ Family_Income             : chr [1:6607] "Low" "Medium" "Medium" "Medium" ...
##  $ Teacher_Quality           : chr [1:6607] "Medium" "Medium" "Medium" "Medium" ...
##  $ School_Type               : chr [1:6607] "Public" "Public" "Public" "Public" ...
##  $ Peer_Influence            : chr [1:6607] "Positive" "Negative" "Neutral" "Negative" ...
##  $ Physical_Activity         : num [1:6607] 3 4 4 4 4 3 2 2 1 5 ...
##  $ Learning_Disabilities     : chr [1:6607] "No" "No" "No" "No" ...
##  $ Parental_Education_Level  : chr [1:6607] "High School" "College" "Postgraduate" "High School" ...
##  $ Distance_from_Home        : chr [1:6607] "Near" "Moderate" "Near" "Moderate" ...
##  $ Gender                    : chr [1:6607] "Male" "Female" "Male" "Male" ...
##  $ Exam_Score                : num [1:6607] 67 61 74 71 70 71 67 66 69 72 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   Hours_Studied = col_double(),
##   ..   Attendance = col_double(),
##   ..   Parental_Involvement = col_character(),
##   ..   Access_to_Resources = col_character(),
##   ..   Extracurricular_Activities = col_character(),
##   ..   Sleep_Hours = col_double(),
##   ..   Previous_Scores = col_double(),
##   ..   Motivation_Level = col_character(),
##   ..   Internet_Access = col_character(),
##   ..   Tutoring_Sessions = col_double(),
##   ..   Family_Income = col_character(),
##   ..   Teacher_Quality = col_character(),
##   ..   School_Type = col_character(),
##   ..   Peer_Influence = col_character(),
##   ..   Physical_Activity = col_double(),
##   ..   Learning_Disabilities = col_character(),
##   ..   Parental_Education_Level = col_character(),
##   ..   Distance_from_Home = col_character(),
##   ..   Gender = col_character(),
##   ..   Exam_Score = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

3. Selección de variables relevantes

Para este modelo se seleccionan las siguientes variables explicativas:

  • Hours_Studied: Horas de estudio semanales.
  • Sleep_Hours: Horas de sueño promedio por noche.
  • Tutoring_Sessions: Número de sesiones de tutoría por mes.
  • Previous_Scores: Puntajes anteriores.
  • Attendance: Porcentaje de asistencia a clases.
  • Physical_Activity: Horas de actividad física semanal.

La variable dependiente será Exam_Score.

# Seleccionar solo las variables necesarias
datos_modelo <- datos %>%
  select(Hours_Studied, Attendance, Previous_Scores, Sleep_Hours, Tutoring_Sessions, Exam_Score, Physical_Activity)

# Eliminar filas con NA
datos_modelo <- na.omit(datos_modelo)

# Ver estructura y primeras filas
str(datos_modelo)
## tibble [6,607 × 7] (S3: tbl_df/tbl/data.frame)
##  $ Hours_Studied    : num [1:6607] 23 19 24 29 19 19 29 25 17 23 ...
##  $ Attendance       : num [1:6607] 84 64 98 89 92 88 84 78 94 98 ...
##  $ Previous_Scores  : num [1:6607] 73 59 91 98 65 89 68 50 80 71 ...
##  $ Sleep_Hours      : num [1:6607] 7 8 7 8 6 8 7 6 6 8 ...
##  $ Tutoring_Sessions: num [1:6607] 0 2 2 1 3 3 1 1 0 0 ...
##  $ Exam_Score       : num [1:6607] 67 61 74 71 70 71 67 66 69 72 ...
##  $ Physical_Activity: num [1:6607] 3 4 4 4 4 3 2 2 1 5 ...
head(datos_modelo)
## # A tibble: 6 × 7
##   Hours_Studied Attendance Previous_Scores Sleep_Hours Tutoring_Sessions
##           <dbl>      <dbl>           <dbl>       <dbl>             <dbl>
## 1            23         84              73           7                 0
## 2            19         64              59           8                 2
## 3            24         98              91           7                 2
## 4            29         89              98           8                 1
## 5            19         92              65           6                 3
## 6            19         88              89           8                 3
## # ℹ 2 more variables: Exam_Score <dbl>, Physical_Activity <dbl>

4. Ajuste del modelo de regresión

Se ajusta el modelo lineal múltiple usando la función lm():

# Ajustar el modelo
modelo <- lm(Exam_Score ~ Hours_Studied + Sleep_Hours + Tutoring_Sessions + Previous_Scores + Attendance + Physical_Activity, data = datos_modelo)

# Resumen del modelo
summary(modelo)
## 
## Call:
## lm(formula = Exam_Score ~ Hours_Studied + Sleep_Hours + Tutoring_Sessions + 
##     Previous_Scores + Attendance + Physical_Activity, data = datos_modelo)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.4391 -1.1316 -0.1619  0.8435 30.9951 
## 
## Coefficients:
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)       40.927132   0.338284 120.985  < 2e-16 ***
## Hours_Studied      0.291579   0.005069  57.517  < 2e-16 ***
## Sleep_Hours       -0.018022   0.020686  -0.871    0.384    
## Tutoring_Sessions  0.493505   0.024679  19.997  < 2e-16 ***
## Previous_Scores    0.048123   0.002110  22.809  < 2e-16 ***
## Attendance         0.197978   0.002631  75.262  < 2e-16 ***
## Physical_Activity  0.143997   0.029449   4.890 1.03e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.467 on 6600 degrees of freedom
## Multiple R-squared:  0.5982, Adjusted R-squared:  0.5979 
## F-statistic:  1638 on 6 and 6600 DF,  p-value: < 2.2e-16

Interpretación del modelo

- Intercepto (40.93): Representa el valor esperado del puntaje del examen (Exam_Score) cuando todas las variables predictoras son cero. Aunque no tiene interpretación práctica directa (por ejemplo, estudiar 0 horas o tener 0 de asistencia no es realista), es necesario para construir la ecuación del modelo.

- Hours_Studied (0.29): Por cada hora adicional de estudio por semana, el puntaje del examen aumenta en promedio 0.29 unidades, manteniendo constantes las demás variables. Este coeficiente es altamente significativo (p < 0.001).

- Sleep_Hours (-0.018): Por cada hora adicional de sueño por noche, el puntaje del examen disminuye en promedio 0.018 unidades, aunque este efecto no es estadísticamente significativo (p = 0.384). No tendría un gran impacto si se excluyera del modelo.

- Tutoring_Sessions (0.49): Cada sesión adicional de tutoría al mes se asocia con un incremento promedio de 0.49 puntos en el examen, lo cual es estadísticamente significativo (p < 0.001).

- Previous_Scores (0.048): Por cada punto adicional en los exámenes anteriores, el puntaje actual aumenta en promedio 0.048 unidades, siendo significativo (p < 0.001).

- Attendance (0.198): Por cada punto porcentual adicional de asistencia, el puntaje del examen aumenta en promedio 0.198 puntos. Este es uno de los predictores más relevantes del modelo y muy significativo (p < 0.001).

- Physical_Activity (0.144): Por cada hora adicional de actividad física por semana, el puntaje del examen aumenta en promedio 0.144 unidades. Este efecto también es estadísticamente significativo (p < 0.001).

En resumen:

  • Las variables Hours_Studied, Tutoring_Sessions, Previous_Scores, Attendance y Physical_Activity resultaron significativas (p < 0.05).
  • Sleep_Hours no fue estadísticamente significativa.

5. Diagnóstico del modelo

## Cargar paquetes necesarios
library(lmtest)     # Para pruebas de Breusch-Pagan y Durbin-Watson
## Cargando paquete requerido: zoo
## 
## Adjuntando el paquete: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(car)        # Para multicolinealidad (VIF)
## Cargando paquete requerido: carData
## 
## Adjuntando el paquete: 'car'
## The following object is masked from 'package:dplyr':
## 
##     recode

5.1 Supuesto de linealidad

Se analiza mediante el gráfico de residuos vs valores ajustados:

# Gráfico de residuos vs valores ajustados
plot(modelo$fitted.values, modelo$residuals,
     main = "Residuos vs Valores Ajustados",
     xlab = "Valores Ajustados", ylab = "Residuos",
     pch = 19, col = "darkgreen")
abline(h = 0, col = "blue", lty = 2)

Interpretación:

  • La dispersión aleatoria alrededor de la línea cero sugiere linealidad apropiada.

5.2 Independencia de los errores

Se utiliza la prueba de Durbin-Watson:

dwtest(modelo)
## 
##  Durbin-Watson test
## 
## data:  modelo
## DW = 2.0002, p-value = 0.504
## alternative hypothesis: true autocorrelation is greater than 0
# Si el estadístico está cerca de 2 y el p-valor > 0.05, se cumple la independencia.

Interpretación:

  • El estadístico DW ≈ 2 y p > 0.05, lo cual indica independencia de errores.

5.3 Homocedasticidad (varianza constante)

Prueba de Breusch-Pagan:

bptest(modelo)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo
## BP = 5.9304, df = 6, p-value = 0.431
# p > 0.05 indica homocedasticidad (buen indicador).

Interpretación:

  • p > 0.05 indica que no se rechaza la hipótesis de homocedasticidad.

5.4 Multicolinealidad

Se evalúa el VIF (Variance Inflation Factor):

vif(modelo)
##     Hours_Studied       Sleep_Hours Tutoring_Sessions   Previous_Scores 
##          1.001048          1.001019          1.001032          1.001815 
##        Attendance Physical_Activity 
##          1.001474          1.000988
# Valores VIF < 5 no indican problemas serios de colinealidad.

Interpretación:

  • Todos los VIF < 5 → no hay multicolinealidad grave.

5.5 Normalidad de los errores

Gráficos e interpretación:

Histograma de residuos:

hist(modelo$residuals,
     col = "lightblue", main = "Histograma de Residuos")

Interpretación:

  • Asimetría positiva (cola derecha alargada).

Gráfico Q-Q:

qqnorm(modelo$residuals)
qqline(modelo$residuals, col = "red")

# Los puntos deben seguir la línea roja si los residuos son normales.

Interpretación:

  • Se observa una desviación clara en la cola superior (residuos muy altos).

Prueba de Kolmogorov-Smirnov:

ks.test(modelo$residuals, "pnorm", mean = mean(modelo$residuals), sd = sd(modelo$residuals))
## Warning in ks.test.default(modelo$residuals, "pnorm", mean =
## mean(modelo$residuals), : ties should not be present for the one-sample
## Kolmogorov-Smirnov test
## 
##  Asymptotic one-sample Kolmogorov-Smirnov test
## 
## data:  modelo$residuals
## D = 0.14262, p-value < 2.2e-16
## alternative hypothesis: two-sided
# p > 0.05 indica que no se rechaza la hipótesis de normalidad.

Interpretación:

  • Se rechaza la hipótesis de normalidad.

Aunque el test sugiere que los residuos no siguen distribución normal (p < 0.05), esto no invalida el modelo debido al gran tamaño de la muestra (n = 6607).

6. Conclusiones

  • El modelo explica aproximadamente el 60% de la variabilidad en el puntaje del examen (R² ajustado = 0.598).
  • Las variables más influyentes fueron Hours_Studied, Attendance y Previous_Scores.
  • Todos los supuestos de Gauss-Markov se cumplen, excepto la normalidad estricta de los errores, lo cual es aceptable dado el tamaño muestral.

Este modelo permite una comprensión académica sólida del proceso de regresión lineal múltiple aplicada a datos educativos.

Análisis de Varianza (ANOVA)

Se aplica el análisis de varianza al modelo de regresión lineal múltiple para determinar si cada una de las variables predictoras contribuye significativamente a explicar la variabilidad observada en el puntaje del examen (Exam_Score).

# Tabla ANOVA del modelo ajustado
anova(modelo)
## Analysis of Variance Table
## 
## Response: Exam_Score
##                     Df Sum Sq Mean Sq   F value    Pr(>F)    
## Hours_Studied        1  19840   19840 3259.8322 < 2.2e-16 ***
## Sleep_Hours          1     48      48    7.8881  0.004991 ** 
## Tutoring_Sessions    1   2645    2645  434.6138 < 2.2e-16 ***
## Previous_Scores      1   2747    2747  451.3550 < 2.2e-16 ***
## Attendance           1  34391   34391 5650.5130 < 2.2e-16 ***
## Physical_Activity    1    146     146   23.9098 1.033e-06 ***
## Residuals         6600  40169       6                        
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Interpretación:

El modelo muestra que todas las variables incluidas son estadísticamente significativas (p < 0.01), lo que indica que cada una de ellas explica una parte importante de la variación en los puntajes de examen.

Las variables con mayor capacidad explicativa son:

  • Attendance (F = 5650.5)

  • Hours_Studied (F = 3259.8)

  • Previous_Scores y Tutoring_Sessions también presentan valores F elevados, confirmando su relevancia.

Aunque Sleep_Hours tiene un F más bajo (7.89), también es significativa (p = 0.00499), por lo que su efecto, aunque menor, no debe ser descartado.

Explicación de cada columna

  • Df (Grados de libertad): Es igual a 1 para cada predictor porque se estima un coeficiente por variable. Para los residuos, es n - k - 1 = 6607 - 6 - 1 = 6600.

  • Sum Sq (Suma de cuadrados): Representa la cantidad total de variabilidad que cada variable explica del Exam_Score.

  • Mean Sq (Media cuadrática): Se obtiene al dividir la suma de cuadrados entre los grados de libertad (en este caso, son iguales a Sum Sq porque Df = 1).

  • F value: Compara la variabilidad explicada por el predictor con la variabilidad no explicada (residual). Valores altos indican fuerte efecto del predictor sobre la respuesta.

  • Pr(>F): p-valor asociado al estadístico F. Valores menores a 0.05 indican que el predictor es estadísticamente significativo.

Variable Sum Sq ¿Qué significa?
Hours_Studied 19840 Esta variable explica 19.840 unidades de variabilidad total del Exam_Score. Es una gran porción del total. El modelo dice: “solo con saber cuántas horas estudia un estudiante, ya puedo explicar 19840 unidades de la variabilidad de los puntajes”.
Sleep_Hours 48 Esta variable explica muy poca variabilidad (solo 48 unidades). Aunque es estadísticamente significativa (p = 0.0049), su impacto práctico es bajo comparado con otras variables.
Tutoring_Sessions 2645 Las sesiones de tutoría explican 2645 unidades de variabilidad. Es una contribución moderada-alta. Es una variable importante en términos de predicción.
Previous_Scores 2747 Las notas anteriores explican 2747 unidades, lo que indica que tienen una fuerte relación con el rendimiento actual. Es un predictor muy relevante.
Attendance 34391 Es la variable más poderosa del modelo: explica 34391 unidades de variabilidad del Exam_Score. Es decir, más de un tercio de toda la variación se debe a la asistencia.
Physical_Activity 146 Explica 146 unidades de variación. Es estadísticamente significativa, pero su contribución es baja en comparación con las variables anteriores. Aun así, su efecto no debe descartarse.

Estos valores nos permiten cuantificar y comparar la importancia relativa de cada predictor. Por ejemplo:

  • Aunque tanto Hours_Studied como Attendance son significativos, Attendance explica casi el doble de la variabilidad (34391 vs 19840).

  • En cambio, Sleep_Hours y Physical_Activity, aunque significativos, explican poca variación absoluta, por lo que pueden tener un efecto más complementario o moderador.

Teniendo en cuenta lo anterior:

  • Todas estas cantidades se comparan con la suma de cuadrados residual (40169), que es lo que el modelo no puede explicar.

Interpretación variable por variable

  • Hours_Studied: F = 3259.83, p < 2.2e-16

Es un predictor altamente significativo. Explica una gran parte de la variabilidad del puntaje del examen. Cada hora de estudio adicional por semana tiene un fuerte impacto positivo.

  • Sleep_Hours: F = 7.89, p = 0.004991

Aunque su F es menor, es estadísticamente significativo. Su efecto es leve, pero consistente. Puede tener un impacto menor o interactuar con otras variables.

  • Tutoring_Sessions: F = 434.61, p < 2.2e-16

Predictor con alto poder explicativo. Aumentar las sesiones de tutoría mensuales está fuertemente asociado a un mejor desempeño.

  • Previous_Scores: F = 451.36, p < 2.2e-16

Tiene un efecto claro y significativo. Las calificaciones anteriores predicen de forma robusta el rendimiento actual.

  • Attendance: F = 5650.51, p < 2.2e-16

Es el predictor más relevante del modelo. La asistencia tiene la mayor suma de cuadrados y F más alto, lo que indica que explica gran parte de la varianza del examen.

  • Physical_Activity: F = 23.91, p = 1.033e-06

Tiene un efecto moderado pero significativo. Sugiere que la actividad física también contribuye de forma positiva al rendimiento académico.

  • Residuals:

La suma de cuadrados residual (40169) representa la parte de la variabilidad que el modelo no logra explicar.

Conclusión del ANOVA:

El análisis ANOVA indica que todas las variables predictoras seleccionadas son estadísticamente significativas y contribuyen a explicar la variación en los puntajes de examen (todas con p < 0.01).

Las variables Attendance, Hours_Studied, Previous_Scores y Tutoring_Sessions son las más relevantes en términos de magnitud de varianza explicada. Aunque Sleep_Hours y Physical_Activity explican una porción menor, su efecto también es significativo y complementario dentro del modelo.

Este análisis respalda la validez del modelo ajustado y confirma que el conjunto de variables incluidas permite explicar de forma significativa y coherente el rendimiento académico de los estudiantes.

Regresión Logística Simple

✅ Paso 1: Crear una variable binaria

Vamos a crear una variable llamada High_Score, que tome el valor 1 si el estudiante aprobó el examen, utilizando como punto de corte una nota ≥ 70, y 0 para indicar que el estudiante reprobó, es decir obtuvo una nora < 70.

datos_modelo <- datos_modelo %>%
  mutate(High_Score = ifelse(Exam_Score >= 70, 1, 0))

✅ Paso 2: Ajustar el modelo de regresión logística simple

Ahora, vamos a predecir la probabilidad de High_Score = 1 en función de una sola variable, como Hours_Studied:

modelo_logit_simple <- glm(High_Score ~ Hours_Studied, data = datos_modelo, family = binomial)
summary(modelo_logit_simple)
## 
## Call:
## glm(formula = High_Score ~ Hours_Studied, family = binomial, 
##     data = datos_modelo)
## 
## Coefficients:
##                Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   -4.728779   0.139129  -33.99   <2e-16 ***
## Hours_Studied  0.169378   0.006077   27.87   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 7371.3  on 6606  degrees of freedom
## Residual deviance: 6385.4  on 6605  degrees of freedom
## AIC: 6389.4
## 
## Number of Fisher Scoring iterations: 4

Interpretación del modelo logístico:

  • Modelo ajustado:

\[ \log\left( \frac{p}{1-p} \right)= -4.7288 + 0.1694 \cdot \text{Hours_Studied} \]

  • Interpretación de los coeficientes:

(Intercept) = -4.7288

Este es el valor del logit cuando Hours_Studied = 0. No tiene una interpretación práctica directa porque estudiar 0 horas puede no ser realista.

Hours_Studied = 0.1694

Cada hora adicional de estudio aumenta el logit de obtener una nota alta en 0.1694 unidades.

  • Interpretación de la desviancia

La desviancia es una medida estadística utilizada para evaluar la calidad del ajuste de modelos de regresión generalizada, especialmente la regresión logística. Se interpreta como una medida de discrepancia entre los datos observados y el modelo ajustado.

Fundamento matemático:

La fórmula general es:

\[ \text{Desviancia} = -2 \cdot \log(\text{verosimilitud del modelo}) \]

Pero también puede expresarse como:

\[ \text{Desviancia} = 2 \cdot \left( \log L_{\text{saturado}} - \log L_{\text{modelo}} \right) \]

Donde:

\[ \ L_{\text{saturado}} \]

Es la verosimilitud del modelo saturado, un modelo hipotético que predice perfectamente cada valor observado.

\[ \ L_{\text{modelo}} \]

Es la verosimilitud del modelo ajustado.

Al restarlas, se obtiene una medida de “distancia” entre lo que predice nuestro modelo y lo que predeciría el modelo perfecto.

Interpretación general:

  • Desviancia baja → el modelo se ajusta bien a los datos.

  • Desviancia alta → el modelo se ajusta mal (hay mayor discrepancia entre lo observado y lo predicho).

Tipos de Desviancia:

Al usar summary obtenemos los siguientes tipos de Desviancia:

  1. Null deviance (Desviancia nula):
  • Es la desviancia de un modelo sin predictores, sólo con el intercepto.

  • Representa el peor escenario, donde se predice la misma probabilidad para todos.

  1. Residual deviance (Desviancia residual):
  • Es la desviancia del modelo ajustado, incluyendo los predictores.

  • Mide qué tanto error comete el modelo al estimar las probabilidades.

Si el modelo ajustado es mejor que el modelo nulo, la residual deviance debe ser mucho menor que la null deviance. Por lo anterior, se puede interpretar la diferencia entre ambas como una medida de mejora del ajuste.

Teniendo en cuenta lo anterior obtuvimos:

  • Null deviance: 7371.3

  • Residual deviance: 6385.4

Esto significa que:

  • El modelo sin predictores (sólo intercepto) tiene una desviancia de 7371.3.

  • Al incluir Hours_Studied, la desviancia baja a 6385.4 → eso indica una mejora en el ajuste.

Por lo anterior, el valor obtenido (6385.4) representa qué tan lejos está nuestro modelo (con solo Hours_Studied) del modelo ideal que ajusta perfectamente los datos:

  • Aunque es un modelo simple y muestra relación significativa, su desviancia es relativamente alta porque no está capturando suficiente información de otras variables relevantes. A pesar de esto, el modelo es útil para mostrar la relación positiva y significativa entre las horas de estudio y la probabilidad de aprobar.

✅ Paso 3: Interpretar los resultados

exp(coef(modelo_logit_simple))
##   (Intercept) Hours_Studied 
##   0.008837255   1.184567644

📌 Coeficientes del modelo:

  • (Intercept): 0.0088 → Corresponde a los odds de tener nota alta cuando Hours_Studied = 0. Muy bajo, como es esperable.

  • Hours_Studied: 1.1846 → Por cada hora adicional de estudio, los odds de obtener nota ≥ 70 aumentan un 18.46%.

Esto equivale a un aumento de los odds de:

\[ e^{0.1694} \approx 1.1845 \]

Es decir, por cada hora extra de estudio, los odds de obtener una nota alta aumentan en un 18.45%.

✅ Paso 4: Predecir probabilidades

predict(modelo_logit_simple, newdata = data.frame(Hours_Studied = 15), type = "response")
##         1 
## 0.1008232
  • Resultado: 0.1008:

Un estudiante que estudia 15 horas tiene una probabilidad estimada del 10.08% de obtener una nota alta (según el umbral ≥ 70 definido).

✅ Paso 5: Clasificación y evaluación

predicciones <- ifelse(predict(modelo_logit_simple, type = "response") >= 0.5, 1, 0)
table(Predicho = predicciones, Real = datos_modelo$High_Score)
##         Real
## Predicho    0    1
##        0 4708 1218
##        1  274  407

Interpretación:

  • VP (407): Casos correctamente clasificados como nota alta.

  • VN (4708): Casos correctamente clasificados como nota baja.

  • FP (274): Casos que fueron clasificados como alta nota, pero no lo eran.

  • FN (1218): Casos con nota alta que fueron clasificados incorrectamente como nota baja.

library(dplyr)
library(ggplot2)

# Crear etiquetas legibles
datos_modelo$High_Score_Factor <- factor(datos_modelo$High_Score, levels = c(0, 1), labels = c("No Aprobado", "Aprobado"))
pred_factor <- factor(predicciones, levels = c(0, 1), labels = c("No Aprobado", "Aprobado"))

# Crear tabla de frecuencia
matriz <- table(`Clase real` = datos_modelo$High_Score_Factor, `Clase predicha` = pred_factor)
matriz
##              Clase predicha
## Clase real    No Aprobado Aprobado
##   No Aprobado        4708      274
##   Aprobado           1218      407
# Convertir a data frame largo
matriz_df <- as.data.frame(matriz)

# Opcional: verificar estructura
str(matriz_df)
## 'data.frame':    4 obs. of  3 variables:
##  $ Clase.real    : Factor w/ 2 levels "No Aprobado",..: 1 2 1 2
##  $ Clase.predicha: Factor w/ 2 levels "No Aprobado",..: 1 1 2 2
##  $ Freq          : int  4708 1218 274 407
ggplot(matriz_df, aes(x = `Clase.real`, y = `Clase.predicha`, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), size = 8) +
  scale_fill_gradient(low = "#deebf7", high = "#3182bd") +
  labs(title = "Matriz de confusion",
       x = "Real",
       y = "Predicho",
       fill = "Frecuencia") +
  theme_minimal(base_size = 14)

Métricas de desempeño del modelo (precisión, sensibilidad, especificidad):

Esto nos permite saber qué tan bien clasifica el modelo más allá del conteo

VP <- sum(predicciones == 1 & datos_modelo$High_Score == 1)
VN <- sum(predicciones == 0 & datos_modelo$High_Score == 0)
FP <- sum(predicciones == 1 & datos_modelo$High_Score == 0)
FN <- sum(predicciones == 0 & datos_modelo$High_Score == 1)

precision <- (VP + VN) / (VP + VN + FP + FN)
sensitivity <- VP / (VP + FN)
specificity <- VN / (VN + FP)
f1_score <- 2 * (precision * sensitivity) / (precision + sensitivity)

cat("Precision:", round(precision, 3), "\nSensibilidad:", round(sensitivity, 3), "\nEspecificidad:", round(specificity, 3), "\nF1-Score:", round(f1_score, 3))
## Precision: 0.774 
## Sensibilidad: 0.25 
## Especificidad: 0.945 
## F1-Score: 0.378
Métrica Valor Interpretación
Precisión 0.774 El 77.4% de todas las predicciones del modelo fueron correctas.
Sensibilidad 0.25 El modelo detectó correctamente solo el 25% de los estudiantes que aprueban.
Especificidad 0.945 El modelo identificó correctamente el 94.5% de los que reprueban.
F1-Score 0.378 Promedio armónico de precisión y sensibilidad: indica bajo balance general.

🧠 Interpretación

  • El modelo es muy bueno para detectar quiénes van a perder (especificidad).

  • Pero es muy malo para detectar quiénes van a ganar (sensibilidad baja).

  • Esto indica un problema de desbalance en las clases: probablemente hay muchos más estudiantes con nota < 70 que con nota ≥ 70.

  • Además, usar un umbral de 0.5 puede no ser el mejor criterio cuando las clases están desbalanceadas.

Probar otro umbral:

Cambiamos el umbral a 0.3

# Cambiar el umbral a 0.3
predicciones_03 <- ifelse(predict(modelo_logit_simple, type = "response") >= 0.3, 1, 0)

VP <- sum(predicciones_03 == 1 & datos_modelo$High_Score == 1)
VN <- sum(predicciones_03 == 0 & datos_modelo$High_Score == 0)
FP <- sum(predicciones_03 == 1 & datos_modelo$High_Score == 0)
FN <- sum(predicciones_03 == 0 & datos_modelo$High_Score == 1)

precision <- (VP + VN) / (VP + VN + FP + FN)
sensitivity <- VP / (VP + FN)
specificity <- VN / (VN + FP)
f1_score_2 <- 2 * (precision * sensitivity) / (precision + sensitivity)

cat("Umbral 0.3\nPrecision:", round(precision, 3),
    "\nSensibilidad:", round(sensitivity, 3),
    "\nEspecificidad:", round(specificity, 3),
    "\nF1-Score:", round(f1_score_2, 3))
## Umbral 0.3
## Precision: 0.713 
## Sensibilidad: 0.603 
## Especificidad: 0.749 
## F1-Score: 0.654
Métrica Valor Interpretación
Precisión 0.713 El 71.3% de todas las predicciones fueron correctas.
Sensibilidad 0.603 El modelo detectó correctamente el 60.3% de los estudiantes que aprobaron.
Especificidad 0.749 El modelo identificó correctamente el 74.9% de los estudiantes que reprobaron.
F1-Score 0.654 Buen equilibrio entre precisión y sensibilidad: el modelo clasifica bien y detecta muchos casos positivos.

Comparación de desempeño con diferentes umbrales

Métrica Umbral 0.5 Umbral 0.3 Interpretación
Precisión 0.774 0.713 Disminuye un poco al permitir más positivos
Sensibilidad 0.250 0.603 Sube mucho → detectas más estudiantes que aprueban
Especificidad 0.945 0.749 Baja → se aceptan más falsos positivos
F1-Score 0.378 0.654 Aumenta notablemente → mejor balance global

🧠 Interpretación:

  • Sensibilidad subió de 25% a 60.3% → ahora detectas muchos más estudiantes con nota ≥ 70.

  • Especificidad bajó (era esperable) → ahora tienes más falsos positivos.

  • Precisión general bajó un poco, pero sigue siendo razonable.

📈 Curva logística para visualización

library(ggplot2)

ggplot(datos_modelo, aes(x = Hours_Studied, y = High_Score)) +
  geom_jitter(height = 0.05, alpha = 0.2) +
  stat_smooth(method = "glm", method.args = list(family = "binomial"),
              se = FALSE, color = "blue") +
  labs(title = "Curva logística: Probabilidad de nota ≥ 70 según horas de estudio",
       y = "Probabilidad estimada", x = "Horas de estudio")
## `geom_smooth()` using formula = 'y ~ x'

✅ ¿Qué muestra este gráfico?

  • Eje X (Horas de estudio): Variable independiente continua.

  • Eje Y (Probabilidad estimada): Valor que predice el modelo, es decir, la probabilidad de que un estudiante saque una nota ≥ 70.

  • Puntos dispersos (geom_jitter):

Cada punto representa a un estudiante.

Está codificado como 0 (nota < 70) o 1 (nota ≥ 70), pero con jitter para evitar sobreposición.

  • Línea azul (stat_smooth con glm y binomial):

Representa la curva logística ajustada por el modelo.

Muestra cómo cambia la probabilidad de éxito (High_Score = 1) al aumentar las horas de estudio.

📌 ¿Qué interpretaciones puedes hacer?

  • La curva no es lineal, sino sigmoidea (forma de S), lo cual es ideal para modelar probabilidades.

  • A medida que aumentan las horas de estudio, también aumenta la probabilidad de tener una nota mayor o igual a 70.

  • Hay una zona de cambio rápido (aproximadamente entre 15 y 30 horas) donde pequeñas diferencias en horas de estudio provocan grandes cambios en probabilidad.

  • Para estudiantes que estudian menos de 10 horas, la probabilidad de éxito es muy baja (cerca de 0).

  • Para quienes estudian más de 35 horas, la probabilidad se aproxima a 1.

📉 Curva ROC y AUC

library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Adjuntando el paquete: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
roc_obj <- roc(datos_modelo$High_Score, predict(modelo_logit_simple, type = "response"))
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
plot(roc_obj, col = "darkblue", main = "Curva ROC")

auc(roc_obj)
## Area under the curve: 0.7469

✅ ¿Qué es la curva ROC?

La curva ROC (Receiver Operating Characteristic) evalúa el rendimiento de un modelo de clasificación binaria para todos los posibles umbrales de decisión.

  • Eje X: 1 − Especificidad (falsos positivos).

  • Eje Y: Sensibilidad (verdaderos positivos).

  • Cada punto en la curva representa un umbral de probabilidad distinto utilizado para clasificar.

La línea diagonal gris representa el rendimiento de un clasificador aleatorio (sin capacidad predictiva). Cuanto más lejos esté la curva del área diagonal y más se acerque a la esquina superior izquierda, mejor el modelo.

✅ ¿Qué significa el AUC?

  • AUC (Area Under the Curve) = 0.7469

Esto quiere decir:

  • El modelo tiene capacidad discriminativa moderada.

  • Si tomas una observación positiva y una negativa al azar, el modelo tiene aproximadamente un 74.7% de probabilidad de asignar una mayor probabilidad al caso positivo.

Interpretación general del AUC:

  • 0.90–1.00: Excelente

  • 0.80–0.90: Buena

  • 0.70–0.80: Aceptable/moderada

  • 0.60–0.70: Débil

  • 0.50–0.60: Muy débil (cercano al azar)

Regresión Logística Múltiple

✅ ¿Qué es la regresión logística múltiple?

Es una extensión de la regresión logística simple, donde en lugar de tener una sola variable independiente, incluyes varias variables explicativas. El modelo tiene esta forma:

\[ \log\left( \frac{p}{1-p} \right) = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \cdots + \beta_k X_k \]

Ajustamos varios modelos de regresión logística múltiple, con distintas combinaciones de variables:

modelo_1 <- glm(High_Score ~ Hours_Studied + Attendance, data = datos_modelo, family = binomial)
summary(modelo_1)
## 
## Call:
## glm(formula = High_Score ~ Hours_Studied + Attendance, family = binomial, 
##     data = datos_modelo)
## 
## Coefficients:
##                 Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   -23.053091   0.607793  -37.93   <2e-16 ***
## Hours_Studied   0.286737   0.009446   30.36   <2e-16 ***
## Attendance      0.187612   0.005376   34.90   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 7371.3  on 6606  degrees of freedom
## Residual deviance: 4007.7  on 6604  degrees of freedom
## AIC: 4013.7
## 
## Number of Fisher Scoring iterations: 6
modelo_2 <- glm(High_Score ~ Hours_Studied + Attendance + Previous_Scores, data = datos_modelo, family = binomial)
summary(modelo_2)
## 
## Call:
## glm(formula = High_Score ~ Hours_Studied + Attendance + Previous_Scores, 
##     family = binomial, data = datos_modelo)
## 
## Coefficients:
##                   Estimate Std. Error z value Pr(>|z|)    
## (Intercept)     -28.276954   0.765359  -36.95   <2e-16 ***
## Hours_Studied     0.302602   0.010019   30.20   <2e-16 ***
## Attendance        0.201948   0.005819   34.70   <2e-16 ***
## Previous_Scores   0.048019   0.003091   15.53   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 7371.3  on 6606  degrees of freedom
## Residual deviance: 3740.5  on 6603  degrees of freedom
## AIC: 3748.5
## 
## Number of Fisher Scoring iterations: 6
modelo_3 <- glm(High_Score ~ Hours_Studied + Attendance + Previous_Scores + Tutoring_Sessions, data = datos_modelo, family = binomial)
summary(modelo_3)
## 
## Call:
## glm(formula = High_Score ~ Hours_Studied + Attendance + Previous_Scores + 
##     Tutoring_Sessions, family = binomial, data = datos_modelo)
## 
## Coefficients:
##                     Estimate Std. Error z value Pr(>|z|)    
## (Intercept)       -31.543543   0.871041  -36.21   <2e-16 ***
## Hours_Studied       0.329739   0.010884   30.30   <2e-16 ***
## Attendance          0.218521   0.006396   34.16   <2e-16 ***
## Previous_Scores     0.053212   0.003245   16.40   <2e-16 ***
## Tutoring_Sessions   0.546184   0.036649   14.90   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 7371.3  on 6606  degrees of freedom
## Residual deviance: 3498.6  on 6602  degrees of freedom
## AIC: 3508.6
## 
## Number of Fisher Scoring iterations: 7
modelo_4 <- glm(High_Score ~ Hours_Studied + Sleep_Hours + Tutoring_Sessions + Previous_Scores + Attendance + Physical_Activity, data = datos_modelo, family = binomial)
summary(modelo_4)
## 
## Call:
## glm(formula = High_Score ~ Hours_Studied + Sleep_Hours + Tutoring_Sessions + 
##     Previous_Scores + Attendance + Physical_Activity, family = binomial, 
##     data = datos_modelo)
## 
## Coefficients:
##                     Estimate Std. Error z value Pr(>|z|)    
## (Intercept)       -32.440246   0.925379 -35.056  < 2e-16 ***
## Hours_Studied       0.331895   0.010966  30.266  < 2e-16 ***
## Sleep_Hours         0.009559   0.029601   0.323    0.747    
## Tutoring_Sessions   0.549444   0.036844  14.913  < 2e-16 ***
## Previous_Scores     0.053847   0.003260  16.518  < 2e-16 ***
## Attendance          0.220439   0.006452  34.167  < 2e-16 ***
## Physical_Activity   0.191484   0.042114   4.547 5.45e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 7371.3  on 6606  degrees of freedom
## Residual deviance: 3477.6  on 6600  degrees of freedom
## AIC: 3491.6
## 
## Number of Fisher Scoring iterations: 7

Interpretación de Desviancias:

Modelo Variables incluidas Desviancia
modelo_1 Hours_Studied, Attendance 4007.7
modelo_2 modelo_1 + Previous_Scores 3740.5
modelo_3 modelo_2 + Tutoring_Sessions 3498.6
modelo_4 modelo_3 + Sleep_Hours + Physical_Activity 3477.6
  • modelo_1 Utiliza solo dos predictores: Hours_Studied y Attendance.

Desviancia: 4007.7, la más alta.

Esto indica que el modelo tiene un ajuste pobre y no captura bien toda la información relevante.

  • modelo_2 Añade Previous_Scores, mejorando el ajuste.

Desviancia baja a 3740.5, lo que representa una mejora considerable.

Confirma que el rendimiento previo es un predictor importante.

  • modelo_3 Agrega Tutoring_Sessions.

Desviancia: 3498.6.

El modelo mejora aún más al considerar el acceso a tutorías, que es otra variable predictiva significativa.

  • modelo_4 Incorpora Sleep_Hours y Physical_Activity.

Desviancia final: 3477.6, la más baja de todos los modelos.

Aunque Sleep_Hours no es significativa, el modelo completo mejora el ajuste general.

Calculamos el AIC de cada modelo:

AIC(modelo_1, modelo_2, modelo_3, modelo_4)
##          df      AIC
## modelo_1  3 4013.688
## modelo_2  4 3748.475
## modelo_3  5 3508.571
## modelo_4  7 3491.574

Comparación de modelos según AIC:

Modelo Variables incluidas AIC
modelo_1 Hours_Studied, Attendance 4013.7
modelo_2 modelo_1 + Previous_Scores 3748.5
modelo_3 modelo_2 + Tutoring_Sessions 3508.6
modelo_4 modelo_3 + Sleep_Hours + Physical_Activity 3491.6
  • ¿Cuál es el mejor modelo?:

El modelo_4 tiene el AIC más bajo (3491.6), lo que indica que es el modelo con mejor balance entre ajuste y complejidad.

Aunque Sleep_Hours no fue significativa (p = 0.747), su inclusión no aumentó el AIC, y Physical_Activity sí fue significativa.

Curva logística para modelo_4

# Asegúrate de tener las librerías necesarias
library(ggplot2)
library(patchwork)
## Warning: package 'patchwork' was built under R version 4.4.3
# Obtener coeficientes del modelo
coefs <- coef(modelo_4)

# Media de cada variable (para mantenerlas fijas)
medias <- colMeans(datos_modelo[, c("Hours_Studied", "Sleep_Hours", "Tutoring_Sessions", 
                                    "Previous_Scores", "Attendance", "Physical_Activity")])

# Función para crear una curva logística univariada manteniendo las otras variables fijas
curva_logistica <- function(var, var_label) {
  # Secuencia de valores para la variable
  secuencia <- seq(min(datos_modelo[[var]]), max(datos_modelo[[var]]), length.out = 100)
  
  # Crear data frame con la variable de interés variando y las otras fijas en su media
  datos_curva <- as.data.frame(t(sapply(secuencia, function(x) {
    vec <- medias
    vec[var] <- x
    return(vec)
  })))
  
  # Agregar nombre de columnas
  colnames(datos_curva) <- names(medias)
  
  # Predecir probabilidades
  datos_curva$Probabilidad <- predict(modelo_4, newdata = datos_curva, type = "response")
  datos_curva$Variable <- secuencia
  
  # Graficar
  ggplot(datos_curva, aes(x = Variable, y = Probabilidad)) +
    geom_line(color = "blue", size = 1) +
    labs(x = "Valor de la variable",
         y = "Probabilidad estimada de aprobar",
         title = var_label) +
    theme_minimal()
}

# Crear todas las curvas
g1 <- curva_logistica("Attendance", "Attendance")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
g2 <- curva_logistica("Hours_Studied", "Hours_Studied")
g3 <- curva_logistica("Previous_Scores", "Previous_Scores")
g4 <- curva_logistica("Tutoring_Sessions", "Tutoring_Sessions")
g5 <- curva_logistica("Sleep_Hours", "Sleep_Hours")
g6 <- curva_logistica("Physical_Activity", "Physical_Activity")

# Juntar los gráficos en una sola figura con 2 filas y 3 columnas
(g1 | g2 | g3) / (g4 | g5 | g6) +
  plot_annotation(title = "Curvas logísticas por variable (modelo_4)")

Interpretación variable por variable:

1. Attendance

Tiene una forma sigmoidea muy clara.

A medida que la asistencia sube del 80% al 100%, la probabilidad de aprobar pasa de cerca de 0 a más de 0.9.

✔️ Es uno de los predictores más potentes.

2. Hours_Studied

Similar a Attendance: curva logística muy pronunciada.

El salto más importante en probabilidad ocurre entre 15 y 30 horas semanales de estudio.

3. Previous_Scores

Curva creciente pero más suave.

El efecto es importante, pero más gradual. Mejores puntajes anteriores → mayor probabilidad de éxito actual.

4. Tutoring_Sessions

Relación positiva clara: más tutorías → mayor probabilidad de aprobar.

El cambio es bastante progresivo (no tan abrupto como con Hours_Studied).

5. Sleep_Hours

Curva muy plana: indica muy poca variación en la probabilidad.

Ya lo habías identificado como no significativo en el modelo → esta gráfica lo confirma visualmente.

6. Physical_Activity

Curva creciente leve → contribuye de forma moderada.

La probabilidad de aprobar aumenta, pero en un rango muy estrecho (de 0.04 a 0.11 aprox).

Conclusión visual:

Las variables Attendance y Hours_Studied son las más influyentes.

Sleep_Hours tiene un efecto muy bajo → podrías considerar excluirla si buscas un modelo más parsimonioso.

El resto (Previous_Scores, Tutoring_Sessions, Physical_Activity) también contribuyen, con diferentes magnitudes.

Evaluación del modelo de regresión logística múltiple (modelo_4)

# Paso 1: Generar predicciones de probabilidad
probabilidades <- predict(modelo_4, type = "response")

# Paso 2: Clasificar usando umbral 0.3
predicciones_03 <- ifelse(probabilidades >= 0.3, 1, 0)

# Paso 3: Calcular matriz de confusión
VP <- sum(predicciones_03 == 1 & datos_modelo$High_Score == 1)
VN <- sum(predicciones_03 == 0 & datos_modelo$High_Score == 0)
FP <- sum(predicciones_03 == 1 & datos_modelo$High_Score == 0)
FN <- sum(predicciones_03 == 0 & datos_modelo$High_Score == 1)

Visualización de la matriz de confusión (modelo_4)

library(ggplot2)
library(dplyr)

# Convertir las predicciones y valores reales a factores con etiquetas
datos_modelo$Pred_Logit_03 <- factor(predicciones_03, levels = c(0, 1), labels = c("No Aprobado", "Aprobado"))
datos_modelo$Real_Logit <- factor(datos_modelo$High_Score, levels = c(0, 1), labels = c("No Aprobado", "Aprobado"))

# Crear la tabla de frecuencia
matriz_confusion <- table(Real = datos_modelo$Real_Logit, Predicho = datos_modelo$Pred_Logit_03)
matriz_df <- as.data.frame(matriz_confusion)

# Graficar heatmap
ggplot(matriz_df, aes(x = Real, y = Predicho, fill = Freq)) +
  geom_tile(color = "white") +
  geom_text(aes(label = Freq), size = 6) +
  scale_fill_gradient(low = "#deebf7", high = "#08519c") +
  labs(title = "Matriz de Confusion - Modelo Logistico Multiple",
       x = "Clase real",
       y = "Clase predicha",
       fill = "Frecuencia") +
  theme_minimal(base_size = 14)

Interpretación:

  • Verdaderos Positivos (VP = 1395): Estudiantes que aprobaron y fueron correctamente clasificados.

  • Falsos Negativos (FN = 230): Estudiantes que aprobaron, pero el modelo los clasificó como no aprobados.

  • Verdaderos Negativos (VN = 4355): Estudiantes que no aprobaron y fueron correctamente identificados.

  • Falsos Positivos (FP = 627): Estudiantes que no aprobaron, pero el modelo los clasificó como aprobados.

Métricas:

# Paso 4: Calcular métricas
precision <- (VP + VN) / (VP + VN + FP + FN)
sensibilidad <- VP / (VP + FN)
especificidad <- VN / (VN + FP)
f1_score <- 2 * (precision * sensibilidad) / (precision + sensibilidad)

# Mostrar resultados
cat("Evaluacion modelo_4 (Umbral 0.3):\n")
## Evaluacion modelo_4 (Umbral 0.3):
cat("Precision:", round(precision, 3), "\n")
## Precision: 0.87
cat("Sensibilidad:", round(sensibilidad, 3), "\n")
## Sensibilidad: 0.858
cat("Especificidad:", round(especificidad, 3), "\n")
## Especificidad: 0.874
cat("F1-Score:", round(f1_score, 3), "\n")
## F1-Score: 0.864
Métrica Valor Interpretación
Precisión 0.870 El 87.0% de todas las predicciones fueron correctas.
Sensibilidad 0.858 El modelo identificó correctamente el 85.8% de los estudiantes que aprobaron.
Especificidad 0.874 Identificó correctamente el 87.4% de los estudiantes que no aprobaron.
F1-Score 0.864 Excelente equilibrio entre sensibilidad y precisión.

🧠 Interpretación general:

  • El modelo clasifica de forma muy precisa tanto los aprobados como los no aprobados.

  • La sensibilidad alta indica que rara vez deja de identificar a quienes sí aprobaron.

  • El F1-Score de 0.864 confirma que el modelo tiene un rendimiento equilibrado.

  • Esto lo convierte en una herramienta útil para predecir con anticipación el rendimiento académico, ideal para intervenir antes de que ocurra el fracaso escolar.

Curva ROC y AUC del modelo_4

library(pROC)

roc_obj <- roc(datos_modelo$High_Score, probabilidades)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
plot(roc_obj, col = "darkblue", main = "Curva ROC - modelo_4")

auc(roc_obj)
## Area under the curve: 0.94

La Curva ROC (Receiver Operating Characteristic) permite visualizar el rendimiento del modelo en todos los posibles umbrales de clasificación. Representa la relación entre:

  • Sensibilidad (eje Y): Tasa de verdaderos positivos.

  • 1 - Especificidad (eje X): Tasa de falsos positivos.

En el gráfico, la línea azul representa la curva del modelo, mientras que la línea diagonal gris representa un clasificador aleatorio.

✅ Área Bajo la Curva (AUC): 0.94

  • El valor de AUC = 0.94 indica que el modelo tiene una capacidad de discriminación excelente.

  • En términos prácticos: si se selecciona un estudiante que aprobó y uno que no al azar, hay un 94% de probabilidad de que el modelo asigne una probabilidad mayor al estudiante que aprobó.

  • Cuanto más cerca de 1 esté el AUC, mejor es el modelo clasificando correctamente.

Conclusión Final

El presente análisis empleó modelos de regresión logística para predecir la probabilidad de que un estudiante obtenga una nota igual o superior a 70 puntos (variable binaria High_Score), utilizando como predictores variables académicas y de hábitos personales.

Modelo Logístico Simple

Se ajustó un primer modelo con un único predictor: las horas de estudio semanales. Este mostró una relación positiva y estadísticamente significativa con la probabilidad de éxito (OR ≈ 1.18 por hora adicional), pero con desempeño predictivo limitado:

  • Desviancia residual: 6385.4

  • AUC: 0.7469 (discriminación aceptable)

  • F1-Score (umbral 0.5): 0.378 → sensibilidad baja (25%)

  • Clasifica bien a quienes no aprobaron (alta especificidad), pero falla al identificar adecuadamente a quienes sí aprobaron (baja sensibilidad).

Modelos Logísticos Múltiples

Se desarrollaron distintos modelos incluyendo nuevas variables predictoras: Attendance, Previous Scores, Tutoring Sessions, Sleep Hours y Physical Activity.

El modelo más completo y con mejor desempeño fue el que incluyó todas las variables anteriores (modelo_4):

  • AUC: 0.94 → excelente discriminación.

  • Precisión: 0.87

  • Sensibilidad: 0.858

  • Especificidad: 0.874

  • F1-Score: 0.864

  • AIC: 3491.6 (el más bajo de todos los modelos probados)

  • Desviancia residual: 3477.6 → la más baja de todos los modelos

Este modelo mostró una gran capacidad de clasificación, tanto en detectar a los estudiantes con alto desempeño como a quienes no alcanzan la nota mínima.

Consideraciones adicionales

  • El cambio de umbral de clasificación de 0.5 a 0.3 fue clave para mejorar la sensibilidad del modelo sin sacrificar demasiado la especificidad.

  • La curva ROC y el AUC confirmaron que el modelo múltiple completo presenta un mejor balance entre verdaderos positivos y falsos positivos que el modelo simple.

  • Las curvas logísticas individuales mostraron la influencia positiva de cada predictor sobre la probabilidad de éxito académico.

Conclusión general

El análisis confirma que variables como horas de estudio, asistencia, desempeño previo y tutorías influyen significativamente en la probabilidad de aprobar un examen. La regresión logística múltiple, junto con una elección adecuada del umbral, se consolida como una herramienta eficaz para:

  • Evaluar el rendimiento académico.

  • Identificar estudiantes en riesgo.

  • Diseñar intervenciones educativas basadas en datos.