El dataset contiene información clínica de pacientes con o sin enfermedad cardíaca. Las variables categóricas se recodificaron para facilitar la interpretación.
Se recodificaron las variables categóricas del conjunto de datos, que originalmente venían como valores numéricos, para facilitar su interpretación. Por ejemplo, la variable cp que representaba el tipo de dolor en el pecho con valores 0 a 3, fue recodificada a categorías como “Angina típica”, “Dolor no anginoso”, etc. También se limpiaron los datos de la variable thal, eliminando valores codificados como 0, que no representan una categoría válida.
# 1. Sexo: 0 = mujer, 1 = hombre
datos$sex = factor(datos$sex, levels = c(0, 1), labels = c("Mujer", "Hombre"))
# 2. Tipo de dolor en el pecho (0-3)
datos$cp = factor(datos$cp,
levels = c(0, 1, 2, 3),
labels = c("Angina tipica", "Angina atipica", "Dolor no anginoso", "Asintomatico"))
# 3. Azúcar en sangre
datos$fbs = factor(datos$fbs,
levels = c(0, 1),
labels = c("≤ 120 mg/dl", "> 120 mg/dl"))
# 4. Resultado ECG (0-2)
datos$restecg = factor(datos$restecg,
levels = c(0, 1, 2),
labels = c("Normal", "Anormalidad ST-T", "Hipertrofia ventricular izquierda"))
# 5. Angina inducida por ejercicio
datos$exang = factor(datos$exang,
levels = c(0, 1),
labels = c("No","Si"))
# 6. Pendiente del segmento ST
datos$slope = factor(datos$slope,
levels = c(0, 1, 2),
labels = c("Ascendente", "Plana", "Descendente"))
# 7. N° Vasos Coronarios principales observados por fluoroscopia
datos = subset(datos, ca != 4)
datos$ca = factor(datos$ca,
levels = c(0, 1, 2, 3),
labels = c("0 vasos", "1 Vaso", "2 vasos", "3 vasos"))
# 8. Thalassemia
datos = subset(datos, thal != 0)
datos$thal = factor(datos$thal,
levels = c( 1, 2, 3),
labels = c("Flujo normal", "Defecto fijo", "Defecto reversible"))
# 9. Variable objetivo (Target)
datos$target = factor(datos$target,
levels = c(0, 1),
labels = c("Sin enfermedad", "Con enfermedad"))El conjunto de datos incluye variables clínicas como edad, sexo, tipo de dolor en el pecho, azúcar en sangre, resultados del electrocardiograma, y otras que han sido estudiadas por su posible relación con la enfermedad cardíaca. Las variables edad y sexo fueron tratadas como suplementarias para el Análisis de Correspondencias Múltiples (MCA), mientras que target (presencia o ausencia de enfermedad) se usó como variable activa, para observar qué categorías están más asociadas con la enfermedad.
Se aplicó MCA para identificar qué categorías están más asociadas a la enfermedad. Edad y sexo se trataron como variables suplementarias.
Fig 1. Análisis de Correspondencias Múltiples
En la la Figura 1 se muestra ilustrado que ciertas categorías como 0 vasos,Dolor no anginioso, Descendiente, defecto fijo y el no dolor anginoso inducido por ejercicio estan asociados a la categoría ilustrativa Mujer y estan asociados a la categoría Con enfermedad, mientras las categoría ilustrativa Hombre estan asociadas la pendiente Normal.
Tambien se puede observar cierta independencia de la categoría Edad, ya que la categoría mas asociada son las personas con glucosa en la sangre en ayunas ≤ 120 mg/dl (normal). Para poder analizar mejor estas categorías relacionadas a la enfermedad se realiza otro grafico de elipses para ver sus asociaciones.
# 1. Extraer coordenadas y limpiar nombres
coord = as.data.frame(mca$var$coord)
coord$nombre = rownames(coord)
# 2. Validar que estén las categorías
unique(coord$nombre)## [1] "Angina tipica" "Angina atipica"
## [3] "Dolor no anginoso" "Asintomatico"
## [5] "≤ 120 mg/dl" "> 120 mg/dl"
## [7] "Normal" "Anormalidad ST-T"
## [9] "Hipertrofia ventricular izquierda" "No"
## [11] "Si" "Ascendente"
## [13] "Plana" "Descendente"
## [15] "0 vasos" "1 Vaso"
## [17] "2 vasos" "3 vasos"
## [19] "Flujo normal" "Defecto fijo"
## [21] "Defecto reversible" "Sin enfermedad"
## [23] "Con enfermedad"
# 3. Crear agrupación
coord$grupo = ifelse(coord$nombre == "Con enfermedad", "Con enfermedad",
ifelse(coord$nombre == "Sin enfermedad", "Sin enfermedad", "Otras"))
# 4. Guardar posiciones de las dos categorías de target
pos_enf = coord[coord$nombre == "Con enfermedad", c("Dim 1", "Dim 2")]
pos_sano = coord[coord$nombre == "Sin enfermedad", c("Dim 1", "Dim 2")]
# 5. Calcular distancias
coord$dist_enf = sqrt((coord$`Dim 1` - pos_enf$`Dim 1`)^2 + (coord$`Dim 2` - pos_enf$`Dim 2`)^2)
coord$dist_sano = sqrt((coord$`Dim 1` - pos_sano$`Dim 1`)^2 + (coord$`Dim 2` - pos_sano$`Dim 2`)^2)
# 6. Clasificar por cercanía
coord$asociado_a = ifelse(coord$grupo != "Otras", coord$grupo,
ifelse(coord$dist_enf < coord$dist_sano, "Con enfermedad", "Sin enfermedad"))
# 7. Graficar
ggplot(coord, aes(x = `Dim 1`, y = `Dim 2`)) +
geom_point(aes(color = asociado_a), size = 3.5) +
geom_text_repel(aes(label = nombre, color = asociado_a), size = 4) +
scale_color_manual(values = c(
"Con enfermedad" = "#e74c3c",
"Sin enfermedad" = "#2ecc71"
)) +
geom_mark_ellipse(aes(fill = asociado_a, label = asociado_a),
concavity = 10, expand = unit(0.5, "lines"),
show.legend = FALSE, alpha = 0.1, color = NA) +
scale_fill_manual(values = c(
"Con enfermedad" = "#e74c3c",
"Sin enfermedad" = "#2ecc71"
)) +
labs(
title = "Asociación de Categorías con la Enfermedad Cardiaca",
subtitle = "Visualización de proximidad según análisis MCA",
color = "Asociado a",
x = "Dim 1", y = "Dim 2"
) +
theme_minimal(base_size = 10)Fig 2. Asociación de Categorías con la Enfermedad Cardiaca
En la Figura 2 se observa mas claro que categorías estan asociadas a Con Enfermedad y Sin Enfermedad, se puede apreciar que por ejemplo para 0 vasos visualizados por la fluoroscopia representa una alta probabilidad de tener una enfermedad cardíaca, mientras que si se visualiza al menos 1 vaso se puede asociar a que no tiene ninguna enfermedad cardíaca. Otra categoría a resaltar es la de la pendinte del segmento ST durante el ejercicio, una pendiente Plana y Ascendente esta relacionada a que no tiene ninguna enfermedad cardíaca, mientras que una pendiente Descendiente esta asociada a Enfermedades Cardíacas.
Se ajustó un modelo logístico (GLM) para predecir la presencia de
enfermedad cardíaca (target) a partir de las demás
variables del conjunto de datos.
Este modelo permite ver qué variables tienen mayor influencia en la probabilidad de tener la enfermedad, y cuáles no aportan tanto según su significancia estadística (valor p).
# Ajustar el modelo logístico
modelo_glm <- glm(target ~ ., data = datos, family = "binomial")
summary(modelo_glm)##
## Call:
## glm(formula = target ~ ., family = "binomial", data = datos)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.554442 1.666178 0.933 0.350852
## age -0.002108 0.022197 -0.095 0.924327
## sexHombre -1.529005 0.510491 -2.995 0.002743
## cpAngina atipica 1.007762 0.553037 1.822 0.068420
## cpDolor no anginoso 1.922751 0.511856 3.756 0.000172
## cpAsintomatico 1.992947 0.645050 3.090 0.002004
## fbs> 120 mg/dl 0.433605 0.555970 0.780 0.435445
## restecgAnormalidad ST-T 0.517397 0.380823 1.359 0.174264
## restecgHipertrofia ventricular izquierda -1.761113 1.804819 -0.976 0.329172
## exangSi -0.930469 0.429707 -2.165 0.030360
## slopePlana -0.340765 0.721686 -0.472 0.636799
## slopeDescendente 1.501363 0.742450 2.022 0.043158
## ca1 Vaso -2.159729 0.499656 -4.322 1.54e-05
## ca2 vasos -3.367709 0.750849 -4.485 7.28e-06
## ca3 vasos -2.480149 0.883373 -2.808 0.004991
## thalDefecto fijo 0.132456 0.781165 0.170 0.865355
## thalDefecto reversible -1.514235 0.760121 -1.992 0.046360
##
## (Intercept)
## age
## sexHombre **
## cpAngina atipica .
## cpDolor no anginoso ***
## cpAsintomatico **
## fbs> 120 mg/dl
## restecgAnormalidad ST-T
## restecgHipertrofia ventricular izquierda
## exangSi *
## slopePlana
## slopeDescendente *
## ca1 Vaso ***
## ca2 vasos ***
## ca3 vasos **
## thalDefecto fijo
## thalDefecto reversible *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 408.40 on 295 degrees of freedom
## Residual deviance: 190.39 on 279 degrees of freedom
## AIC: 224.39
##
## Number of Fisher Scoring iterations: 6
En los resultados se observa que algunas variables son estadísticamente significativas:
Sexo (Hombre): Tiene una relación negativa con la enfermedad (p < 0.01), lo cual sugiere que ser hombre está asociado a menor probabilidad de enfermedad en este modelo, aunque esto puede deberse a cómo se distribuyen otras variables.
Tipo de dolor en el pecho:
Angina inducida por ejercicio
(exang = Sí): También es significativa (p ≈ 0.03)
y con un coeficiente negativo, lo que sugiere que esta condición se
asocia con menor probabilidad de tener la enfermedad (lo cual puede
parecer contraintuitivo y requeriría analizar más a fondo).
Pendiente del segmento ST descendente
(slope Descendente): Es significativa (p ≈ 0.04), lo cual
indica que tener una pendiente descendente del ST también está asociada
con mayor probabilidad de enfermedad.
Número de vasos coronarios coloreados por fluoroscopia
(ca): Tener 1, 2 o 3 vasos está altamente
relacionado con menor probabilidad de enfermedad (coeficientes negativos
y p < 0.001), lo cual tiene sentido clínico, ya que indica mejor
flujo coronario.
Thalassemia - Defecto reversible: Esta categoría tiene una relación negativa con la enfermedad (p ≈ 0.046), por lo que podría estar asociada a menor probabilidad de diagnóstico positivo, aunque no es tan fuerte como otras variables.
En cambio, variables como la edad, fbs (azúcar en sangre en ayunas) y los resultados de ECG, thal defecto fijo y slope plana no resultaron significativas en este modelo (p > 0.05), lo que no significa que no influyan en la salud del paciente, sino que no aportan poder predictivo estadístico significativo en este conjunto específico.
La siguiente matriz de confusión permite evaluar el rendimiento del modelo de regresión logística para predecir la presencia o ausencia de enfermedad cardíaca:
# Ver las probabilidades predichas
probabilidades <- predict(modelo_glm, type = "response")
# Clasificación con un umbral de 0.5
predicciones <- ifelse(probabilidades > 0.5, "Enfermedad", "No enfermedad")
# Matriz de confusion
matriz_confusion <- table(Real = datos$target, Predicho = predicciones)
# Convertir a data frame para graficar
conf_df <- as.data.frame(matriz_confusion)
names(conf_df) <- c("Predicho", "Real", "Frecuencia")
# Gráfico de calor
ggplot(conf_df, aes(x = Real, y = Predicho, fill = Frecuencia)) +
geom_tile(color = "white") +
geom_text(aes(label = Frecuencia), size = 6, color = "black") +
scale_fill_gradient(low = "white", high = "steelblue") +
labs(
title = "Matriz de confusión",
x = "Clase real",
y = "Clase predicha"
) +
theme_minimal()Como se puede observar:
145 personas que sí tenían enfermedad fueron correctamente identificadas (Verdaderos Positivos).
115 personas sanas fueron correctamente clasificadas como sanas (Verdaderos Negativos).
21 personas con enfermedad fueron clasificadas como sanas (Falsos Negativos).
15 personas sanas fueron clasificadas erróneamente como enfermas (Falsos Positivos).
Este resultado muestra un buen rendimiento del modelo, aunque aún hay margen de mejora en la reducción de falsos negativos.
A partir de la matriz de confusión se calcularon las métricas de desempeño más importantes:
# Extraer valores
VP <- matriz_confusion["Con enfermedad","Enfermedad"] # Verdaderos Positivos
FN <- matriz_confusion["Sin enfermedad","Enfermedad"] # Falsos Negativos
FP <- matriz_confusion["Con enfermedad","No enfermedad"] # Falsos Positivos
VN <- matriz_confusion["Sin enfermedad","No enfermedad"] # Verdaderos NegativosA continuación, se interpreta cada una de las métricas extraídas del modelo GLM:
## [1] 0.8783784
## [1] 0.873494
## [1] 0.8846154
Se realizó una limpieza y recodificación del conjunto de datos Heart Disease, mejorando así la legibilidad de las variables categóricas para facilitar su análisis estadístico.
A través del Análisis de Correspondencias Múltiples (MCA) se identificaron asociaciones entre distintas categorías y la presencia o ausencia de enfermedad cardíaca. Se evidenció, por ejemplo, que los pacientes con 0 vasos coronarios visibles, pendiente descendente del segmento ST y ciertos tipos de dolor en el pecho están más asociados a la presencia de enfermedad.
El modelo de regresión logística (GLM) permitió identificar las variables con mayor peso predictivo. Variables como el tipo de dolor en el pecho, el número de vasos observados, y el tipo de thalassemia mostraron asociaciones estadísticamente significativas con la enfermedad.
La matriz de confusión mostró que el modelo logra una adecuada clasificación: 145 verdaderos positivos y 115 verdaderos negativos. Sin embargo, aún se presentan 21 falsos negativos y 15 falsos positivos, lo cual indica espacio para mejorar la sensibilidad del modelo.
Las métricas de evaluación del modelo fueron destacables:
En conjunto, estos resultados indican que el modelo presenta un rendimiento aceptable para identificar pacientes con enfermedad cardíaca, aunque podría complementarse con otros enfoques o ajustes (como cambio de umbral o inclusión de variables clínicas adicionales) para optimizar la detección de casos positivos.
Finalmente, tanto el análisis exploratorio (MCA) como el modelo predictivo (GLM) aportan perspectivas valiosas y complementarias para el estudio de enfermedades cardíacas, mostrando cómo el análisis estadístico puede apoyar decisiones clínicas y la interpretación de datos médicos.