Predicción de Enfermedad Cardíaca con Inteligencia Artificial

Inteligencia Artificial Tiempo de lectura: 9 min.

Introducción

Las enfermedades cardiovasculares (ECV) son la principal causa de muerte en el mundo, con 17,9 millones de fallecimientos en 2019, equivalentes al 32% de todas las muertes globales. El 85% de estos casos se debieron a infartos de miocardio y accidentes cerebrovasculares, afectando especialmente a países de ingresos bajos y medianos. Además, el 38% de las muertes prematuras por enfermedades no transmisibles se atribuyen a las ECV. Fuente: Organización Mundial de la Salud.

La insuficiencia cardíaca emerge como una consecuencia común de las enfermedades cardiovasculares, y las personas con ECV o con alto riesgo cardiovascular requieren detección y manejo temprano. En este contexto, los modelos de Machine Learning pueden proporcionar una ayuda invaluable para la identificación temprana de estos riesgos.

Dataset Utilizado

Para este estudio se utilizó un conjunto de datos que combina cinco conjuntos independientes de datos de cardiología, lo cual constituye el mayor dataset sobre enfermedades cardíacas disponible para investigación. El dataset contiene 918 observaciones con 11 características predictoras y una variable objetivo binaria (0 = Normal, 1 = Enfermedad cardíaca). Fuente: Kaggle.

Características del Dataset

  1. Age: Edad del paciente [años]
  2. Sex: Sexo del paciente [M: Masculino, F: Femenino]
  3. ChestPainType: Tipo de dolor torácico [TA: angina típica, ATA: angina atípica, NAP: dolor no anginoso, ASY: asintomático]
  4. RestingBP: Presión arterial en reposo [mm Hg]
  5. Cholesterol: Colesterol sérico [mm/dl]
  6. FastingBS: Azúcar en sangre en ayunas [1: si Azúcar en sangre en ayunas > 120 mg/dl, 0: en caso contrario]
  7. RestingECG: Resultados del electrocardiograma en reposo [Normal: Normal, ST: con anomalía de la onda ST-T (inversiones de la onda T y/o elevación o depresión del ST de > 0,05 mV), LVH: que muestra hipertrofia ventricular izquierda probable o definitiva según los criterios de Estes]
  8. MaxHR: Frecuencia cardíaca máxima alcanzada [Valor numérico entre 60 y 202]
  9. ExerciseAngina: Angina inducida por el ejercicio [Y: Sí, N: No]
  10. Oldpeak: Depresión del ST [Valor numérico medido en depresión]
  11. ST_Slope: Pendiente del segmento ST del ejercicio máximo [Arriba: pendiente ascendente, Plano: plano, Abajo: pendiente descendente]
  12. HeartDisease: Clase de salida [1: enfermedad cardíaca, 0: normal]

Nota: El segmento ST es una parte específica de la onda del electrocardiograma que se encuentra entre el complejo QRS (despolarización ventricular) y la onda T (repolarización ventricular).

Metodología: Enfoque de 7 Fases

El estudio se estructura en siete fases sistemáticas que abarcan desde la carga inicial de datos hasta la evaluación final de modelos optimizados. Se evaluaron 9 algoritmos diferentes de Machine Learning, comparando su rendimiento tanto con hiperparámetros por defecto (línea base) como con hiperparámetros ajustados (optimización).

Fase 1: Carga del Conjunto de Datos

La primera fase consiste en la importación y carga inicial del dataset utilizando la librería pandas de Python.

Código: Carga del Dataset
# FASE 1: Carga del conjunto de datos
    import warnings
    warnings.filterwarnings("ignore")
    import pandas as pd

    df = pd.read_csv('heart.csv')
    pd.set_option('display.max_columns', None)
    df.head(3)

Resultados de la Carga del Conjunto de Datos


Descripción de la imagen
Fase 2: Exploración Básica del Conjunto de Datos

En esta fase se determina la información fundamental del dataset: dimensiones, tipos de datos, clasificación de variables y detección de valores nulos.

