#########EJEMPLO 1##################

Descripción Un conjunto de datos simulados que contiene información sobre diez mil clientes. El objetivo aquí es predecir qué clientes incumplirán con la deuda de su tarjeta de crédito.

default: No y Sí que indican si el cliente incumplió con su deuda.

student: No y Sí que indica si el cliente es estudiante

balance: El saldo promedio que le queda al cliente en su tarjeta de crédito después de realizar su pago mensual

income: Ingreso del cliente

data("Default")
names(Default)
## [1] "default" "student" "balance" "income"
table(Default$default)
## 
##   No  Yes 
## 9667  333
table(Default$student)
## 
##   No  Yes 
## 7056 2944

Tenemos que existen en total 333 personas que incumplieron su pago y 9667 no. En función del total, 7056 no son estudiantes su deuda y 2944 si lo son.

####Descripción de ingresos#################
ggplot(data = Default, aes(x= income)) +
  geom_density(adjust = 1.4, alpha=.4,fill="blue")+
  labs(x="Ingresos",y="Densidad")

Existe una distribución bimodal, probablemente por los dos grupos que existen.

ggplot(data = Default, aes(x= income, group=default, fill=default)) +
  geom_density(adjust = 1.4, alpha=.4)+
  labs(x="Ingresos",y="Densidad",fill="Cumple")

Ahora la relación entre el ingreso con respecto a los que cumplen o no cumplen con el credito.

ggplot(data = Default, aes(x= income, group=student, fill=student)) +
  geom_density(adjust = 1.4, alpha=.4)+
  labs(x="Ingresos",y="Densidad",fill="Estudia")

Ahora el ingreso medio por las personas que estudian y no estudian.

##########Categorizar la variable default###################
Datos_bancarios = Default %>%
  mutate(def_cat = if_else(default == "No", 0,  1)) %>%
  as.data.table()

Ahora voy a crear una variable que se llame Datos_bancarios y lo que se referencia como “No” lo clasique con el numero “0” y viceversa para “1”.

#####Verifica correcto cambio a 0 o 1. 
table(Datos_bancarios$default,
      Datos_bancarios$def_cat)
##      
##          0    1
##   No  9667    0
##   Yes    0  333
########Ajuste del modelo de regresión logística#########
mod_logistico = glm(def_cat~balance+as.factor(student), family =  binomial(link=logit), data = Datos_bancarios)
summary(mod_logistico)
## 
## Call:
## glm(formula = def_cat ~ balance + as.factor(student), family = binomial(link = logit), 
##     data = Datos_bancarios)
## 
## Coefficients:
##                         Estimate Std. Error z value Pr(>|z|)    
## (Intercept)           -1.075e+01  3.692e-01 -29.116  < 2e-16 ***
## balance                5.738e-03  2.318e-04  24.750  < 2e-16 ***
## as.factor(student)Yes -7.149e-01  1.475e-01  -4.846 1.26e-06 ***
## ---
## 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: 1571.7  on 9997  degrees of freedom
## AIC: 1577.7
## 
## Number of Fisher Scoring iterations: 8

Todos los predictores (balance y student) son altamente significativos (Pr(>|z|) < 0.001) -> ***. Por cada unidad extra de balance, los log-odds de default aumentan. Ser estudiante reduce los log-odds de caer en default.

Explicación: En regresión logística, todo lo que ves en los coeficientes (Intercept, balance, studentYes, etc.) está en log-odds.

Los log-odds (logaritmo de las probabilidades relativas) son la escala en la que trabaja la regresión logística. En lugar de predecir directamente una probabilidad, el modelo predice los log-odds de que ocurra un evento (por ejemplo, que alguien caiga en default).

El intercepto = -10.75 Significa que cuando una persona no es estudiante y tiene un balance de $0, sus log-odds de caer en default son -10.75. Es decir, que exp(-10.75) ≈ 2.1e-5 (muy cercanos a cero), si alguien sin saldo en su tarjeta y que no es estudiante tiene una probabilidad muy baja de caer en default.

El balance = 0.00574 significa que por cada dólar adicional en el balance, los log-odds de caer en default aumentan en 0.00574 - 0,574% es decir que mientras más alto el saldo de la tarjeta, más probable es que caiga en default.

Ejemplo de interpretación del coeficiente de balance

Ejemplo:

Cálculo de la diferencia en log-odds

La diferencia en log-odds se calcula multiplicando el coeficiente del balance por la diferencia de saldo:

\[ 1000 \times 0.00574 = 5.74 \]

Conversión a odds

Ahora transformamos los log-odds a odds usando la exponencial:

\[ e^{5.74} \approx 311 \]

La persona con 1,000 de saldo tiene odds de caer en default unas 311 veces mayores que la persona con balance $0.

