In [234]:
# Importe de bibliotecas

import pandas as pd
from sklearn.preprocessing import MinMaxScaler
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import chi2_contingency
In [235]:
#Importe de datos
from google.colab import drive
drive.mount('/content/drive')

df = pd.read_csv('/content/drive/MyDrive/heart.csv')
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).

Exploración de datos¶

In [236]:
#forma del dataframe
df.shape
Out[236]:
(303, 14)

El dataframe se compone de 303 filas y 14 columnas. Las filas corresponden a la cantidad de registros de pacientes, las columnas al tipo de variable.

In [237]:
df.columns
Out[237]:
Index(['age', 'sex', 'cp', 'trtbps', 'chol', 'fbs', 'restecg', 'thalachh',
       'exng', 'oldpeak', 'slp', 'caa', 'thall', 'output'],
      dtype='object')

Convención de variables¶

La convencion de las variables se presenta a continuación:

age: Edad del individuo (numérica).

sex: Sexo del individuo (categórica).

0: Mujer
1: Hombre

cp: Tipo de dolor de pecho (Angina) (categórica).

1: Angina tipica
2: Angina atipica
3: Dolor no anginal
4: Asintomatico

trtbps: Presión arterial en reposo (numérica).

chol: Colesterol sérico en mg/dl (numérica).

fbs: Nivel de azúcar en sangre en ayunas (categórica).

1: Nivel de azucar > 120 mg/dl
0: Nivel de azucar < 120 mg/dl

restecg: Resultados del electrocardiograma en reposo (categórica).

0: Normal
1: Anormalidad en onda ST-T (inversiones de la onda T y/o elevación o depresión del ST de > 0.05 mV)
2: Hipertrofia ventricular izquierda probable o definitiva según los criterios de Estes

thalachh: Frecuencia cardíaca máxima alcanzada (numérica).

exng: Angina inducida por el ejercicio (categórica).

1 = Si
0 = no

oldpeak: Depresión del segmento ST (numérica).

slp: Pendiente del segmento ST (categórica).

0: Ascendente
1: Plana (Horizontal)
2: Descendente

caa: Número de vasos principales obstruidos (numérica).

thall: Defecto reversible del tálamo (categórica).

0: Normal
1: Defecto fijo
2: Defecto reversible

output: Presencia de enfermedad cardíaca (categórica).