Código: Exploración Básica
# FASE 2: Exploración básica del conjunto de datos
    # Número de registros y columnas
    print("DIMENSIONES DEL DATASET:")
    print(f"Registros (filas): {df.shape[0]}")
    print(f"Columnas (variables): {df.shape[1]}\n")

    # Tipos de datos por columna
    print("TIPOS DE DATOS DE CADA COLUMNA:")
    print(df.dtypes)
    print()

    # Clasificación de variables: numéricas vs categóricas
    print("CLASIFICACIÓN DE VARIABLES (Numéricas vs Categóricas):")
    numericas = df.select_dtypes(include=['number']).columns.tolist()
    categoricas = df.select_dtypes(exclude=['number']).columns.tolist()
    print(f"Variables numéricas ({len(numericas)}): {numericas}")
    print(f"Variables categóricas ({len(categoricas)}): {categoricas}\n")

    # Valores nulos por columna
    print("VALORES NULOS POR COLUMNA:")
    nulos = df.isnull().sum()
    print(nulos.to_string())

Resultados de la Exploración Básica

El dataset contiene 918 registros y 12 columnas (11 características + 1 variable objetivo). Se identificaron 8 variables numéricas y 3 categóricas, sin presencia de valores nulos, lo que indica un dataset limpio y completo.

Fase 3: Exploración Profunda del Conjunto de Datos

Esta fase incluye análisis estadísticos descriptivos, visualizaciones de la distribución de clases, matrices de correlación y detección de outliers mediante boxplots.

Código: Análisis Exploratorio Avanzado
# FASE 3: Exploración más a fondo del conjunto de datos
    import seaborn as sns
    import matplotlib.pyplot as plt

    # 1. Estadísticas descriptivas de variables numéricas
    print("ESTADÍSTICAS DESCRIPTIVAS (Variables Numéricas):")
    print(df.describe())

    # 2. Distribución de la variable objetivo 'HeartDisease'
    print("\nDISTRIBUCIÓN DE LA VARIABLE OBJETIVO (HeartDisease):")
    print(df['HeartDisease'].value_counts())
    print("\nDISTRIBUCIÓN PORCENTUAL:")
    print(df['HeartDisease'].value_counts(normalize=True).map("{:.2%}".format))

    # 3. Gráfico de barras: distribución de clases
    plt.figure(figsize=(6,4))
    sns.countplot(x='HeartDisease', data=df, palette='Set2')
    plt.title('Distribución de Clases (HeartDisease)')
    plt.xlabel('Clase (0 = No Enfermo, 1 = Enfermo)')
    plt.ylabel('Frecuencia')
    plt.show()

Análisis Exploratorio Profundo


Descripción de la imagen

Descripción de la imagen

Descripción de la imagen
Fase 4: Preprocesamiento y División del Conjunto de Datos

En esta etapa se realiza el escalado de variables numéricas, codificación de variables categóricas, y la división estratégica de los datos en conjuntos de entrenamiento, prueba y predicción.

Código: Preprocesamiento de Datos
# FASE 4: Preprocesamiento y división del conjunto de datos
    import numpy as np
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import OneHotEncoder, StandardScaler
    from sklearn.compose import ColumnTransformer

    # 1. Separar 5 registros aleatorios para pronóstico futuro
    df_sample = df.sample(n=5, random_state=42)
    df_rest = df.drop(df_sample.index).reset_index(drop=True)

    # 2. Identificar columnas numéricas y categóricas
    X = df_rest.drop(columns=['HeartDisease'])  # características
    y = df_rest['HeartDisease']                 # variable objetivo

    numeric_features = X.select_dtypes(include=['number']).columns.tolist()
    categorical_features = X.select_dtypes(exclude=['number']).columns.tolist()

    # 3. Definir transformaciones para preprocesamiento
    preprocessor = ColumnTransformer(
        transformers=[
            ('num', StandardScaler(), numeric_features),
            ('cat', OneHotEncoder(handle_unknown='ignore', drop='first'), categorical_features)
        ])

    # 4. Aplicar transformaciones y dividir datasets
    X_processed = preprocessor.fit_transform(X)
    X_train, X_test, y_train, y_test = train_test_split(
        X_processed, y, test_size=0.2, random_state=42, stratify=y
    )

Resultados Preprocesamiento


Descripción de la imagen
Fase 5: Entrenamiento con Hiperparámetros por Defecto

Se entrenan y evalúan 9 modelos diferentes utilizando sus configuraciones por defecto para establecer una línea base de rendimiento.