ACLARACIÓN: Si alguien tiene un balance alto, eso no significa que tenga más plata, sino que está usando más su crédito y deja más saldo sin pagar. Es decir, tiene más deuda activa en su tarjeta, mes tras mes.

El studentYes = -0.715 significa que ser estudiante reduce los log-odds de caer en default en 0.715 unidades, en comparación con no ser estudiante. Es decir exp(-0.715) ≈ 0.489 los estudiantes tienen un 51% menos de odds de caer en default que los no estudiantes.

#Odds-Ratio
exp(coef(mod_logistico)[-1])
##               balance as.factor(student)Yes 
##              1.005755              0.489252

Por cada $1 adicional en balance, las odds de caer en default aumentan 0.5755% (1.005755 - 1). Ser estudiante reduce las odds de caer en default en un 51% (1-0.48) comparado con no serlo.

round(exp(confint(mod_logistico)),3)
## Waiting for profiling to be done...
##                       2.5 % 97.5 %
## (Intercept)           0.000  0.000
## balance               1.005  1.006
## as.factor(student)Yes 0.365  0.651

Interpretación de los Odds Ratios con Intervalos de Confianza

Variable OR (95% IC) ¿Qué significa?
balance 1.005 – 1.006 Por cada $1 más de saldo, las odds de default aumentan entre 0.5% y 0.6%.
studentYes 0.365 – 0.651 Ser estudiante reduce las odds de default entre un 34.9% y 63.5%, comparado con no serlo.
(Intercept) 0.000 – 0.000 No se interpreta como odds ratio (porque es el punto base con balance = 0 y no estudiante).
Datos_bancarios$predicted_prob = predict(mod_logistico, type = "response")

Bajo lo anterior, se calcula la probabilidad de que cada persona caiga en default y guarda esas probabilidades en una nueva columna llamada predicted_prob dentro del dataframe Datos_bancarios.

## Curva ROC
roc_obj = roc(Datos_bancarios$def_cat, Datos_bancarios$predicted_prob)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
plot(roc_obj, main = paste0("Curva ROC (AUC = ", round(auc(roc_obj), 4), ")"),
     col = "blue", lwd = 2)

La curva entre mas se acerce a la linea de referencia su clasificación va ser menor precisa, viceversa a cuanto mas se acerque a los extremos como la anterior grafica.

# Encontrar el punto de corte óptimo usando el criterio de Youden
optimal_coords = coords(roc_obj, "best", best.method = "youden")
optimal_coords
##    threshold specificity sensitivity
## 1 0.03168237   0.8627289   0.9039039

Bajo lo anterior, se detecta correctamente al 90.39% de quienes sí caen en default (sensibilidad) y se clasificas correctamente al 86.27% de quienes no caen en default (especificidad).

Recordemos:

Especificidad:Representa la capacidad del modelo para identificar correctamente los casos negativos. Sensibilidad: Representa la capacidad del modelo para identificar correctamente los casos positivos.

Datos_bancarios$nueva= ifelse(Datos_bancarios$predicted_prob>=optimal_coords$threshold,"Si","No")
table(Datos_bancarios$nueva)
## 
##   No   Si 
## 8372 1628

El modelo predice que 1.628 personas van a caer en default (“Si”). Y predice que 8.372 personas no van a caer en default (“No”).

# Comparar con la variable real
table(Predicción = Datos_bancarios$nueva, Real = Datos_bancarios$def_cat)
##           Real
## Predicción    0    1
##         No 8340   32
##         Si 1327  301

TN (Verdaderos Negativos) = 8340 - El modelo predijo “No” y en realidad era “No”.

TP (Verdaderos Positivos) = 301 - El modelo predijo “Sí” y en realidad era “Sí”.

FP (Falsos Positivos) = 1327 - El modelo predijo “Sí”, pero era “No”.

FN (Falsos Negativos) = 32 - El modelo predijo “No”, pero era “Sí”.

Es decir alrededor de 1359 datos mal clasificados.

TN = 8340
TP = 301
FP = 1327
FN = 32

accuracy = (TP + TN) / (TP + TN + FP + FN)
sensibilidad = TP / (TP + FN) 
especificidad = TN / (TN + FP)
precision = TP / (TP + FP)
f1 = 2 * (precision * sensibilidad) / (precision + sensibilidad)

round(c(Accuracy = accuracy,
        Sensibilidad = sensibilidad,
        Especificidad = especificidad,
        Precision = precision,
        F1_Score = f1), 4)
##      Accuracy  Sensibilidad Especificidad     Precision      F1_Score 
##        0.8641        0.9039        0.8627        0.1849        0.3070

Muy buena sensibilidad (90.39%): detecta bien los que sí caen en default.

Buena especificidad (86.27%): no se equivoca mucho con los que no.

Precisión baja (18%): muchos falsos positivos.

F1 Score bajo, lo que refleja el desbalance entre positivos y negativos.