##1.- Regresión Logística Simple

La Regresión Logística Simple, desarrollada por David Cox en 1958, es un método de regresión que permite estimar la probabilidad de una variable cualitativa binaria en función de una variable cuantitativa. Una de las principales aplicaciones de la regresión logística es la de clasificación binaria, en el que las observaciones se clasifican en un grupo u otro dependiendo del valor que tome la variable empleada como predictor. Por ejemplo: clasificar a un individuo desconocido como hombre o mujer en función del tamaño de la mandíbula.

Es importante tener en cuenta que, aunque la regresión logística permite clasificar, se trata de un modelo de regresión que modela el logaritmo de la probabilidad de pertenecer a cada grupo. La asignación final se hace en función de las probabilidades predichas.

La existencia de una relación significativa entre una variable cualitativa con dos niveles y una variable continua se puede estudiar mediante otros test estadísticos tales como t-test o ANOVA (un ANOVA de dos grupos es equivalente al t-test). Sin embargo, la regresión logística permite además calcular la probabilidad de que la variable dependiente pertenezca a cada una de las dos categorías en función del valor que adquiera la variable independiente. Supóngase que se quiere estudiar la relación entre los niveles de colesterol y los ataques de corazón.

Para ello, se mide el colesterol de un grupo de personas y durante los siguientes 20 años se monitoriza que individuos han sufrido un ataque. Un t-test entre los niveles de colesterol de las personas que han sufrido ataque vs las que no lo han sufrido permitiría contrastar la hipótesis de que el colesterol y los ataques al corazón están asociados. Si además se desea conocer la probabilidad de que una persona con un determinado nivel de colesterol sufra un infarto en los próximos 20 años, o poder conocer cuánto tiene que reducir el colesterol un paciente para no superar un 50% de probabilidad de padecer un infarto en los próximos 20 años, se tiene que recurrir a la regresión logística.

Si una variable cualitativa con dos niveles se codifica como 1 y 0, matemáticamente es posible ajustar un modelo de regresión lineal por mínimos cuadrados β0+β1x.

El problema de esta aproximación es que, al tratarse de una recta, para valores extremos del predictor, se obtienen valores de Y menores que 0 o mayores que 1, lo que entra en contradicción con el hecho de que las probabilidades siempre están dentro del rango [0,1].

En el siguiente ejemplo se modela la probabilidad de fraude por impago (default) en función del balance de la cuenta bancaria (balance).

#2.- DATOS

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.2.3
## Warning: package 'ggplot2' was built under R version 4.2.3
## Warning: package 'tibble' was built under R version 4.2.3
## Warning: package 'tidyr' was built under R version 4.2.3
## Warning: package 'readr' was built under R version 4.2.3
## Warning: package 'purrr' was built under R version 4.2.3
## Warning: package 'dplyr' was built under R version 4.2.3
## Warning: package 'forcats' was built under R version 4.2.3
## Warning: package 'lubridate' was built under R version 4.2.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.2     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.3     ✔ tibble    3.2.1
## ✔ lubridate 1.9.2     ✔ tidyr     1.3.0
## ✔ purrr     1.0.1     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(ISLR)
## Warning: package 'ISLR' was built under R version 4.2.3
datos <- Default

# Se recodifician los niveles No,, yes a 1 y 0
datos <- datos %>%
         select(default, balance) %>%
         mutate(default = recode(default,
                                 "No" = 0,
                                 "Yes" = 1))
