Sistema de Predicción de Enfermedades Cardíacas

Implementación completa de machine learning con procesamiento robusto de datos y gestión de modelos. Características principales: - Pipeline automatizado de datos que maneja valores faltantes y escalado de características - División estratificada de datos con balanceo de clases - Seguimiento de rendimiento del modelo (Precisión, Recall, AUC-ROC) - Despliegue Dockerizado con gestión de dependencias Poetry - Versionado automático de modelos con serialización timestamp - Métricas de evaluación comprehensivas y reproductibilidad

Sistema de Predicción de Enfermedades Cardíacas

Tecnologías

Python 3.10 XGBoost Scikit-learn Pandas Joblib Docker Poetry

Detalles Técnicos

/src/data/data_loader.py

 1 | import pandas as pd
 2 | 
 3 | def load_data(file_path: str) -> pd.DataFrame:
 4 |     """
 5 |     Carga los datos desde un archivo CSV.
 6 | 
 7 |     Args:
 8 |         file_path (str): Ruta del archivo CSV.
 9 | 
10 |     Returns:
11 |         pd.DataFrame: DataFrame con los datos cargados.
12 |     """
13 |     return pd.read_csv(file_path)
14 | 
/src/data/data_loader.py visualization

/src/data/data_processor.py

 1 | import pandas as pd
 2 | import numpy as np
 3 | 
 4 | def process_data(df: pd.DataFrame, columns_to_impute: list, target_column: str = None) -> tuple[pd.DataFrame, pd.Series]:
 5 |     """
 6 |     Procesa los datos:
 7 |     - Imputa los valores faltantes
 8 |     - Escalar las variables numéricas
 9 | 
10 |     Args:
11 |        df (pd.DataFrame): DataFrame con los datos a procesar.
12 |        columns_to_impute (list): Lista de columnas a imputar los valores faltantes.
13 |        target_column (str, optional): Nombre de la columna objetivo.
14 | 
15 |     Returns:
16 |        pd.DataFrame: DataFrame con los datos procesados.
17 |        pd.Series: Serie con la variable objetivo.
18 |     """
19 |     # Reemplazar valores 0 por NaN en las columnas a imputar
20 |     df[columns_to_impute] = df[columns_to_impute].replace(0, np.nan)
21 |     
22 |     # Extraer la columna objetivo si es proporcionada
23 |     target = df[target_column] if target_column else None
24 |     
25 |     return df, target
26 | 
/src/data/data_processor.py visualization

/src/data/data_splitter.py

 1 | from sklearn.model_selection import train_test_split
 2 | import pandas as pd
 3 | 
 4 | def split_data(data: pd.DataFrame, target_column: str, test_size=0.2, random_state=42, stratify: bool = True) -> tuple[pd.DataFrame, pd.DataFrame, pd.Series, pd.Series]:
 5 |     """
 6 |     Dividir los datos en conjuntos de entrenamiento y prueba
 7 | 
 8 |     Args:
 9 |         data (pd.DataFrame): Dataframe que contiene los datos a dividir
10 |         target_column (str): Nombre de la columna objetivo
11 |         test_size (float, optional): Proporción de los datos que quedan en el test. Defaults to 0.2.
12 |         random_state (int, optional): Semilla para la aleatoriedad. Defaults to 42.
13 | 
14 |     Returns:
15 |         Tupla: Una tupla que contiene los conjuntos de entrenamiento y prueba
16 |         - X_train (pd.DataFrame): Conjunto de entrenamiento de las variables independientes
17 |         - X_test (pd.DataFrame): Conjunto de prueba de las variables independientes
18 |         - y_train (pd.Series): Conjunto de entrenamiento de la variable objetivo
19 |         - y_test (pd.Series): Conjunto de prueba de la variable objetivo
20 |     """
21 |     X = data.drop(columns=target_column, axis=1)
22 |     y = data[target_column]
23 |     
24 |     # Dividir el conjunto de datos en entrenamiento y prueba
25 |     if stratify:
26 |         X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state, stratify=y)
27 |     else:
28 |         X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
29 |     
30 |     return X_train, X_test, y_train, y_test
31 | 

/src/main.py

 1 | import sys
 2 | import os
 3 | # Agregar la raíz del proyecto al PYTHONPATH
 4 | sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
 5 | from src.data.data_loader import load_data
 6 | from src.data.data_processor import process_data
 7 | import pandas as pd
 8 | from src.data.data_splitter import split_data
 9 | from src.model.trainer import train_model