Código: Entrenamiento de Modelos Base
# FASE 5: Entrenamiento y evaluación de modelos con hiperparámetros por defecto
    from sklearn.linear_model import LogisticRegression
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, AdaBoostClassifier
    from sklearn.svm import SVC
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.naive_bayes import GaussianNB
    from xgboost import XGBClassifier
    from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

    # Definición de modelos
    modelos = [
        ("Decision Tree", DecisionTreeClassifier(random_state=42)),
        ("AdaBoost", AdaBoostClassifier(random_state=42)),
        ("Gradient Boosting", GradientBoostingClassifier(random_state=42)),
        ("Random Forest", RandomForestClassifier(random_state=42)),
        ("SVM", SVC(random_state=42, probability=True)),
        ("Gaussian Naive Bayes", GaussianNB()),
        ("Logistic Regression", LogisticRegression(max_iter=1000, random_state=42)),
        ("K Neighbors", KNeighborsClassifier()),
        ("XGBoost", XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42))
    ]

    # Entrenar y evaluar modelos
    resultados = []
    for nombre, modelo in modelos:
        modelo.fit(X_train, y_train)
        y_pred = modelo.predict(X_test)
        
        # Calcular métricas
        accuracy = accuracy_score(y_test, y_pred)
        precision = precision_score(y_test, y_pred)
        recall = recall_score(y_test, y_pred)
        f1 = f1_score(y_test, y_pred)
        
        resultados.append({
            "Modelo": nombre,
            "Accuracy": accuracy,
            "Precision": precision,
            "Recall": recall,
            "F1-Score": f1
        })

Modelos con Hiperparámetros por Defecto

Descripción de la imagen

Exactitud Inicial de Referencia

Basado en los resultados de validación cruzada (CV Accuracy) podemos alcanzar exactitud en los resultados del orden del 86.16%. Esta métrica podría ser engañosa si las clases están desbalanceadas, sin embargo, no es el caso de este estudio porque las clases de la variable objetivo están bien balancedas. Sin ajuste de parámetros, las mejores líneas bases fueron alcanzados por:

  1. Random Forest
  2. SVM
  3. Logistic Regression

Nota: Puede ampliar la información sobre métricas de evalación de modelos en el artículo Métricas de Evaluación de Modelos de Clasificación Binaria .

Fase 6: Optimización de Hiperparámetros

Se implementa GridSearchCV para encontrar la configuración óptima de hiperparámetros para cada modelo, maximizando el F1-Score.

Código: Optimización con GridSearchCV
# FASE 6: Entrenamiento y evaluación con ajuste de hiperparámetros
    from sklearn.model_selection import GridSearchCV

    # Definición de modelos con espacios de búsqueda
    modelos_con_grid = [
        ("Random Forest", RandomForestClassifier(random_state=42), {
            'n_estimators': [50, 100, 150],
            'max_depth': [None, 5, 10],
            'min_samples_split': [2, 4],
            'min_samples_leaf': [1, 2],
            'bootstrap': [True, False]
        }),
        ("SVM", SVC(random_state=42, probability=True), {
            'C': [0.1, 1, 10],
            'kernel': ['linear', 'rbf'],
            'gamma': ['scale', 'auto']
        }),
        ("XGBoost", XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42), {
            'n_estimators': [50, 100, 150],
            'learning_rate': [0.01, 0.1, 0.2],
            'max_depth': [3, 5],
            'min_child_weight': [1, 3, 5]
        })
    ]

    # Optimizar hiperparámetros
    for nombre, modelo, param_grid in modelos_con_grid:
        grid_search = GridSearchCV(
            estimator=modelo, 
            param_grid=param_grid, 
            cv=5, 
            scoring='f1', 
            n_jobs=-1
        )
        grid_search.fit(X_train, y_train)
        print(f"Mejores parámetros para {nombre}: {grid_search.best_params_}")

Modelos con Hiperparámetros Ajustados

Descripción de la imagen

Mejora con Hiperparámetros Optimizados

En este caso, en la validación cruzada, podemos alcanzar exactitud en los resultados del orden del 87.53%. Tras la optimización, los tres mejores modelos corresponden a:

  1. K Neighbors (n_neighbors=10, p=1, weights='distance')
  2. XGBoost (learning_rate=0.1, max_depth=3, n_estimators=50)
  3. Random Forest (max_depth=5, n_estimators=50, bootstrap=True)

