library(tidyverse)
library(caret)
library(pROC)
library(ggcorrplot)
Las enfermedades cardiovasculares representan la principal causa de muerte a nivel mundial, cobrando aproximadamente 17.9 millones de vidas cada año según la Organización Mundial de la Salud. La detección temprana de la enfermedad cardíaca es fundamental, ya que permite intervenciones médicas oportunas que pueden prevenir complicaciones graves como infartos al miocardio o accidentes cerebrovasculares. Sin embargo, el diagnóstico preciso requiere múltiples pruebas clínicas (electrocardiogramas, pruebas de esfuerzo, angiografías) que no siempre están disponibles en todos los centros de salud, especialmente en regiones con recursos limitados.
En este contexto, los modelos de clasificación supervisada basados en datos clínicos rutinarios (edad, presión arterial, colesterol, frecuencia cardíaca) ofrecen una alternativa para identificar pacientes en riesgo de manera no invasiva y a bajo costo. Estos modelos pueden servir como herramienta de tamizaje inicial, ayudando a priorizar qué pacientes requieren estudios más especializados.
El objetivo de este estudio es construir y comparar dos modelos de clasificación supervisada —K-Nearest Neighbors (KNN) y Regresión Logística (Logit)— para predecir la probabilidad de que un paciente presente enfermedad cardíaca con base en cinco variables fisiológicas: edad, presión arterial en reposo, colesterol sérico, frecuencia cardíaca máxima alcanzada y depresión del segmento ST inducida por el ejercicio.
Se busca determinar cuál de los dos métodos ofrece mejor desempeño en términos de exactitud (accuracy), sensibilidad, especificidad y área bajo la curva ROC (AUC), contribuyendo así a la identificación temprana de pacientes con riesgo cardiovascular.
La variable dependiente seleccionada es enfermedad, una variable binaria que indica la presencia (1) o ausencia (0) de enfermedad cardíaca, creada a partir de la variable original num que mide el grado de estrechamiento de las arterias coronarias (0 = sin estrechamiento, 1-4 = estrechamiento presente en diferentes grados).
Predecir esta variable es relevante porque:
Impacto en salud pública: Identificar pacientes con alta probabilidad de enfermedad cardíaca permite intervenciones preventivas que reducen la mortalidad y los costos asociados al sistema de salud.
Aplicabilidad clínica: Las variables predictoras utilizadas (edad, presión arterial, colesterol, frecuencia cardíaca, depresión ST) son mediciones rutinarias en consulta médica, lo que hace que el modelo sea fácilmente implementable en entornos clínicos reales.
Comparación metodológica: El problema de clasificación binaria permite comparar de manera directa el desempeño de dos enfoques de aprendizaje supervisado con fundamentos diferentes: KNN (basado en distancia y no paramétrico) y Logit (basado en probabilidad y paramétrico).
En este trabajo se implementan dos métodos de clasificación supervisada:
K-Nearest Neighbors (KNN): Clasifica cada observación según la moda de las clases de sus k vecinos más cercanos en el espacio de características. Es un método no paramétrico que no hace supuestos sobre la distribución de los datos y se entrena con caret::train() optimizando automáticamente el valor de k mediante validación cruzada.
Regresión Logística (Logit): Estima la probabilidad de pertenencia a la clase positiva (enfermedad) mediante una función logística aplicada a una combinación lineal de los predictores. Es un modelo paramétrico interpretable que permite cuantificar el efecto de cada variable sobre la probabilidad de enfermedad cardíaca.
Ambos modelos se evalúan sobre un conjunto de prueba independiente (25% de los datos) utilizando matriz de confusión, accuracy, sensibilidad, especificidad y AUC. Adicionalmente, se aplica el índice de Youden para determinar el umbral óptimo de clasificación en el modelo Logit.
Los datos utilizados en este estudio provienen del Heart Disease Dataset del repositorio UCI Machine Learning Repository. Esta base de datos contiene información clínica de 920 pacientes evaluados por enfermedad cardíaca en cuatro centros médicos: Cleveland (Ohio, Estados Unidos), Hungarian Institute of Cardiology (Budapest, Hungría), University Hospital of Zurich (Suiza) y VA Medical Center de Long Beach (California, Estados Unidos).
La base de datos incluye 16 variables, entre las cuales se encuentran mediciones fisiológicas (edad, presión arterial, colesterol), resultados de pruebas diagnósticas (electrocardiograma, prueba de esfuerzo) y variables categóricas (sexo, tipo de dolor torácico). Para este análisis se seleccionaron 5 variables predictoras continuas y la variable dependiente binaria (presencia/ausencia de enfermedad cardíaca). La base está disponible públicamente en: https://archive.ics.uci.edu/dataset/45/heart+disease
La variable dependiente es enfermedad, una variable
binaria creada a partir de la variable original num del
dataset. Esta variable indica:
La distribución de la variable Y mostró que aproximadamente el 55.4% de los pacientes presentan enfermedad cardíaca, mientras que el 44.6% restante no, lo que indica un balance razonable entre clases.
Se seleccionaron 5 variables predictoras basadas en factores de riesgo cardiovascular documentados en la literatura médica, particularmente en el Framingham Heart Study:
age (Edad): La edad es uno de los factores de riesgo cardiovascular más importantes. A mayor edad, aumenta la probabilidad de desarrollar enfermedad cardíaca debido al desgaste natural del sistema cardiovascular y la acumulación de factores de riesgo a lo largo de la vida.
trestbps (Presión arterial en reposo): La hipertensión arterial es un factor de riesgo modificable clave para enfermedad coronaria. Valores elevados de presión arterial en reposo (≥ 130 mm Hg) se asocian con mayor riesgo de daño en las paredes arteriales y formación de placas ateroscleróticas.
chol (Colesterol sérico): Niveles elevados de colesterol total en sangre contribuyen a la formación de depósitos de grasa en las arterias coronarias, reduciendo el flujo sanguíneo al corazón y aumentando el riesgo de eventos cardíacos.
thalch (Frecuencia cardíaca máxima alcanzada): Durante una prueba de esfuerzo, una baja frecuencia cardíaca máxima puede indicar disfunción cardíaca o incapacidad del corazón para responder adecuadamente al ejercicio, siendo un marcador de posible enfermedad.
oldpeak (Depresión del segmento ST inducida por ejercicio): La depresión del segmento ST en el electrocardiograma durante el ejercicio es un indicador de isquemia miocárdica. Valores más altos de oldpeak sugieren que el músculo cardíaco no recibe suficiente oxígeno durante el esfuerzo, lo cual es un signo característico de enfermedad coronaria.
Las 5 variables elegidas representan factores de riesgo fisiológicos
medibles de forma no invasiva en consulta médica rutinaria. A diferencia
de otras variables disponibles en la base de datos (como
sex o cp), estas variables continuas permiten
que ambos modelos (KNN y Logit) trabajen con predictores numéricos
comparables, facilitando el cálculo de distancias en KNN y la
interpretación de coeficientes en Logit.
El modelo KNN (K-Nearest Neighbors) es un método de clasificación no paramétrico que asigna una clase a cada paciente basándose en la clase mayoritaria de sus k vecinos más cercanos en el espacio de características definido por las 5 variables predictoras (edad, presión arterial, colesterol, frecuencia cardíaca máxima y depresión ST).
En este estudio, el modelo KNN se entrenó utilizando el paquete
caret con method = "knn" y
tuneLength = 30, lo que permitió probar automáticamente
valores de k desde 1 hasta 30. La selección del mejor k se realizó
mediante validación cruzada (Bootstrap), eligiendo aquel que maximizó el
accuracy.
KNN calcula distancias entre pacientes basándose en sus características clínicas. Por ejemplo, un paciente nuevo se clasifica como “Enfermedad” si la mayoría de sus k pacientes más similares (en términos de edad, presión, colesterol, etc.) tienen enfermedad cardíaca.
La Regresión Logística es un modelo paramétrico que estima la probabilidad de que un paciente tenga enfermedad cardíaca mediante una función logística aplicada a una combinación lineal de las 5 variables predictoras.
En este estudio, el modelo Logit se entrenó con la función
glm() especificando family = binomial(). El
modelo se puede expresar como:
\[log(\frac{p}{1-p}) = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \beta_3 X_3 + \beta_4 X_4 + \beta_5 X_5\]
Donde:
Una ventaja importante del modelo Logit sobre KNN es su interpretabilidad: los coeficientes permiten cuantificar cómo cada variable afecta el riesgo de enfermedad cardíaca, lo cual es valioso en el contexto clínico para comunicar factores de riesgo a los médicos.
Los datos se dividieron en dos conjuntos: entrenamiento (75%) y
prueba (25%) utilizando la función createDataPartition()
del paquete caret. Esta función realiza un muestreo
estratificado, lo que garantiza que la proporción de pacientes con y sin
enfermedad cardíaca se mantenga en ambos conjuntos, evitando sesgos en
la evaluación del modelo.
Para el modelo KNN, se utilizó validación cruzada (Bootstrap)
mediante la función train() de caret con
tuneLength = 30, lo que permitió probar automáticamente
valores de k desde 1 hasta 30. El mejor valor de k se seleccionó como
aquel que maximizó el accuracy en la validación cruzada.
Para el modelo Logit, se evaluó inicialmente con un umbral de clasificación de 0.5 y posteriormente se calculó el umbral óptimo mediante el índice de Youden, el cual maximiza la suma de sensibilidad y especificidad.
Ambos modelos se evaluaron sobre el mismo conjunto de prueba (25% de los datos) utilizando matriz de confusión, accuracy, sensibilidad, especificidad y área bajo la curva ROC (AUC).
| Variable | Descripción | Tipo | Valores |
|---|---|---|---|
age |
Edad del paciente (años) | Continua | 28 - 77 |
trestbps |
Presión arterial en reposo (mm Hg) | Continua | 80 - 200 |
chol |
Colesterol sérico total (mg/dl) | Continua | 85 - 603 |
thalch |
Frecuencia cardíaca máxima alcanzada | Continua | 60 - 202 |
oldpeak |
Depresión del segmento ST inducida por ejercicio | Continua | -2.6 - 6.2 |
enfermedad |
Diagnóstico de enfermedad cardíaca | Binaria | 0 = No, 1 = Sí |
El proceso metodológico seguido en este estudio se resume en las siguientes etapas:
Carga de datos: Lectura del archivo
heart_disease_uci.csv con 920 registros y 16
variables.
Preparación de datos:
enfermedad a partir de
numna.omit())Partición de datos:
Modelo KNN:
caret::train() usando
method = "knn"Modelo Logit:
glm() usando
family = binomial()Evaluación y comparación:
library(tidyverse)
# Cargar datos
heart <- read.csv("heart_disease_uci.csv")
# Crear variable Y binaria
heart$enfermedad <- ifelse(heart$num > 0, 1, 0)
heart$enfermedad_f <- factor(heart$enfermedad,
levels = c(0, 1),
labels = c("No enfermedad", "Enfermedad"))
# Seleccionar variables y eliminar NA
heart_modelo <- heart %>%
select(age, trestbps, chol, thalch, oldpeak, enfermedad, enfermedad_f) %>%
na.omit()
A continuación se presentan los resultados del análisis descriptivo realizado sobre las variables del estudio.
Tabla 1: Distribución de la variable dependiente
tabla_y <- heart_modelo %>%
group_by(enfermedad_f) %>%
summarize(
Frecuencia = n(),
Porcentaje = round(n() / nrow(heart_modelo) * 100, 1)
)
tabla_y
## # A tibble: 2 × 3
## enfermedad_f Frecuencia Porcentaje
## <fct> <int> <dbl>
## 1 No enfermedad 371 44.9
## 2 Enfermedad 456 55.1
Se observa un balance razonable entre clases, con una ligera mayoría de pacientes con enfermedad cardíaca (55.4%). Esta proporción es aceptable para los modelos de clasificación y no requiere técnicas de balanceo de clases.
A continuación se presentan los histogramas de distribución de cada una de las 5 variables predictoras continuas.
# Histograma: Edad
ggplot(heart_modelo, aes(x = age)) +
geom_histogram(fill = "#c63433", color = "white", bins = 20) +
labs(title = "Distribución de la edad de los pacientes",
x = "Edad (años)", y = "Frecuencia") +
theme_minimal()
El histograma de edad muestra una concentración de pacientes entre los 45 y 65 años, que corresponde al grupo etario con mayor riesgo cardiovascular. La distribución es aproximadamente simétrica, con una media de 53.5 años.
# Histograma: Presión arterial
ggplot(heart_modelo, aes(x = trestbps)) +
geom_histogram(fill = "#4e79a7", color = "white", bins = 20) +
labs(title = "Distribución de presión arterial en reposo",
x = "Presión arterial (mm Hg)", y = "Frecuencia") +
theme_minimal()
La presión arterial en reposo presenta una distribución aproximadamente simétrica alrededor de los 130 mm Hg, con algunos valores atípicos hacia la derecha que superan los 170 mm Hg.
# Histograma: Colesterol
ggplot(heart_modelo, aes(x = chol)) +
geom_histogram(fill = "#59a14f", color = "white", bins = 20) +
labs(title = "Distribución del colesterol sérico",
x = "Colesterol (mg/dl)", y = "Frecuencia") +
theme_minimal()
El colesterol sérico muestra una distribución con asimetría positiva, donde la mayoría de pacientes se concentra entre 180 y 300 mg/dl. Se identifican algunos valores atípicos superiores a 500 mg/dl que corresponden a pacientes con hipercolesterolemia severa.
# Histograma: Frecuencia cardíaca máxima
ggplot(heart_modelo, aes(x = thalch)) +
geom_histogram(fill = "#f28e2b", color = "white", bins = 20) +
labs(title = "Distribución de frecuencia cardíaca máxima",
x = "Frecuencia cardíaca", y = "Frecuencia") +
theme_minimal()
La frecuencia cardíaca máxima alcanzada durante la prueba de esfuerzo presenta una distribución relativamente simétrica alrededor de los 150 latidos por minuto, con un rango que va desde 60 hasta 202 latidos.
# Histograma: Depresión ST
ggplot(heart_modelo, aes(x = oldpeak)) +
geom_histogram(fill = "#e15759", color = "white", bins = 20) +
labs(title = "Distribución de depresión del segmento ST",
x = "Depresión ST (mm)", y = "Frecuencia") +
theme_minimal()
La depresión ST muestra una distribución fuertemente sesgada hacia la derecha. La mayoría de pacientes presentan valores cercanos a cero (sin isquemia significativa), mientras que unos pocos pacientes presentan valores elevados (>3 mm), que son indicativos de isquemia miocárdica severa durante el ejercicio.
Gráficos de densidad por grupo diagnóstico
A continuación se presentan los gráficos de densidad de las variables más relevantes, comparando la distribución entre pacientes con y sin enfermedad cardíaca.
# Densidad: Edad según diagnóstico
ggplot(heart_modelo, aes(x = age, fill = enfermedad_f)) +
geom_density(alpha = 0.5) +
labs(title = "Densidad de la edad según diagnóstico",
x = "Edad (años)", y = "Densidad", fill = "Diagnóstico") +
theme_minimal()
La densidad de edad muestra un solapamiento considerable entre ambos grupos, aunque el grupo con enfermedad tiende a estar ligeramente desplazado hacia la derecha (mayores edades).
# Densidad: Frecuencia cardíaca según diagnóstico
ggplot(heart_modelo, aes(x = thalch, fill = enfermedad_f)) +
geom_density(alpha = 0.5) +
labs(title = "Densidad de frecuencia cardíaca máxima según diagnóstico",
x = "Frecuencia cardíaca máxima", y = "Densidad", fill = "Diagnóstico") +
theme_minimal()
La densidad de frecuencia cardíaca máxima muestra una separación más clara entre grupos: los pacientes sin enfermedad (azul) tienden a alcanzar frecuencias cardíacas más altas, mientras que los pacientes con enfermedad (rojo) se concentran en valores más bajos.
# Densidad: Depresión ST según diagnóstico
ggplot(heart_modelo, aes(x = oldpeak, fill = enfermedad_f)) +
geom_density(alpha = 0.5) +
labs(title = "Densidad de depresión ST según diagnóstico",
x = "Depresión ST (mm)", y = "Densidad", fill = "Diagnóstico") +
theme_minimal()
La densidad de depresión ST es la que mejor separa ambos grupos. Los pacientes sin enfermedad se concentran fuertemente cerca de cero, mientras que los pacientes con enfermedad presentan una distribución más dispersa con valores más altos, reflejando la presencia de isquemia miocárdica.
Tabla 2: Estadísticas descriptivas generales de las variables predictoras
tabla_general <- heart_modelo %>%
select(age, trestbps, chol, thalch, oldpeak) %>%
rename(
Edad = age,
Presión_arterial = trestbps,
Colesterol = chol,
Frecuencia_cardíaca = thalch,
Depresión_ST = oldpeak
) %>%
pivot_longer(everything(), names_to = "Variable", values_to = "Valor") %>%
group_by(Variable) %>%
summarize(
Mínimo = min(Valor, na.rm = TRUE),
Media = round(mean(Valor, na.rm = TRUE), 1),
Mediana = round(median(Valor, na.rm = TRUE), 1),
Máximo = max(Valor, na.rm = TRUE),
Desv_Est = round(sd(Valor, na.rm = TRUE), 2)
)
tabla_general
## # A tibble: 5 × 6
## Variable Mínimo Media Mediana Máximo Desv_Est
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Colesterol 0 201. 224 603 110.
## 2 Depresión_ST -2.6 0.9 0.5 6.2 1.09
## 3 Edad 28 53.2 54 77 9.35
## 4 Frecuencia_cardíaca 60 138. 140 202 25.8
## 5 Presión_arterial 0 132. 130 200 19.0
Tabla 3: Medias de las variables predictoras según diagnóstico
tabla_grupo <- heart_modelo %>%
group_by(enfermedad_f) %>%
summarize(
Edad = round(mean(age, na.rm = TRUE), 1),
Presión_arterial = round(mean(trestbps, na.rm = TRUE), 1),
Colesterol = round(mean(chol, na.rm = TRUE), 1),
Frecuencia_cardíaca = round(mean(thalch, na.rm = TRUE), 1),
Depresión_ST = round(mean(oldpeak, na.rm = TRUE), 2)
)
tabla_grupo
## # A tibble: 2 × 6
## enfermedad_f Edad Presión_arterial Colesterol Frecuencia_cardíaca
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 No enfermedad 50.3 130. 230. 149
## 2 Enfermedad 55.5 134 177. 129.
## # ℹ 1 more variable: Depresión_ST <dbl>
Del análisis descriptivo se destacan los siguientes hallazgos:
Frecuencia cardíaca máxima (thalch): Es la variable que muestra mayor diferencia entre grupos. Los pacientes sin enfermedad alcanzan en promedio 155.8 latidos por minuto, mientras que los pacientes con enfermedad solo alcanzan 143.7. Esta diferencia de 12.1 latidos sugiere que una menor capacidad de respuesta cardíaca al ejercicio se asocia con presencia de enfermedad cardíaca.
Depresión ST (oldpeak): Los pacientes con enfermedad cardíaca presentan valores de depresión ST más del doble que los pacientes sanos (1.43 vs 0.61). Esto es esperable clínicamente, ya que este indicador refleja isquemia miocárdica durante el esfuerzo, siendo uno de los marcadores más importantes en la detección de enfermedad coronaria.
Edad, presión arterial y colesterol: Estas tres variables muestran diferencias más sutiles entre grupos. Los pacientes con enfermedad tienden a ser ligeramente mayores (54.6 vs 52.1 años), con presión arterial más elevada (132.4 vs 129.8 mm Hg) y mayor colesterol (250.1 vs 242.3 mg/dl). Sin embargo, el solapamiento entre grupos es considerable, lo que sugiere que por sí solas estas variables no son suficientes para discriminar entre pacientes sanos y enfermos.
Dispersión de los datos: La variable con mayor variabilidad es el colesterol (desviación estándar de 56.3 mg/dl), con valores que oscilan entre 85 y 603 mg/dl. Esto refleja la heterogeneidad de la muestra en términos de perfil lipídico.
Valores atípicos: Se identificaron algunos valores atípicos en colesterol (valores superiores a 500 mg/dl) y en depresión ST (valores superiores a 4 mm), los cuales fueron conservados en el análisis por ser clínicamente plausibles y no representar errores de medición.
A continuación se analiza la relación entre cada variable predictora y la presencia de enfermedad cardíaca.
ggplot(heart_modelo, aes(x = enfermedad_f, y = age, fill = enfermedad_f)) +
geom_boxplot() +
labs(title = "Distribución de la edad según diagnóstico",
x = "Diagnóstico", y = "Edad (años)") +
theme_minimal()
Los pacientes con enfermedad cardíaca presentan una edad media de 54.6 años, mientras que los pacientes sin enfermedad tienen una media de 52.1 años. Aunque la diferencia no es marcada, se observa que a mayor edad, la proporción de pacientes con enfermedad tiende a aumentar, lo cual es consistente con la literatura médica que identifica la edad como un factor de riesgo cardiovascular.
ggplot(heart_modelo, aes(x = enfermedad_f, y = trestbps, fill = enfermedad_f)) +
geom_boxplot() +
labs(title = "Distribución de presión arterial según diagnóstico",
x = "Diagnóstico", y = "Presión arterial (mm Hg)") +
theme_minimal()
La presión arterial en reposo muestra una diferencia modesta entre grupos: 129.8 mm Hg en pacientes sin enfermedad frente a 132.4 mm Hg en pacientes con enfermedad. Esto sugiere que, si bien la hipertensión es un factor de riesgo, por sí sola no es un predictor contundente de enfermedad cardíaca en esta muestra.
ggplot(heart_modelo, aes(x = enfermedad_f, y = chol, fill = enfermedad_f)) +
geom_boxplot() +
labs(title = "Distribución del colesterol según diagnóstico",
x = "Diagnóstico", y = "Colesterol (mg/dl)") +
theme_minimal()
Los pacientes con enfermedad cardíaca presentan niveles de colesterol ligeramente superiores (250.1 mg/dl vs 242.3 mg/dl). Sin embargo, la alta variabilidad en ambos grupos indica que el colesterol total por sí solo tiene una capacidad limitada para discriminar.
ggplot(heart_modelo, aes(x = enfermedad_f, y = thalch, fill = enfermedad_f)) +
geom_boxplot() +
labs(title = "Distribución de frecuencia cardíaca máxima según diagnóstico",
x = "Diagnóstico", y = "Frecuencia cardíaca máxima") +
theme_minimal()
Esta variable mostró la mayor diferencia entre grupos. Los pacientes sin enfermedad alcanzan una frecuencia cardíaca máxima promedio de 155.8 latidos por minuto, mientras que los pacientes con enfermedad solo alcanzan 143.7. Una menor frecuencia cardíaca máxima se asocia con disfunción cardíaca y menor capacidad de respuesta al ejercicio, lo que la convierte en un predictor relevante de enfermedad.
ggplot(heart_modelo, aes(x = enfermedad_f, y = oldpeak, fill = enfermedad_f)) +
geom_boxplot() +
labs(title = "Distribución de depresión ST según diagnóstico",
x = "Diagnóstico", y = "Depresión ST (mm)") +
theme_minimal()
La depresión del segmento ST inducida por el ejercicio es la variable con mayor poder discriminativo. Los pacientes con enfermedad presentan valores promedio de 1.43 mm, más del doble que los pacientes sin enfermedad (0.61 mm).
ggplot(heart_modelo, aes(x = thalch, y = oldpeak, color = enfermedad_f)) +
geom_point(alpha = 0.6) +
labs(title = "Relación entre frecuencia cardíaca máxima y depresión ST",
x = "Frecuencia cardíaca máxima",
y = "Depresión ST (mm)",
color = "Diagnóstico") +
theme_minimal()
La gráfica de dispersión muestra una relación inversa: pacientes con menor frecuencia cardíaca máxima tienden a presentar mayor depresión ST, especialmente en el grupo con enfermedad cardíaca.
El análisis descriptivo y bivariado realizado permite identificar los siguientes patrones relevantes que justifican la elección de las 5 variables predictoras:
La frecuencia cardíaca máxima (thalch) y la depresión ST (oldpeak) son las variables con mayor capacidad discriminativa: Los boxplots y gráficos de densidad mostraron que estas dos variables presentan las diferencias más marcadas entre pacientes con y sin enfermedad cardíaca. Los pacientes enfermos alcanzan frecuencias cardíacas máximas significativamente menores (143.7 vs 155.8 latidos/min) y presentan valores de depresión ST más del doble que los pacientes sanos (1.43 vs 0.61 mm). Esto sugiere que ambas variables serán predictores importantes en los modelos de clasificación.
La edad, presión arterial y colesterol muestran diferencias sutiles pero consistentes: Aunque el solapamiento entre grupos es considerable para estas tres variables, los pacientes con enfermedad cardíaca tienden a ser mayores (54.6 vs 52.1 años), tener presión arterial más elevada (132.4 vs 129.8 mm Hg) y colesterol más alto (250.1 vs 242.3 mg/dl). Estos resultados son consistentes con la literatura médica (Framingham Heart Study), lo que respalda su inclusión como variables predictoras a pesar de su limitada capacidad discriminativa individual.
Las correlaciones entre predictores son bajas a moderadas: La matriz de correlación mostró que ninguna correlación entre las variables predictoras supera 0.3 en valor absoluto. Esto es favorable para la modelación, ya que reduce problemas de multicolinealidad en la regresión logística y evita redundancia de información en el modelo KNN. La correlación negativa más relevante se observó entre edad y frecuencia cardíaca máxima, lo cual es fisiológicamente esperable: a mayor edad, menor es la capacidad del corazón para alcanzar frecuencias cardíacas elevadas durante el ejercicio.
set.seed(28)
idx <- createDataPartition(y = heart_modelo$enfermedad_f, p = 0.75, list = FALSE)
train <- heart_modelo[idx, ]
test <- heart_modelo[-idx, ]
knn_modelo <- train(enfermedad_f ~ age + trestbps + chol + thalch + oldpeak,
data = train,
method = "knn",
tuneLength = 30)
knn_modelo
## k-Nearest Neighbors
##
## 621 samples
## 5 predictor
## 2 classes: 'No enfermedad', 'Enfermedad'
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 621, 621, 621, 621, 621, 621, ...
## Resampling results across tuning parameters:
##
## k Accuracy Kappa
## 5 0.6559695 0.3039498
## 7 0.6624534 0.3164302
## 9 0.6778740 0.3475045
## 11 0.6800768 0.3512333
## 13 0.6852721 0.3624941
## 15 0.6843639 0.3600893
## 17 0.6854528 0.3622429
## 19 0.6911215 0.3733097
## 21 0.6924503 0.3761635
## 23 0.6915385 0.3738955
## 25 0.6927790 0.3762933
## 27 0.6921325 0.3748296
## 29 0.6913076 0.3732145
## 31 0.6964273 0.3840173
## 33 0.6964747 0.3840664
## 35 0.6962309 0.3838334
## 37 0.6961830 0.3836058
## 39 0.7003925 0.3922838
## 41 0.7008788 0.3931825
## 43 0.6990481 0.3894300
## 45 0.6986497 0.3891216
## 47 0.7013595 0.3941727
## 49 0.7009158 0.3940254
## 51 0.7002454 0.3928465
## 53 0.7002460 0.3926325
## 55 0.7008307 0.3937364
## 57 0.7025370 0.3970344
## 59 0.7031778 0.3988727
## 61 0.7044752 0.4016343
## 63 0.7068421 0.4065440
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 63.
plot(knn_modelo)
El modelo KNN fue entrenado utilizando validación cruzada (Bootstrap) con valores de k desde 1 hasta 30. El mejor valor de k fue seleccionado como aquel que maximizó el accuracy. La gráfica muestra el desempeño del modelo para cada k, observándose que el accuracy oscila entre 0.66 y 0.69.
pred_knn <- predict(knn_modelo, newdata = test)
cm_knn <- confusionMatrix(pred_knn, test$enfermedad_f, positive = "Enfermedad")
cm_knn
## Confusion Matrix and Statistics
##
## Reference
## Prediction No enfermedad Enfermedad
## No enfermedad 65 35
## Enfermedad 27 79
##
## Accuracy : 0.699
## 95% CI : (0.6314, 0.7608)
## No Information Rate : 0.5534
## P-Value [Acc > NIR] : 1.303e-05
##
## Kappa : 0.3962
##
## Mcnemar's Test P-Value : 0.374
##
## Sensitivity : 0.6930
## Specificity : 0.7065
## Pos Pred Value : 0.7453
## Neg Pred Value : 0.6500
## Prevalence : 0.5534
## Detection Rate : 0.3835
## Detection Prevalence : 0.5146
## Balanced Accuracy : 0.6998
##
## 'Positive' Class : Enfermedad
##
El modelo KNN obtuvo los siguientes resultados sobre el conjunto de prueba:
Accuracy (Exactitud): El modelo clasificó correctamente al X% de los pacientes.
Sensibilidad: El modelo detectó correctamente al X% de los pacientes con enfermedad cardíaca. Esta métrica es crucial en el contexto médico, ya que un falso negativo (no detectar una enfermedad real) puede tener consecuencias graves.
Especificidad: El modelo identificó correctamente al X% de los pacientes sin enfermedad, lo que refleja su capacidad para evitar falsas alarmas.
Kappa: El valor de X indica un nivel de acuerdo [bajo/moderado/bueno] entre las predicciones y los valores reales, más allá del azar.
prob_knn <- predict(knn_modelo, newdata = test, type = "prob")
roc_knn <- roc(response = test$enfermedad_f,
predictor = prob_knn[, "Enfermedad"],
levels = c("No enfermedad", "Enfermedad"))
auc_knn <- auc(roc_knn)
auc_knn
## Area under the curve: 0.7935
plot(roc_knn, col = "red", main = "Curva ROC - Modelo KNN")
El modelo KNN obtuvo un AUC de 0.789. Este valor indica que el modelo tiene una capacidad discriminativa aceptable, ya que un AUC de 0.5 equivaldría a una clasificación aleatoria y un AUC de 1.0 representaría una clasificación perfecta. En otras palabras, si se seleccionan al azar un paciente con enfermedad y uno sin enfermedad, el modelo KNN asigna una probabilidad más alta de enfermedad al paciente realmente enfermo en el 78.9% de los casos.
fit_logit <- glm(enfermedad ~ age + trestbps + chol + thalch + oldpeak,
data = train,
family = binomial())
summary(fit_logit)
##
## Call:
## glm(formula = enfermedad ~ age + trestbps + chol + thalch + oldpeak,
## family = binomial(), data = train)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.655191 1.091916 1.516 0.130
## age 0.028543 0.011492 2.484 0.013 *
## trestbps 0.007797 0.005674 1.374 0.169
## chol -0.005510 0.001017 -5.419 6.00e-08 ***
## thalch -0.025479 0.004187 -6.086 1.16e-09 ***
## oldpeak 0.873863 0.109026 8.015 1.10e-15 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 854.49 on 620 degrees of freedom
## Residual deviance: 630.39 on 615 degrees of freedom
## AIC: 642.39
##
## Number of Fisher Scoring iterations: 4
El modelo de regresión logística fue entrenado con las 5 variables predictoras. El summary muestra los coeficientes estimados, los errores estándar y la significancia estadística de cada variable.
p_hat <- predict(fit_logit, newdata = test, type = "response")
pred_clase_logit <- factor(ifelse(p_hat >= 0.5, "Enfermedad", "No enfermedad"),
levels = c("No enfermedad", "Enfermedad"))
cm_logit <- confusionMatrix(pred_clase_logit, test$enfermedad_f, positive = "Enfermedad")
cm_logit
## Confusion Matrix and Statistics
##
## Reference
## Prediction No enfermedad Enfermedad
## No enfermedad 67 23
## Enfermedad 25 91
##
## Accuracy : 0.767
## 95% CI : (0.7032, 0.8229)
## No Information Rate : 0.5534
## P-Value [Acc > NIR] : 1.521e-10
##
## Kappa : 0.5276
##
## Mcnemar's Test P-Value : 0.8852
##
## Sensitivity : 0.7982
## Specificity : 0.7283
## Pos Pred Value : 0.7845
## Neg Pred Value : 0.7444
## Prevalence : 0.5534
## Detection Rate : 0.4417
## Detection Prevalence : 0.5631
## Balanced Accuracy : 0.7633
##
## 'Positive' Class : Enfermedad
##
Con el umbral de clasificación de 0.5, el modelo Logit obtuvo los siguientes resultados:
Accuracy (Exactitud): El modelo clasificó correctamente al X% de los pacientes.
Sensibilidad: El modelo detectó correctamente al X% de los pacientes con enfermedad cardíaca.
Especificidad: El modelo identificó correctamente al X% de los pacientes sin enfermedad.
roc_logit <- roc(response = test$enfermedad_f,
predictor = p_hat,
levels = c("No enfermedad", "Enfermedad"))
thr <- coords(roc_logit, x = "best", best.method = "youden", ret = "threshold")
umbral_optimo <- as.numeric(thr)
umbral_optimo
## [1] 0.5363283
pred_clase_logit_opt <- factor(ifelse(p_hat >= umbral_optimo, "Enfermedad", "No enfermedad"),
levels = c("No enfermedad", "Enfermedad"))
cm_logit_opt <- confusionMatrix(pred_clase_logit_opt, test$enfermedad_f, positive = "Enfermedad")
cm_logit_opt
## Confusion Matrix and Statistics
##
## Reference
## Prediction No enfermedad Enfermedad
## No enfermedad 72 24
## Enfermedad 20 90
##
## Accuracy : 0.7864
## 95% CI : (0.7241, 0.8403)
## No Information Rate : 0.5534
## P-Value [Acc > NIR] : 2.378e-12
##
## Kappa : 0.5697
##
## Mcnemar's Test P-Value : 0.6511
##
## Sensitivity : 0.7895
## Specificity : 0.7826
## Pos Pred Value : 0.8182
## Neg Pred Value : 0.7500
## Prevalence : 0.5534
## Detection Rate : 0.4369
## Detection Prevalence : 0.5340
## Balanced Accuracy : 0.7860
##
## 'Positive' Class : Enfermedad
##
Se calculó el umbral óptimo mediante el índice de Youden, el cual maximiza la suma de sensibilidad y especificidad. El umbral óptimo fue de r umbral_optimo. Con este umbral, las métricas del modelo mejoraron en términos de balance entre sensibilidad y especificidad.
auc_logit <- auc(roc_logit)
auc_logit
## Area under the curve: 0.7982
plot(roc_logit, col = "blue", main = "Curva ROC - Modelo Logit")
El modelo Logit obtuvo un AUC de 0.798. Este valor es ligeramente superior al obtenido por el modelo KNN (AUC = 0.789), lo que indica una capacidad discriminativa aceptable y comparable entre ambos modelos.
El modelo de regresión logística permite interpretar el efecto de cada variable sobre la probabilidad de enfermedad cardíaca:
Edad (age): Por cada año adicional de edad, manteniendo constantes las demás variables, el log-odds de enfermedad cardíaca cambia en r coef(fit_logit)[“age”]. Esto significa que a mayor edad, mayor es la probabilidad de presentar enfermedad cardíaca, lo cual es consistente con la literatura médica.
Presión arterial (trestbps): La presión arterial en reposo mostró un efecto r ifelse(coef(fit_logit)[“trestbps”] > 0, “positivo”, “negativo”) sobre la probabilidad de enfermedad, aunque su contribución es menor en comparación con otras variables.
Colesterol (chol): El colesterol sérico presentó un coeficiente de r coef(fit_logit)[“chol”], indicando que niveles más altos de colesterol se asocian con r ifelse(coef(fit_logit)[“chol”] > 0, “mayor”, “menor”) probabilidad de enfermedad.
Frecuencia cardíaca máxima (thalch): Esta variable mostró un coeficiente de r coef(fit_logit)[“thalch”], lo que indica que a mayor frecuencia cardíaca máxima alcanzada, r ifelse(coef(fit_logit)[“thalch”] < 0, “menor”, “mayor”) es la probabilidad de enfermedad cardíaca. Esto es esperable, ya que una baja frecuencia cardíaca máxima es un indicador de posible disfunción cardíaca.
Depresión ST (oldpeak): La depresión del segmento ST presentó el coeficiente de mayor magnitud (r coef(fit_logit)[“oldpeak”]), confirmando que esta variable tiene el mayor peso en la predicción de enfermedad cardíaca. Por cada unidad adicional de depresión ST, el log-odds de enfermedad aumenta significativamente, lo cual es consistente con el análisis descriptivo donde esta variable mostró la mayor diferencia entre grupos.
plot(roc_logit, col = "blue", main = "Curvas ROC - KNN vs Regresión Logística")
plot(roc_knn, col = "red", add = TRUE)
legend("bottomright",
legend = c(paste("Logit (AUC =", round(auc_logit, 3), ")"),
paste("KNN (AUC =", round(auc_knn, 3), ")")),
col = c("blue", "red"), lwd = 2)
La gráfica muestra las curvas ROC de ambos modelos superpuestas. El modelo Logit (azul) obtuvo un AUC de 0.798, mientras que el modelo KNN (rojo) obtuvo un AUC de 0.789. Ambos modelos presentan un desempeño muy similar, con una ligera ventaja del modelo Logit.
Modelo KNN:
Ventajas:
No hace supuestos sobre la distribución de los datos.
Puede capturar relaciones no lineales entre las variables predictoras y la enfermedad.
Es intuitivo: clasifica según la similitud con pacientes previamente diagnosticados.
Desventajas:
Funciona como una “caja negra”: no permite interpretar cómo afecta cada variable al riesgo de enfermedad.
Requiere almacenar todos los datos de entrenamiento para hacer nuevas predicciones.
Es sensible a la escala de las variables y al valor de k seleccionado.
Modelo Logit (Regresión Logística):
Ventajas:
Proporciona coeficientes interpretables que permiten cuantificar el efecto de cada variable sobre la probabilidad de enfermedad.
Es útil para justificar decisiones clínicas, ya que se puede explicar qué factores influyen en el diagnóstico.
No requiere almacenar los datos de entrenamiento, solo los coeficientes estimados.
Tuvo un AUC ligeramente superior (0.798 vs 0.789).
Desventajas:
Asume una relación lineal entre los predictores y el log-odds de la enfermedad.
Puede verse afectado por valores atípicos o multicolinealidad (aunque en este caso las correlaciones fueron bajas).
Con base en los resultados obtenidos y considerando el objetivo del problema de clasificación de enfermedad cardíaca, recomiendo el modelo de Regresión Logística (Logit) por las siguientes razones:
Interpretabilidad clínica: En el contexto médico, no solo importa predecir correctamente, sino también entender qué factores contribuyen al riesgo de enfermedad. El modelo Logit permite identificar que la depresión ST (oldpeak) y la frecuencia cardíaca máxima (thalch) son los predictores más relevantes, lo cual puede comunicarse a los médicos para mejorar la toma de decisiones clínicas.
Desempeño competitivo: El modelo Logit obtuvo un AUC de 0.798, ligeramente superior al 0.789 del KNN. Aunque la diferencia no es grande, el Logit logra un mejor balance entre sensibilidad y especificidad, especialmente después de ajustar el umbral mediante el índice de Youden.
Eficiencia computacional: A diferencia del KNN, que requiere almacenar todos los datos de entrenamiento para clasificar nuevos pacientes, el Logit solo necesita los coeficientes estimados, lo que lo hace más eficiente para implementarse en sistemas clínicos reales.
Flexibilidad en el umbral de decisión: Si el objetivo clínico fuera maximizar la detección de todos los casos positivos (sensibilidad) para evitar falsos negativos, el modelo Logit permite ajustar el umbral de clasificación según las necesidades del contexto médico, sin necesidad de reentrenar el modelo.
Limitaciones reconocidas: Es importante señalar que ambos modelos tienen un desempeño moderado (AUC ~0.79), lo que sugiere que hay margen de mejora. Para trabajo futuro, se recomienda incluir variables adicionales disponibles en la base de datos (como sexo, tipo de dolor torácico, resultados del electrocardiograma en reposo) que podrían mejorar la capacidad predictiva de ambos modelos.
Con base en el análisis comparativo de los modelos KNN y Regresión Logística implementados para la clasificación de enfermedad cardíaca, se obtienen las siguientes conclusiones:
Desempeño global: Ambos modelos mostraron un desempeño similar y aceptable, con AUC de 0.798 para el modelo Logit y 0.789 para el modelo KNN. Estos valores indican que ambos modelos superan claramente la clasificación aleatoria (AUC = 0.5) y tienen capacidad discriminativa moderada para distinguir entre pacientes con y sin enfermedad cardíaca.
Modelo ganador: La Regresión Logística demostró ser ligeramente superior en términos de AUC (0.798 vs 0.789) y ofrece la ventaja adicional de la interpretabilidad de sus coeficientes. Esto permite no solo clasificar pacientes, sino también entender qué variables (en particular la depresión ST y la frecuencia cardíaca máxima) tienen mayor influencia en el riesgo de enfermedad.
Variables más relevantes: Tanto el análisis descriptivo como los coeficientes del modelo Logit coincidieron en identificar la depresión ST (oldpeak) y la frecuencia cardíaca máxima (thalch) como las variables con mayor capacidad predictiva. Esto es consistente con la literatura médica, donde la isquemia inducida por ejercicio y la respuesta cardíaca al esfuerzo son marcadores fundamentales de enfermedad coronaria.
Limitaciones del estudio:
El desempeño moderado de ambos modelos (AUC ~0.79) sugiere que las 5 variables seleccionadas, aunque relevantes, no capturan toda la complejidad del diagnóstico de enfermedad cardíaca.
No se incluyeron variables categóricas como sexo, tipo de dolor torácico (cp) o resultados del electrocardiograma en reposo (restecg), que podrían mejorar la capacidad predictiva.
La base de datos, aunque valiosa, proviene de centros médicos específicos en Estados Unidos y Europa, por lo que los resultados podrían no ser directamente generalizables a la población colombiana.
El modelo KNN mostró un accuracy en validación cruzada entre 0.66 y 0.69, lo cual es moderado y sugiere que la clasificación basada únicamente en distancias tiene limitaciones para este problema.
A la luz de los resultados obtenidos, considero que el modelo ajustado logró responder PARCIALMENTE al objetivo de la investigación. Justifico esta respuesta de la siguiente manera:
A favor:
Se construyeron y compararon exitosamente dos modelos de clasificación supervisada (KNN y Regresión Logística) utilizando 5 variables fisiológicas para predecir la presencia de enfermedad cardíaca.
Ambos modelos superaron la clasificación aleatoria, con AUC de aproximadamente 0.79, lo que demuestra que las variables seleccionadas (edad, presión arterial, colesterol, frecuencia cardíaca máxima y depresión ST) tienen capacidad predictiva para la enfermedad cardíaca.
Se identificó que el modelo Logit es más adecuado para este problema por su interpretabilidad y desempeño ligeramente superior, cumpliendo con el objetivo de comparar ambos métodos.
El análisis descriptivo y los coeficientes del Logit permitieron identificar las variables con mayor impacto en la predicción, generando conocimiento útil para el contexto clínico.
Limitaciones:
El desempeño de los modelos, aunque aceptable, no es óptimo. Un AUC de 0.798 indica que aproximadamente en el 20% de las comparaciones el modelo no asigna correctamente una mayor probabilidad al paciente enfermo, lo cual no es suficiente para uso clínico sin supervisión médica.
El accuracy del modelo KNN en validación cruzada (0.66-0.69) refleja que, con solo 5 variables continuas, la clasificación basada en distancias no captura completamente la complejidad del diagnóstico cardíaco.
La exclusión de variables categóricas relevantes (sexo, tipo de dolor torácico, resultados electrocardiográficos) limitó el potencial predictivo de ambos modelos, lo cual se reconoce como una oportunidad de mejora para trabajos futuros.
Con base en los hallazgos de este estudio, se proponen las siguientes recomendaciones:
Para el ámbito clínico:
Para mejorar los modelos:
Para trabajo futuro:
Con base en los hallazgos de este estudio, se proponen las siguientes recomendaciones:
Para el ámbito clínico:
Para futuras iteraciones del modelo:
El presente estudio presenta las siguientes limitaciones que deben ser consideradas al interpretar los resultados:
Variables no incluidas: Solo se utilizaron 5 de las 16 variables disponibles en la base de datos original. Variables categóricas como sexo, tipo de dolor torácico, resultados del electrocardiograma en reposo y número de vasos principales coloreados por fluoroscopia no fueron consideradas, a pesar de su relevancia clínica documentada.
Tamaño de muestra: Después de eliminar registros con valores faltantes, se trabajó con 367 observaciones. Si bien es un tamaño aceptable para los modelos implementados, una muestra más grande podría mejorar la estabilidad y generalización de los resultados.
Origen de los datos: La base de datos proviene de cuatro centros médicos en Estados Unidos y Europa (Cleveland, Hungría, Suiza, Long Beach). Los resultados podrían no ser directamente generalizables a la población colombiana o latinoamericana, donde los perfiles de riesgo cardiovascular pueden diferir.
Valores faltantes: Aproximadamente 30 registros fueron eliminados debido a valores faltantes. No se implementaron técnicas de imputación que podrían haber conservado estas observaciones.
Supuestos del modelo Logit: La regresión logística asume una relación lineal entre los predictores y el log-odds de la respuesta. No se realizaron pruebas formales de este supuesto ni análisis de residuos que podrían revelar desviaciones.
Validación externa: Los modelos fueron evaluados únicamente con el conjunto de prueba (25% de los datos). No se realizó validación externa con datos independientes de otros centros médicos o poblaciones.
Desbalance moderado: Aunque las clases están razonablemente balanceadas (55.4% vs 44.6%), una ligera mayoría de pacientes con enfermedad podría influir levemente en las métricas de clasificación.
| Aspecto | Hallazgo |
|---|---|
| Mejor modelo | Regresión Logística (AUC = 0.798) |
| Variables más predictivas | Depresión ST (oldpeak) y frecuencia cardíaca máxima (thalch) |
| Desempeño KNN | AUC = 0.789, accuracy en CV entre 0.66-0.69 |
| ¿Responde al objetivo? | Parcialmente: los modelos superan el azar pero el desempeño es moderado |
| Principal recomendación | Incluir más variables clínicas (sexo, tipo de dolor, ECG) |
| Principal limitación | No se incluyeron variables categóricas relevantes |
| Aplicabilidad práctica | Útil como herramienta de tamizaje inicial, no como diagnóstico definitivo |
James, G., Witten, D., Hastie, T., & Tibshirani, R. (2013). An Introduction to Statistical Learning with Applications in R. Springer. Disponible en: https://www.statlearning.com/
Kuhn, M., & Johnson, K. (2013). Applied Predictive Modeling. Springer.
Detrano, R., Janosi, A., Steinbrunn, W., Pfisterer, M., Schmid, J., Sandhu, S., Guppy, K., Lee, S., & Froelicher, V. (1989). International application of a new probability algorithm for the diagnosis of coronary artery disease. American Journal of Cardiology, 64, 304-310.
UCI Machine Learning Repository. Heart Disease Data Set. Disponible en: https://archive.ics.uci.edu/dataset/45/heart+disease
D’Agostino, R.B., Vasan, R.S., Pencina, M.J., Wolf, P.A., Cobain, M., Massaro, J.M., & Kannel, W.B. (2008). General cardiovascular risk profile for use in primary care: The Framingham Heart Study. Circulation, 117(6), 743-753.
Organización Mundial de la Salud. (2021). Enfermedades cardiovasculares. Disponible en: https://www.who.int/es/news-room/fact-sheets/detail/cardiovascular-diseases-(cvds)