0= Menor probabilidad de ataque cardíaco
1= Mayor probabilidad de ataque cardíaco
In [238]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 303 entries, 0 to 302
Data columns (total 14 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   age       303 non-null    int64  
 1   sex       303 non-null    int64  
 2   cp        303 non-null    int64  
 3   trtbps    303 non-null    int64  
 4   chol      303 non-null    int64  
 5   fbs       303 non-null    int64  
 6   restecg   303 non-null    int64  
 7   thalachh  303 non-null    int64  
 8   exng      303 non-null    int64  
 9   oldpeak   303 non-null    float64
 10  slp       303 non-null    int64  
 11  caa       303 non-null    int64  
 12  thall     303 non-null    int64  
 13  output    303 non-null    int64  
dtypes: float64(1), int64(13)
memory usage: 33.3 KB

El dataframe no contiene valores nulos

Gráficos de distribución de las variables¶

Las siguientes gráficas corresponden a la distribución de los valores de cada característica del dataframe y corresponden a un gráfico de densidad.

In [239]:
df.plot(kind='density', subplots=True, layout=(4, 4), figsize=(12, 12), sharex=False)
plt.tight_layout()
plt.show()
No description has been provided for this image

Teniendo en cuenta que la variable objetivo "output" no presenta diferencias significativas en su distribución, no es necesario realizar balanceo del conjunto de datos.

Se generan histogramas para visualizar la distribución de los valores de cada variable.

In [240]:
# Distribución de las variables numéricas
df.hist(figsize=(15, 10), bins=20, color='skyblue', edgecolor='black')
plt.suptitle('Distribuciones de las Variables Numéricas', fontsize=16)
plt.show()
No description has been provided for this image

Diagrama de cajas antes de preprocesar los datos¶

El siguiente diagrama se emplea para identificar los valores atipicos de cada variable.

In [241]:
df.groupby('output').boxplot(fontsize=20,rot=90,figsize=(20,10),patch_artist=True)
Out[241]:
0
0 Axes(0.1,0.15;0.363636x0.75)
1 Axes(0.536364,0.15;0.363636x0.75)

No description has been provided for this image

Matriz de correlación¶

El conjunto de datos presenta la siguiente matriz de correlación

In [242]:
corr_matrix = df.corr()
corr_matrix["output"].sort_values(ascending=False)
Out[242]:
output
output 1.000000
cp 0.433798
thalachh 0.421741
slp 0.345877
restecg 0.137230
fbs -0.028046
chol -0.085239
trtbps -0.144931
age -0.225439
sex -0.280937
thall -0.344029
caa -0.391724
oldpeak -0.430696
exng -0.436757

In [ ]:
pd.plotting.scatter_matrix(df,figsize=(20,20))
plt.tight_layout()
plt.show()

Mapa de calor de la matriz de correlación

In [ ]:
# Plot figsize
fig, ax = plt.subplots(figsize=(20, 20))
# Generate Color Map
# colormap = sns.diverging_palette(220, 10, as_cmap=True)
colormap = sns.color_palette("Blues", 10)
# Generate Heat Map, allow annotations and place floats in map
sns.heatmap(corr_matrix, cmap=colormap, annot=True, fmt=".2f")
#ax.set_yticklabels(column_names);
plt.show()

Procesamiento de los datos¶

Teniendo en cuenta que el dataset contiene distintas variables de tipo categorico, se procede a aplicar el metodo get_dummies, para facilitar la interpretación del modelo.

In [ ]:
# Definicion de variables categoricas
categorical_vars = ['sex', 'cp', 'fbs', 'restecg', 'exng', 'slp', 'thall']

# aplica get_dummies
df_heart = pd.get_dummies(df, columns=categorical_vars, drop_first=False)
df_heart = df_heart.astype(int)

# muestra el nuevo Dataframe
df_heart.head()
In [ ]:
# Mover la columna 'output' a la última posición
output_col = df_heart.pop('output')
df_heart['output'] = output_col

# Mostrar los primeros registros del DataFrame resultante
df_heart.head()
In [ ]:
df_heart.plot(kind='density', subplots=True, layout=(7, 4), figsize=(12, 12), sharex=False)
plt.tight_layout()
plt.show()
In [ ]:
# Distribución de las variables numéricas
df_heart.hist(figsize=(15, 10), bins=20, color='skyblue', edgecolor='black')
plt.suptitle('Distribuciones de las Variables ', fontsize=16)
plt.tight_layout()

plt.show()

Matriz de correlación de datos procesados¶

In [ ]:
corr_matrix = df_heart.corr()
corr_matrix["output"].sort_values(ascending=False)
In [ ]:
# Plot figsize
fig, ax = plt.subplots(figsize=(20, 20))
# Generate Color Map
# colormap = sns.diverging_palette(220, 10, as_cmap=True)
colormap = sns.color_palette("Blues", 10)
# Generate Heat Map, allow annotations and place floats in map
sns.heatmap(corr_matrix, cmap=colormap, annot=True, fmt=".2f")
#ax.set_yticklabels(column_names);
plt.show()

Definición de algoritmos¶

Teniendo en cuenta que la variable objetivo del dataframe es de tipo binario, el tipo de algoritmo a elegir debe ser de clasificación. De acuerdo a la bibliografia encontrada y expuesta de manera mas detallada en el estado del arte, se escogen los algoritmos Random Forest, CNN y

Conjuntos de entrenamiento y pruebas¶

In [ ]:
#Importe de objetos
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
from sklearn.metrics import roc_curve, roc_auc_score, auc
import matplotlib.pyplot as plt


#Se separan las caracteristicas de la variable objetivo.

X = df_heart.drop('output', axis=1)
y = df_heart['output']


#Definción de conjunto de entrenamiento y de pruebas
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

Implementación de Random Forest¶

In [ ]:
#Importe de objeto
from sklearn.ensemble import RandomForestClassifier
# Definición del modelo
RF_model = RandomForestClassifier(n_estimators=100, random_state=42)

# Entrenamiento
RF_model.fit(X_train, y_train)
In [ ]:
#Predicción
RF_pred = RF_model.predict(X_test)



# Matriz de Confusión
conf_matrix = confusion_matrix(y_test, RF_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(conf_matrix, annot=True, fmt="d", cmap="Blues")
plt.title("Matriz de Confusión - Random Forest")
plt.ylabel("Valores Reales")
plt.xlabel("Valores Predicción")
plt.show()


# Evaluaación
#print("Confusion Matrix:\n", confusion_matrix(y_test, RF_pred))
print("\nClassification Report:\n", classification_report(y_test, RF_pred))
print("\nAccuracy Score:", accuracy_score(y_test, RF_pred))
In [ ]:
# Hacer predicciones de probabilidad sobre el conjunto de prueba
RF_pred_prob = RF_model.predict_proba(X_test)[:, 1]

# Calculo de la curva ROC
RF_fpr, RF_tpr, thresholds = roc_curve(y_test, RF_pred_prob)
RF_roc_auc = auc(RF_fpr, RF_tpr)

# figura de curva ROC
plt.figure()
plt.plot(RF_fpr, RF_tpr, color='darkorange', lw=2, label=f'ROC curve (area = {RF_roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC)')
plt.legend(loc='lower right')
plt.show()

#valor de AUC
print(f'AUC: {RF_roc_auc:.2f}')

XGBoost¶

In [ ]:

In [ ]:
import xgboost as xgb

#from sklearn.metrics import accuracy_score, classification_report

# Definición del modelo
model = xgb.XGBClassifier(use_label_encoder=False, eval_metric='mlogloss')

# Entrenamiento
model.fit(X_train, y_train)

# Predicciones
xgb_pred = model.predict(X_test)
xgb_prob = model.predict_proba(X_test)[:, 1]
# Evaluación
accuracy = accuracy_score(y_test, xgb_pred)
report = classification_report(y_test, xgb_pred)

print(f'Accuracy: {accuracy}')
print(f'Classification Report:\n{report}')
In [ ]:
# Matriz de Confusión
conf_matrix = confusion_matrix(y_test, xgb_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(conf_matrix, annot=True, fmt="d", cmap="Blues")
plt.title("Matriz de Confusión - XGBoost")
plt.ylabel("Valores Reales")
plt.xlabel("Valores Predicción")
plt.show()

# Curva ROC
xgb_fpr, xgb_tpr, _ = roc_curve(y_test, xgb_prob)
xgb_roc_auc = auc(xgb_fpr, xgb_tpr)

plt.figure(figsize=(8, 6))
plt.plot(xgb_fpr, xgb_tpr, color='darkorange', lw=2, label=f'ROC curve (area = {xgb_roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Curva ROC')
plt.legend(loc="lower right")
plt.show()

Implementacion de K-Nearest Neighbors¶

In [ ]:
from sklearn.neighbors import KNeighborsClassifier
# Definición del modelo
knn = KNeighborsClassifier(n_neighbors=5)

# Entrenamiento
knn.fit(X_train, y_train)

# Predicciones
knn_y_pred = knn.predict(X_test)
knn_prob = knn.predict_proba(X_test)[:, 1]

# Evaluación
accuracy = accuracy_score(y_test, knn_y_pred)
report = classification_report(y_test, knn_y_pred)
print(f'Accuracy: {accuracy}')
print(f'Classification Report:\n{report}')
In [ ]:
# Matriz de Confusión
conf_matrix = confusion_matrix(y_test, knn_y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(conf_matrix, annot=True, fmt="d", cmap="Blues")
plt.title("Matriz de Confusión - K-Nearest Neighbors")
plt.ylabel("Valores Reales")
plt.xlabel("Valores Predicción")
plt.show()

# Curva ROC
knn_fpr, knn_tpr, _ = roc_curve(y_test, knn_prob)
knn_roc_auc = auc(knn_fpr, knn_tpr)

plt.figure(figsize=(8, 6))
plt.plot(knn_fpr, knn_tpr, color='darkorange', lw=2, label=f'ROC curve (area = {knn_roc_auc:.2f})')
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Curva ROC')
plt.legend(loc="lower right")
plt.show()

SVM (Support Vector Machines)¶

In [ ]:
from sklearn.svm import SVC
from sklearn.neural_network import MLPClassifier
In [ ]:
from google.colab import drive
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report, roc_auc_score
from sklearn.svm import SVC
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv1D, Flatten, MaxPooling1D, Dropout

# Montar Google Drive
drive.mount('/content/drive')

# Cargar datos
df = pd.read_csv('/content/drive/MyDrive/heart.csv')


X = df.drop(columns=['output'])
y = df['output']


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)


scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
In [ ]:
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score, classification_report, roc_auc_score

# Definir los parámetros para GridSearchCV
param_grid = {
    'C': [0.1, 1, 10, 100],
    'gamma': [1, 0.1, 0.01, 0.001],
    'kernel': ['rbf', 'linear']
}

# Realizar búsqueda de hiperparámetros
grid = GridSearchCV(SVC(probability=True), param_grid, refit=True, cv=5, scoring='roc_auc')
grid.fit(X_train_scaled, y_train)

# Mejor modelo
best_svm = grid.best_estimator_
print(f"Mejores parámetros SVM: {grid.best_params_}")

# Predicciones y evaluación
svm_y_pred = best_svm.predict(X_test_scaled)
svm_prob = best_svm.predict_proba(X_test_scaled)[:, 1]
svm_accuracy = accuracy_score(y_test, svm_y_pred)
svm_auc = roc_auc_score(y_test, svm_prob)

print(f"Optimized SVM Accuracy: {svm_accuracy}")
print(f"Optimized SVM AUC: {svm_auc}")
print("Optimized SVM Classification Report:")
print(classification_report(y_test, svm_y_pred))

CNN (Red Neuronal Convolucional)¶

In [ ]:
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv1D, Flatten, MaxPooling1D, Dropout
from tensorflow.keras.callbacks import ReduceLROnPlateau

# Preparar datos para CNN
X_train_cnn = X_train_scaled[..., None]
X_test_cnn = X_test_scaled[..., None]

# Arquitectura mejorada de CNN
cnn = Sequential([
    Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(X_train_cnn.shape[1], 1)),
    MaxPooling1D(pool_size=2),
    Dropout(0.3),
    Conv1D(filters=128, kernel_size=3, activation='relu'),
    MaxPooling1D(pool_size=2),
    Dropout(0.3),
    Flatten(),
    Dense(128, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.01)),
    Dropout(0.4),
    Dense(1, activation='sigmoid')
])

# Compilar el modelo
cnn.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# Callbacks para optimización de aprendizaje
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=5, min_lr=0.001)