head(datos)
##   default   balance
## 1       0  729.5265
## 2       0  817.1804
## 3       0 1073.5492
## 4       0  529.2506
## 5       0  785.6559
## 6       0  919.5885
tail(datos)
##       default   balance
## 9995        0  172.4130
## 9996        0  711.5550
## 9997        0  757.9629
## 9998        0  845.4120
## 9999        0 1569.0091
## 10000       0  200.9222
summary(datos)
##     default          balance      
##  Min.   :0.0000   Min.   :   0.0  
##  1st Qu.:0.0000   1st Qu.: 481.7  
##  Median :0.0000   Median : 823.6  
##  Mean   :0.0333   Mean   : 835.4  
##  3rd Qu.:0.0000   3rd Qu.:1166.3  
##  Max.   :1.0000   Max.   :2654.3
str(datos)
## 'data.frame':    10000 obs. of  2 variables:
##  $ default: num  0 0 0 0 0 0 0 0 0 0 ...
##  $ balance: num  730 817 1074 529 786 ...

#3.- Modelo

##3.1.- Modelo Lineal

# Ajuste de un modelo lineal por mínimos cuadrados.
modelo_lineal <- lm(default ~ balance, data = datos)
summary(modelo_lineal)
## 
## Call:
## lm(formula = default ~ balance, data = datos)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.23533 -0.06939 -0.02628  0.02004  0.99046 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -7.519e-02  3.354e-03  -22.42   <2e-16 ***
## balance      1.299e-04  3.475e-06   37.37   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1681 on 9998 degrees of freedom
## Multiple R-squared:  0.1226, Adjusted R-squared:  0.1225 
## F-statistic:  1397 on 1 and 9998 DF,  p-value: < 2.2e-16

##3.2.- Representación gráfica del modelo lineal

pl <- predict(modelo_lineal)
summary(pl)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -0.07519 -0.01263  0.03178  0.03330  0.07628  0.26953
# Representación gráfica del modelo.
library(ggplot2)
ggplot(data = datos, aes(x = balance, y = default)) +
  geom_point(aes(color = as.factor(default)), shape = 1) + 
  geom_smooth(method = "lm", color = "purple", se = TRUE) +
  theme_bw()  +
  labs(title = "Regresión lineal por mínimos cuadrados",
       y = "Probabilidad default") +
  theme(legend.position = "bottom")
## `geom_smooth()` using formula = 'y ~ x'

Como se puede observar, un modelo lineal de probabilidad puede deparar probabilidades negativas o superiores a la unidad. Por este motivo, se aplica un modelo logístico (o no lineal).

predict(object = modelo_lineal, newdata=data.frame(balance = 500))
##           1 
## -0.01025587

Para evitar estos problemas, la regresión logística transforma el valor devuelto por la regresión lineal (β0+β1X)m empleando una función cuyo resultado está siempre comprendido entre 0 y 1. Existen varias funciones que cumplen esta descripción, una de las más utilizadas es la función logística (también conocida como función sigmoide)

#3.3.- Modelo logit

# Ajuste de un modelo logístico.
modelo_logistico <- glm(default ~balance, data = datos, family = "binomial")
summary(modelo_logistico)
## 
## Call:
## glm(formula = default ~ balance, family = "binomial", data = datos)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.2697  -0.1465  -0.0589  -0.0221   3.7589  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -1.065e+01  3.612e-01  -29.49   <2e-16 ***
## balance      5.499e-03  2.204e-04   24.95   <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: 2920.6  on 9999  degrees of freedom
## Residual deviance: 1596.5  on 9998  degrees of freedom
## AIC: 1600.5
## 
## Number of Fisher Scoring iterations: 8
library(stargazer)
## 
## Please cite as:
##  Hlavac, Marek (2022). stargazer: Well-Formatted Regression and Summary Statistics Tables.
##  R package version 5.2.3. https://CRAN.R-project.org/package=stargazer
stargazer(modelo_lineal, modelo_logistico, type = "text")
## 
## ==========================================================
##                              Dependent variable:          
##                     --------------------------------------
##                                    default                
##                                 OLS              logistic 
##                                 (1)                (2)    
## ----------------------------------------------------------
## balance                      0.0001***           0.005*** 
##                              (0.00000)           (0.0002) 
##                                                           
## Constant                     -0.075***          -10.651***
##                               (0.003)            (0.361)  
##                                                           
## ----------------------------------------------------------
## Observations                  10,000              10,000  
## R2                             0.123                      
## Adjusted R2                    0.122                      
## Log Likelihood                                   -798.226 
## Akaike Inf. Crit.                               1,600.452 
## Residual Std. Error      0.168 (df = 9998)                
## F Statistic         1,396.816*** (df = 1; 9998)           
## ==========================================================
## Note:                          *p<0.1; **p<0.05; ***p<0.01

