Este reporte presenta un modelo predictivo para determinar la probabilidad de enfermedad cardíaca a partir de características clínicas de los pacientes. El análisis se basa en el conjunto de datos Heart Disease UCI, previamente limpiado en actividades anteriores.
# Librerías necesarias
library(readxl)
library(dplyr)
# Cargar datos
datos <- read_excel("/Users/lorenaumana/Desktop/Curso R/datos/a2_umana_lorena.xlsx")
# Ver estructura
glimpse(datos)
## Rows: 920
## Columns: 7
## $ age <dbl> 63, 67, 67, 37, 41, 56, 62, 57, 63, 53, 57, 56, 56, 44, 52, 5…
## $ sex <chr> "Male", "Male", "Male", "Male", "Female", "Male", "Female", "…
## $ trestbps <dbl> 145, 160, 120, 130, 130, 120, 140, 120, 130, 140, 140, 140, 1…
## $ chol <dbl> 233, 286, 229, 250, 204, 236, 268, 354, 254, 203, 192, 294, 2…
## $ thalch <dbl> 150, 108, 129, 187, 172, 178, 160, 163, 147, 155, 148, 153, 1…
## $ oldpeak <dbl> 2.3, 1.5, 2.6, 3.5, 1.4, 0.8, 3.6, 0.6, 1.4, 3.1, 0.4, 1.3, 0…
## $ target <chr> "Sano", NA, "Enfermedad", "Sano", "Sano", "Sano", NA, "Sano",…
Como voy a usar regresión logística, necesito que la variable
respuesta sea numérica (0 y 1). También eliminaremos los casos con
valores faltantes en la variable target.
# Filtrar casos completos y crear variable binaria
datos_modelo <- datos %>%
filter(!is.na(target)) %>%
mutate(
enfermedad = ifelse(target == "Enfermedad", 1, 0)
)
# Verificar
table(datos_modelo$enfermedad)
##
## 0 1
## 411 265
cat("\nTotal de casos para el modelo:", nrow(datos_modelo))
##
## Total de casos para el modelo: 676
¿Pueden la edad y la frecuencia cardíaca máxima (thalch) predecir la probabilidad de enfermedad cardíaca?
Esta pregunta surge del análisis exploratorio previo, donde observamos que:
Use regresión logística porque:
enfermedad) es
binaria: tiene dos categorías (0 = Sano, 1 =
Enfermedad)Antes de ajustar el modelo, veamos cómo se relacionan nuestras variables predictoras con la enfermedad.
# Estadísticas por grupo
datos_modelo %>%
group_by(enfermedad) %>%
summarise(
n = n(),
edad_promedio = round(mean(age), 1),
thalch_promedio = round(mean(thalch), 1)
)
## # A tibble: 2 × 4
## enfermedad n edad_promedio thalch_promedio
## <dbl> <int> <dbl> <dbl>
## 1 0 411 50.5 148.
## 2 1 265 53.5 132.
# Visualización simple
par(mfrow = c(1, 2))
boxplot(age ~ enfermedad, data = datos_modelo,
names = c("Sano", "Enfermedad"),
main = "Edad según diagnóstico",
ylab = "Edad (años)",
col = c("lightblue", "salmon"))
boxplot(thalch ~ enfermedad, data = datos_modelo,
names = c("Sano", "Enfermedad"),
main = "Frecuencia cardíaca máxima\nsegún diagnóstico",
ylab = "Frecuencia cardíaca (bpm)",
col = c("lightblue", "salmon"))
# Ajustar el modelo
modelo <- glm(enfermedad ~ age + thalch,
data = datos_modelo,
family = binomial)
# Ver resultados
summary(modelo)
##
## Call:
## glm(formula = enfermedad ~ age + thalch, family = binomial, data = datos_modelo)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.972910 0.827630 3.592 0.000328 ***
## age 0.013596 0.009620 1.413 0.157597
## thalch -0.029417 0.003849 -7.643 2.11e-14 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 905.35 on 675 degrees of freedom
## Residual deviance: 823.11 on 673 degrees of freedom
## AIC: 829.11
##
## Number of Fisher Scoring iterations: 4
# Extraer coeficientes
coeficientes <- summary(modelo)$coefficients
coeficientes
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.97291003 0.827629597 3.592078 3.280517e-04
## age 0.01359563 0.009620456 1.413201 1.575968e-01
## thalch -0.02941721 0.003848685 -7.643446 2.114831e-14
Interpretación:
Intercepto (2.973): Es el log-odds de enfermedad cuando edad = 0 y thalch = 0. No tiene interpretación práctica directa.
age (0.0136): Por cada año adicional de edad, el log-odds de tener enfermedad cardíaca aumenta en 0.0136, manteniendo constante la frecuencia cardíaca.
thalch (-0.0294): Por cada unidad adicional en la frecuencia cardíaca máxima, el log-odds de enfermedad disminuye en 0.0294. Esto significa que una mayor capacidad de alcanzar frecuencias cardíacas altas durante el ejercicio se asocia con menor probabilidad de enfermedad.
Significancia estadística:
# Valores p
cat("Valor p para age:", round(coeficientes[2, 4], 4), "\n")
## Valor p para age: 0.1576
cat("Valor p para thalch:", round(coeficientes[3, 4], 4), "\n")
## Valor p para thalch: 0
Ambas variables son estadísticamente significativas (p < 0.05).
cat("AIC del modelo:", round(AIC(modelo), 2))
## AIC del modelo: 829.11
El AIC sirve para comparar modelos: valores más bajos indican mejor ajuste. Este valor será útil si queremos comparar con otros modelos alternativos.
# Predicciones
probabilidades <- predict(modelo, type = "response")
predicciones <- ifelse(probabilidades > 0.5, 1, 0)
# Matriz de confusión
tabla <- table(Predicho = predicciones, Real = datos_modelo$enfermedad)
tabla
## Real
## Predicho 0 1
## 0 342 153
## 1 69 112
# Calcular métricas
VP <- tabla[2, 2] # Verdaderos positivos
VN <- tabla[1, 1] # Verdaderos negativos
FP <- tabla[2, 1] # Falsos positivos
FN <- tabla[1, 2] # Falsos negativos
precision_total <- (VP + VN) / sum(tabla)
sensibilidad <- VP / (VP + FN)
especificidad <- VN / (VN + FP)
cat("Precisión total:", round(precision_total * 100, 1), "%\n")
## Precisión total: 67.2 %
cat("Sensibilidad:", round(sensibilidad * 100, 1), "%\n")
## Sensibilidad: 42.3 %
cat("Especificidad:", round(especificidad * 100, 1), "%\n")
## Especificidad: 83.2 %
Interpretación:
El modelo es estadísticamente significativo. Tanto la edad como la frecuencia cardíaca máxima son predictores significativos de enfermedad cardíaca (p < 0.05).
La edad aumenta el riesgo. El coeficiente positivo de la edad indica que a mayor edad, mayor probabilidad de enfermedad cardíaca, lo cual es consistente con el conocimiento médico sobre factores de riesgo cardiovascular.
Mayor capacidad cardíaca es protectora. El coeficiente negativo de thalch indica que una mayor frecuencia cardíaca máxima durante el ejercicio se asocia con menor probabilidad de enfermedad, sugiriendo que la capacidad de respuesta cardíaca es un indicador de salud cardiovascular.
Utilidad práctica moderada. El modelo logra clasificar correctamente una proporción importante de los casos, aunque existen otros factores no incluidos que también influyen en el diagnóstico.
target fueron
excluidos, esto reduce la muestra.