10 | from src.model.evaluator import evaluate_model
11 | from src.model.saver import save_model
12 | 
13 | def main():
14 |     
15 |     # Cargar los datos
16 |     heart_disease = load_data(file_path = "data/raw/heart.csv")
17 |     
18 |     # Preprocesar los datos
19 |     processed_data, target = process_data(
20 |                                   df=heart_disease, 
21 |                                   columns_to_impute=['trestbps', 'chol', 'thalach', 'oldpeak'],  # columnas que pueden tener NaN
22 |                                   target_column='num'  # Cambio para coincidir con la columna objetivo del dataset
23 |                                   )
24 |     
25 |     # Dividir los datos en conjuntos de entrenamiento y prueba
26 |     X_train, X_test, y_train, y_test = split_data(processed_data, target_column='num')
27 |     
28 |     # Convertir 'y' en binario
29 |     y_train_binary = (y_train > 0).astype(int)
30 |     y_test_binary = (y_test > 0).astype(int)
31 | 
32 |     # Entrenar el modelo
33 |     model = train_model(X_train=X_train, y_train=y_train_binary)
34 | 
35 | 
36 | 
37 |     # Evaluar el modelo
38 |     accuracy, precision, recall, f1, auc = evaluate_model(model, test_data=X_test, y_test=y_test_binary)
39 | 
40 |     # Imprimir las métricas
41 |     print(f"Accuracy: {accuracy:.2f}")
42 |     print(f"Precision: {precision:.2f}")
43 |     print(f"Recall: {recall:.2f}")
44 |     print(f"F1: {f1:.2f}")
45 |     print(f"AUC: {auc:.2f}")
46 | 
47 |     
48 |     # Guardar el modelo
49 |     save_model(model, model_path="models/trained_model")
50 |     
51 | if __name__ == "__main__":
52 |     main()
53 | 

/src/model/evaluator.py

 1 | from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
 2 | import xgboost
 3 | import pandas as pd
 4 | 
 5 | def evaluate_model(model: xgboost.sklearn.XGBClassifier, test_data: pd.DataFrame, y_test: pd.Series) -> tuple[float, float, float, float, float]:
 6 |     """
 7 |     Función para evaluar el modelo
 8 |     
 9 |     Args:
10 |         model (xgboost.sklearn.XGBClassifier): Modelo a evaluar
11 |         test_data (pd.DataFrame): Datos de prueba
12 |         y_test (pd.Series): Etiquetas de prueba
13 | 
14 |     Returns:
15 |         tuple[float, float, float, float, float]: Accuracy, Precision, Recall, F1, AUC
16 |     """
17 |     y_pred = model.predict(test_data)
18 |     y_prob = model.predict_proba(test_data)[:, 1]
19 |     
20 |     accuracy = accuracy_score(y_test, y_pred)
21 |     precision = precision_score(y_test, y_pred)
22 |     recall = recall_score(y_test, y_pred)
23 |     f1 = f1_score(y_test, y_pred)
24 |     auc = roc_auc_score(y_test, y_prob)
25 |     
26 |     return accuracy, precision, recall, f1, auc
27 | 
/src/model/evaluator.py visualization

/src/model/saver.py

 1 | from datetime import datetime
 2 | import joblib
 3 | import xgboost
 4 | 
 5 | def save_model(model: xgboost.sklearn.XGBClassifier, model_path: str) -> None:
 6 |     """
 7 |     Guarda el modelo en un archivo usando joblib.
 8 | 
 9 |     Args:
10 |         model (xgboost.sklearn.XGBClassifier): Modelo a guardar.
11 |         model_path (str): Ruta donde guardar el modelo (sin extensión).
12 |     """
13 |     current_date = datetime.now().strftime("%Y-%m-%d")
14 |     full_path = f"{model_path}_{current_date}.joblib"
15 |     joblib.dump(model, full_path)
16 |     print(f"Modelo guardado en {full_path}")
17 | 

/src/model/trainer.py

 1 | import xgboost as xgb
 2 | import pandas as pd
 3 | 
 4 | def train_model(X_train: pd.DataFrame, y_train: pd.Series, params: dict = None) -> xgb.sklearn.XGBClassifier:
 5 |     """
 6 |     Función para entrenar el modelo
 7 | 
 8 |     Args:
 9 |         X_train (pd.DataFrame): Dataframe con las variables independientes
10 |         y_train (pd.Series): Serie con la variable objetivo
11 |         params (dict, optional): Diccionario con los parámetros del modelo. Defaults to None.
12 | 
13 |     Returns:
14 |         xgboost.sklearn.XGBClassifier: Modelo entrenado
15 |     """
16 |     if params is None:
17 |         params = {'objective': 'binary:logistic', 
18 |                   'eval_metric': 'logloss',
19 |                   'n_estimators': 300, 
20 |                   'max_depth': 2, 
21 |                   'learning_rate': 0.1, 
22 |                   'random_state': 42}
23 |     
24 |     model = xgb.XGBClassifier(**params)
25 |     model.fit(X_train, y_train)
26 |     
27 |     return model
28 | 
/src/model/trainer.py visualization