##3.4.- Representación del Logit

# Representación gráfica del modelo.
ggplot(data = datos, aes(x = balance, y = default)) +
  geom_point(aes(color = as.factor(default)), shape = 1) + 
  stat_function(fun = function(x){predict(modelo_logistico,
                                          newdata = data.frame(balance = x),
                                          type = "response")}) +
  theme_bw() +
  labs(title = "Regresión logística",
       y = "Probabilidad default") +
  theme(legend.position = "bottom")

# Con geom_smooth se puede obtener el gráfico directamente.
ggplot(data = datos, aes(x = balance, y = default)) +
  geom_point(aes(color = as.factor(default)), shape = 1) + 
  geom_smooth(method = "glm",
              method.args = list(family = "binomial"),
              color = "black",
              se = TRUE) +
  theme_bw() +
  theme(legend.position = "none")
## `geom_smooth()` using formula = 'y ~ x'

#4.- EJERCICIO

Un estudio quiere establecer un modelo que permita calcular la probabilidad de obtener una matrícula de honor al final del bachillerato en función de la nota que se ha obtenido en matemáticas. La variable matrícula está codificada como 0 si no se tiene matrícula y 1 si se tiene.

matricula <- as.factor(c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1,
                         0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1,
                         0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
                         0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
                         1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
                         1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1,
                         1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1,
                         0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
                         0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0,
                         0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0,
                         0, 0, 0, 0, 1, 0, 0, 0, 1, 1))
matematicas <- c(41, 53, 54, 47, 57, 51, 42, 45, 54, 52, 51, 51, 71, 57, 50, 43,
                 51, 60, 62, 57, 35, 75, 45, 57, 45, 46, 66, 57, 49, 49, 57, 64,
                 63, 57, 50, 58, 75, 68, 44, 40, 41, 62, 57, 43, 48, 63, 39, 70,
                 63, 59, 61, 38, 61, 49, 73, 44, 42, 39, 55, 52, 45, 61, 39, 41,
                 50, 40, 60, 47, 59, 49, 46, 58, 71, 58, 46, 43, 54, 56, 46, 54,
                 57, 54, 71, 48, 40, 64, 51, 39, 40, 61, 66, 49, 65, 52, 46, 61,
                 72, 71, 40, 69, 64, 56, 49, 54, 53, 66, 67, 40, 46, 69, 40, 41,
                 57, 58, 57, 37, 55, 62, 64, 40, 50, 46, 53, 52, 45, 56, 45, 54,
                 56, 41, 54, 72, 56, 47, 49, 60, 54, 55, 33, 49, 43, 50, 52, 48,
                 58, 43, 41, 43, 46, 44, 43, 61, 40, 49, 56, 61, 50, 51, 42, 67,
                 53, 50, 51, 72, 48, 40, 53, 39, 63, 51, 45, 39, 42, 62, 44, 65,
                 63, 54, 45, 60, 49, 48, 57, 55, 66, 64, 55, 42, 56, 53, 41, 42,
                 53, 42, 60, 52, 38, 57, 58, 65)
datos <- data.frame(matricula, matematicas)
head(datos, 4)
##   matricula matematicas
## 1         0          41
## 2         0          53
## 3         0          54
## 4         0          47

##4.1.- Representación de los datos