# Entrenamiento del modelo CNN
cnn.fit(X_train_cnn, y_train, epochs=70, batch_size=32, validation_data=(X_test_cnn, y_test), callbacks=[reduce_lr], verbose=1)

# Evaluación
cnn_y_pred = (cnn.predict(X_test_cnn) > 0.5).astype("int32")
cnn_prob = cnn.predict(X_test_cnn).ravel()
cnn_accuracy = accuracy_score(y_test, cnn_y_pred)
cnn_auc = roc_auc_score(y_test, cnn_prob)

print(f"Optimized CNN Accuracy: {cnn_accuracy}")
print(f"Optimized CNN AUC: {cnn_auc}")
print("Optimized CNN Classification Report:")
print(classification_report(y_test, cnn_y_pred))

Matriz de Confusión para CNN¶

In [ ]:
matrix_cnn = confusion_matrix(y_test, cnn_y_pred)
In [ ]:
# Graficar matriz de confusión para SVM
plt.figure(figsize=(10, 5))
sns.heatmap(matrix_cnn, annot=True, fmt='d', cmap='Blues', xticklabels=['Clase 0', 'Clase 1'], yticklabels=['Clase 0', 'Clase 1'])
plt.title('Matriz de Confusión - CNN')
plt.ylabel("Valores Reales")
plt.xlabel("Valores Predicción")
plt.show()
In [ ]:
svm_cm = confusion_matrix(y_test, svm_y_pred)
In [ ]:
cnn_cm = confusion_matrix(y_test, cnn_y_pred)
In [ ]:
svm_cm = confusion_matrix(y_test, svm_y_pred)
# Graficar matriz de confusión para SVM
plt.figure(figsize=(10, 5))
sns.heatmap(svm_cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Clase 0', 'Clase 1'], yticklabels=['Clase 0', 'Clase 1'])
plt.title('Matriz de Confusión - SVM')
plt.ylabel("Valores Reales")
plt.xlabel("Valores Predicción")
plt.show()

Grafíca Comparativa de curvas ROC¶

In [ ]:
# Graficar las curvas ROC
plt.figure(figsize=(8, 6))
plt.plot(RF_fpr, RF_tpr, color='blue', lw=2, label=f'Random Forest (AUC = {RF_roc_auc:.2f})')
plt.plot(xgb_fpr, xgb_tpr, color='red', lw=2, label=f'XGBoost (AUC = {xgb_roc_auc:.2f})')
plt.plot(knn_fpr, knn_tpr, color='green', lw=2, label=f'K-Nearest Neighbors (AUC = {knn_roc_auc:.2f})')
plt.plot(svm_fpr, svm_tpr, label=f'SVM (AUC = {svm_auc:.2f})', linestyle='-')
plt.plot(cnn_fpr, cnn_tpr, label=f'CNN (AUC = {cnn_auc:.2f})', linestyle='-')

plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Curvas ROC')
plt.legend(loc="lower right")
plt.show()
In [ ]:
!jupyter nbconvert --to html "/content/drive/MyDrive/Colab Notebooks/Prediccion_infarto.ipynb"