Para la selección de los mejores modelos se consideró: F1-Score (Equilibrio entre precisión y recall), ROC AUC (Capacidad de discriminación entre clases), Log Loss (Calibración de probabilidades...menos es mejor) y CV Accuracy (Estabilidad de la exactitud del modelo...promedio en validación cruzada).

Fase 7: Predicciones con los Mejores Modelos

Finalmente, se entrenan los tres mejores modelos identificados y se realizan predicciones sobre los 5 registros reservados para validar la efectividad del sistema.

Código: Predicciones Finales
# FASE 7: Predicciones con los tres mejores modelos identificados
    from sklearn.neighbors import KNeighborsClassifier
    from xgboost import XGBClassifier
    from sklearn.ensemble import RandomForestClassifier

    # Entrenar modelos optimizados
    knn = KNeighborsClassifier(n_neighbors=10, p=1, weights='distance')
    xgb = XGBClassifier(learning_rate=0.1, max_depth=3, min_child_weight=3, 
                        n_estimators=50, subsample=1.0, use_label_encoder=False, 
                        eval_metric='logloss')
    rf = RandomForestClassifier(bootstrap=True, max_depth=5, min_samples_leaf=1, 
                            min_samples_split=2, n_estimators=50, random_state=42)

    # Entrenar con todo el conjunto de entrenamiento
    knn.fit(X_train, y_train)
    xgb.fit(X_train, y_train)
    rf.fit(X_train, y_train)

    # Realizar predicciones en datos reservados
    predicciones = []
    for modelo, nombre in zip([knn, xgb, rf], ['KNN', 'XGBoost', 'Random Forest']):
        y_pred = modelo.predict(X_sample_processed)
        y_proba = modelo.predict_proba(X_sample_processed)[:, 1]
        
        for i, (pred, proba) in enumerate(zip(y_pred, y_proba)):
            predicciones.append({
                "Modelo": nombre,
                "Registro": i + 1,
                "Predicción": "Enfermo (1)" if pred == 1 else "No enfermo (0)",
                "Probabilidad": f"{proba:.2%}"
            })

    # Mostrar resultados
    df_predicciones = pd.DataFrame(predicciones)
    print("PREDICCIONES EN LOS 5 REGISTROS RESERVADOS:")
    print(df_predicciones)

Resultados Predicciones


Descripción de la imagen

Registros Reservados para Predicción


Descripción de la imagen

Validación de Resultados

Las predicciones de los tres mejores modelos fueron completamente correctas, coincidiendo perfectamente con los resultados reales de los datos reservados. Este resultado es altamente satisfactorio, especialmente considerando que estos datos corresponden a información completamente nueva, no utilizada durante el entrenamiento ni las pruebas.

Conclusiones

Este estudio demuestra la efectividad de los modelos de Machine Learning para la predicción temprana de enfermedades cardíacas. Los modelos finales seleccionados (K Neighbors, XGBoost y Random Forest) muestran alta confiabilidad y robustez, validados mediante:

  • Validación cruzada (5 fold)
  • Optimización rigurosa de hiperparámetros
  • Evaluación en datos completamente nuevos
  • Múltiples métricas de rendimiento

Estos resultados sugieren que la implementación de sistemas de IA para la detección temprana de insuficiencia cardíaca puede contribuir significativamente a la reducción de la mortalidad por enfermedades cardiovasculares.

Impacto y Aplicaciones Futuras

La implementación de estos modelos en entornos clínicos podría revolucionar la detección temprana de enfermedades cardiovasculares, permitiendo intervenciones oportunas que podrían salvar millones de vidas anualmente. Los sistemas de apoyo a la decisión clínica basados en estos algoritmos representan un paso crucial hacia una medicina preventiva más efectiva y personalizada.

El enfoque metodológico presentado en este estudio puede ser replicado y adaptado para otros conjuntos de datos médicos, contribuyendo al desarrollo de herramientas de diagnóstico asistido por IA más precisas y confiables. ¡No esperes a que el futuro llegue… Sé parte de él!. Agenda una consulta gratuita con nuestro equipo y descubre cómo podemos personalizar esta tecnología para tu institución..