library(ggplot2)
table(datos$matricula)
## 
##   0   1 
## 151  49
ggplot(data = datos, aes(x = matricula, y = matematicas, color = matricula)) +
  geom_boxplot(outlier.shape = NA) +
  geom_jitter(width = 0.1) +
  theme_bw() +
  theme(legend.position = "bottom")

##4.2.- Modelo

modelo <- glm(matricula ~ matematicas, data = datos, family = "binomial")
summary(modelo)
## 
## Call:
## glm(formula = matricula ~ matematicas, family = "binomial", data = datos)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.0332  -0.6785  -0.3506  -0.1565   2.6143  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -9.79394    1.48174  -6.610 3.85e-11 ***
## matematicas  0.15634    0.02561   6.105 1.03e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 222.71  on 199  degrees of freedom
## Residual deviance: 167.07  on 198  degrees of freedom
## AIC: 171.07
## 
## Number of Fisher Scoring iterations: 5
library(stargazer)
stargazer(modelo, type ="text")
## 
## =============================================
##                       Dependent variable:    
##                   ---------------------------
##                            matricula         
## ---------------------------------------------
## matematicas                0.156***          
##                             (0.026)          
##                                              
## Constant                   -9.794***         
##                             (1.482)          
##                                              
## ---------------------------------------------
## Observations                  200            
## Log Likelihood              -83.537          
## Akaike Inf. Crit.           171.073          
## =============================================
## Note:             *p<0.1; **p<0.05; ***p<0.01
modelo_nulo <- glm(matricula ~ 1, data = datos, family= "binomial")
summary(modelo_nulo)
## 
## Call:
## glm(formula = matricula ~ 1, family = "binomial", data = datos)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -0.7497  -0.7497  -0.7497  -0.7497   1.6772  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -1.1255     0.1644  -6.845 7.62e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 222.71  on 199  degrees of freedom
## Residual deviance: 222.71  on 199  degrees of freedom
## AIC: 224.71
## 
## Number of Fisher Scoring iterations: 4
stargazer(modelo_nulo, modelo, type = "text")
## 
## ==============================================
##                       Dependent variable:     
##                   ----------------------------
##                            matricula          
##                        (1)            (2)     
## ----------------------------------------------
## matematicas                        0.156***   
##                                     (0.026)   
##                                               
## Constant            -1.125***      -9.794***  
##                      (0.164)        (1.482)   
##                                               
## ----------------------------------------------
## Observations           200            200     
## Log Likelihood       -111.355       -83.537   
## Akaike Inf. Crit.    224.710        171.073   
## ==============================================
## Note:              *p<0.1; **p<0.05; ***p<0.01
L1 <- -83.537
L0 <- -111.355

R2MF <- 1-L1/L0
R2MF
## [1] 0.2498137

Al incluir la variable de matemáticas, el logaritmo de la verosimilitud mejora en un 25%.

Dado que un modelo logístico modela el logaritmo de ODDs, éstas son las unidades en las que se devuelven las predicciones. Es necesario convertirlas de nuevo en probabilidad mediante la función logit. En R, la función predict() puede devolver directamente las probabilidades en lugar de los logODDs si se indica el argumento type=“response”. Sin embargo, si se quieren calcular intervalos de confianza y que éstos no se salgan del rango [0, 1] es necesario emplear los logODDs y una vez que se les ha sustraído o sumado el margen de error (Z x SE) se transforman en probabilidades.

# MEDIANTE BASE GRAPHICS SIN INTERVALOS DE CONFIANZA

# Codificación 0,1 de la variable respuesta
datos$matricula <- as.character(datos$matricula)
datos$matricula <- as.numeric(datos$matricula)

plot(matricula ~ matematicas, datos, col = "darkblue",
     main = "Modelo regresión logística",
     ylab = "P(matrícula=1|matemáticas)",
     xlab = "matemáticas", pch = "I")

# type = "response" devuelve las predicciones en forma de probabilidad en lugar de en log_ODDs
curve(predict(modelo, data.frame(matematicas = x), type = "response"),
      col = "firebrick", lwd = 2.5, add = TRUE)

