#########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.
balanceEjemplo:
La diferencia en log-odds se calcula multiplicando el coeficiente del balance por la diferencia de saldo:
\[ 1000 \times 0.00574 = 5.74 \]
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
| 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.