# MEDIANTE GGPLOT2 INCLUYENDO INTERVALOS DE CONFIANZA

datos$matricula <- as.character(datos$matricula)
datos$matricula <- as.numeric(datos$matricula)

# Se crea un vector con nuevos valores interpolados en el rango de observaciones.
nuevos_puntos <- seq(from = min(datos$matematicas), to = max(datos$matematicas),
                     by = 0.5)


# Predicciones de los nuevos puntos según el modelo. 
# Si se indica se.fit = TRUE se devuelve el error estándar de cada predicción
# junto con el valor de la predicción (fit).
predicciones <- predict(modelo, data.frame(matematicas = nuevos_puntos),
                        se.fit = TRUE)

# Mediante la función logit se transforman los log_ODDs a probabilidades.
predicciones_logit <- exp(predicciones$fit) / (1 + exp(predicciones$fit))

# Se calcula el límite inferior y superior del IC del 95% sustrayendo e
# incrementando el logODDs de cada predicción 1.95*SE. Una vez calculados los
# logODDs del intervalo se transforman en probabilidades con la función logit.
limite_inferior       <- predicciones$fit - 1.96 * predicciones$se.fit
limite_inferior_logit <- exp(limite_inferior) / (1 + exp(limite_inferior))
limite_superior       <- predicciones$fit + 1.96 * predicciones$se.fit
limite_superior_logit <- exp(limite_superior) / (1 + exp(limite_superior))

# Se crea un dataframe con los nuevos puntos y sus predicciones
datos_curva <- data.frame(matematicas = nuevos_puntos,
                          probabilidad_matricula = predicciones_logit,
                          limite_inferior_logit = limite_inferior_logit, 
                          limite_superior_logit = limite_superior_logit)

ggplot(datos, aes(x = matematicas, y = matricula)) +
      geom_point(aes(color = as.factor(matricula)), shape = "I", size = 3) + 
      geom_line(data = datos_curva, aes(y = probabilidad_matricula),
                color = "firebrick") + 
      geom_line(data = datos_curva, aes(y = limite_inferior_logit),
                linetype = "dashed") + 
      geom_line(data = datos_curva, aes(y = limite_superior_logit),
                linetype = "dashed") + 
      theme_bw() +
      labs(title = "Modelo regresión logística matrícula ~ nota matemáticas",
           y = "P(matrícula = 1 | matemáticas)", y = "matemáticas") + 
      theme(legend.position = "null") +
      theme(plot.title = element_text(size = 10))

4.3.- Evaluación del Modelo

A la hora de evaluar la validez y calidad de un modelo de regresión logística, se analiza tanto el modelo en su conjunto como los predictores que lo forman.

Se considera que el modelo es útil si es capaz de mostrar una mejora explicando las observaciones respecto al modelo nulo (sin predictores). El test Likelihood ratio calcula la significancia de la diferencia de residuos entre el modelo de interés y el modelo nulo. El estadístico sigue una distribución chi-cuadrado con grados de libertad equivalentes a la diferencia de grados de libertad de los dos modelos.

# Diferencia de residuos
# En R, un objeto glm almacena la "deviance" del modelo, así como la "deviance"
# del modelo nulo. 
dif_residuos <- modelo$null.deviance - modelo$deviance
# Grados libertad
df <- modelo$df.null - modelo$df.residual
# p-value
p_value <- pchisq(q = dif_residuos,df = df, lower.tail = FALSE)

paste("Diferencia de residuos:", round(dif_residuos, 4))
## [1] "Diferencia de residuos: 55.6368"
p_value
## [1] 8.717591e-14

La variable matemáticas es significativa para estudiar la probabilidad de obtener una matrícula.

# El mismo cálculo se puede obtener directamente con:
anova(modelo, test = "Chisq")
## Analysis of Deviance Table
## 
## Model: binomial, link: logit
## 
## Response: matricula
## 
## Terms added sequentially (first to last)
## 
## 
##             Df Deviance Resid. Df Resid. Dev  Pr(>Chi)    
## NULL                          199     222.71              
## matematicas  1   55.637       198     167.07 8.718e-14 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

En este caso, el modelo sí es significativo.

Para determinar si los predictores introducidos en un modelo de regresión logística contribuyen de forma significativa, se emplea el estadístico Z y el test Wald chi-test. Éste es el método utilizado para calcular los p-values que se muestran al hacer summary() del modelo. El predictor matemáticas sí contribuye de forma significativa (p-value = 1.03e-09).

#4.4.- Matriz de Confusión

library(vcd)
## Warning: package 'vcd' was built under R version 4.2.3
## Loading required package: grid
## 
## Attaching package: 'vcd'
## The following object is masked from 'package:ISLR':
## 
##     Hitters
predicciones <- ifelse(test = modelo$fitted.values > 0.5, yes = 1, no = 0)
matriz_confusion <- table(modelo$model$matricula, predicciones,
                          dnn = c("observaciones", "predicciones"))
matriz_confusion
##              predicciones
## observaciones   0   1
##             0 140  11
##             1  27  22
mosaic(matriz_confusion, shade = T, colorize = T,
       gp = gpar(fill = matrix(c("green3", "red2", "red2", "green3"), 2, 2)))

library(pROC)
## Warning: package 'pROC' was built under R version 4.2.3
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
roc(datos$matricula,predicciones,plot = TRUE, legacy.axes = TRUE,
    percent = TRUE, xlab = "% Falsos positivos",
    ylab = "% verdaderos postivios", col = "red", lwd = 2,
    print.auc = TRUE)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases

## 
## Call:
## roc.default(response = datos$matricula, predictor = predicciones,     percent = TRUE, plot = TRUE, legacy.axes = TRUE, xlab = "% Falsos positivos",     ylab = "% verdaderos postivios", col = "red", lwd = 2, print.auc = TRUE)
## 
## Data: predicciones in 151 controls (datos$matricula 0) < 49 cases (datos$matricula 1).
## Area under the curve: 68.81%
tabla2<-table(true=datos$matricula,pred=round(fitted(modelo)))
sum(diag(tabla2))/sum(tabla2)
## [1] 0.81
library(caret)
## Warning: package 'caret' was built under R version 4.2.3
## Loading required package: lattice
## 
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
## 
##     lift
matriz2<-confusionMatrix(tabla2);matriz2
## Confusion Matrix and Statistics
## 
##     pred
## true   0   1
##    0 140  11
##    1  27  22
##                                           
##                Accuracy : 0.81            
##                  95% CI : (0.7487, 0.8619)
##     No Information Rate : 0.835           
##     P-Value [Acc > NIR] : 0.85234         
##                                           
##                   Kappa : 0.4228          
##                                           
##  Mcnemar's Test P-Value : 0.01496         
##                                           
##             Sensitivity : 0.8383          
##             Specificity : 0.6667          
##          Pos Pred Value : 0.9272          
##          Neg Pred Value : 0.4490          
##              Prevalence : 0.8350          
##          Detection Rate : 0.7000          
##    Detection Prevalence : 0.7550          
##       Balanced Accuracy : 0.7525          
##                                           
##        'Positive' Class : 0               
## 

##Test de bondad del ajuste Hosmer Lemeshow.

library(ResourceSelection)
## Warning: package 'ResourceSelection' was built under R version 4.2.3
## ResourceSelection 0.3-6   2023-06-27
h2<-hoslem.test(datos$matricula,fitted(modelo),g=10)
h2
## 
##  Hosmer and Lemeshow goodness of fit (GOF) test
## 
## data:  datos$matricula, fitted(modelo)
## X-squared = 2.9254, df = 8, p-value = 0.939