--- language: - es license: other library_name: scikit-learn tags: - tabular-classification - healthcare - diabetes - lightgbm - scikit-learn - predictive-modeling - shap - brfss datasets: - Jesusrodriguezf90/brfss2015-diabetes-detection metrics: - roc_auc - recall - f1 pipeline_tag: tabular-classification --- # LightGBM — Detección Temprana de Riesgo de Diabetes Modelo de clasificación binaria para **detección temprana de riesgo de diabetes** desarrollado como Trabajo de Fin de Máster (TFM) en el Máster en Data Science de KSchool (2025–2026). Pipeline sklearn completo: preprocesamiento determinista + ColumnTransformer + clasificador LightGBM, entrenado sobre 257.709 observaciones clínicas autorreportadas con 22 variables del cuestionario BRFSS 2015 (CDC). **Repositorio:** [TFM — GitHub](https://github.com/Jesusrodriguezf90/TFM) **Demo interactiva:** [diabetes-risk-demo — HF Spaces](https://huggingface.co/spaces/Jesusrodriguezf90/diabetes-risk-demo) --- ## Métricas Resultados de **validación cruzada estratificada** (5 folds) sobre el conjunto de entrenamiento: | Modelo | ROC-AUC (CV media) | PRC-AUC (CV media) | ROC-AUC (CV std) | |---|---|---|---| | Random Forest | 0.836 | 0.378 | 0.0018 | | XGBoost | 0.837 | 0.384 | 0.0022 | | **LightGBM ✓** | **0.839** | **0.391** | **0.0017** | Resultados sobre el **conjunto de test** (threshold=0.4733, F2-score óptimo en validación): | Métrica | Valor | |---|---| | ROC-AUC | **0.839** | | PRC-AUC | **0.391** | | Recall | **0.813** | | Precision | 0.256 | > LightGBM se seleccionó como modelo final por ofrecer el mejor equilibrio entre ROC-AUC, > PRC-AUC y recall en un contexto clínico desbalanceado. El threshold de 0.4733 maximiza > el F2-score en validación (β=2), priorizando el recall sobre la precisión — en cribado > clínico un falso negativo tiene un coste sanitario mucho mayor que un falso positivo. ### Matriz de confusión (test, threshold=0.4733) | | Predicho: No riesgo | Predicho: Riesgo | |---|---|---| | **Real: No riesgo** | 32.444 (TN) | 13.420 (FP) | | **Real: Riesgo** | 1.063 (FN) | 4.615 (TP) | --- ## Uso ```python import sys import types import numpy as np import pandas as pd import joblib from huggingface_hub import hf_hub_download # 1. Registrar el módulo src — necesario para deserializar el pipeline # El pkl fue serializado con src.preprocessing.preprocessing_pipeline BINARY_VARS = [ "BPMEDS", "BLOODCHO", "HAVARTH3", "QLACTLM2", "USEEQUIP", "BLIND", "DECIDE", "DIFFWALK", "DIFFALON", "DIFFDRES", "SMOKE100", "ADDEPEV2", "SEX", ] CATEGORICAL_NOMINAL = ["BPHIGH4", "_RACE"] def boosting_deterministic_preproc(X_input): X = X_input.copy() X = X.replace(-1, np.nan) for col in BINARY_VARS: if col in X.columns: X[col] = (X[col] == 1).astype(int) for col in CATEGORICAL_NOMINAL: if col in X.columns: X[col] = X[col].astype("category") return X def cap_outliers_numeric(X_input, numeric_vars): X = X_input.copy() for col in numeric_vars: low = np.nanpercentile(X[col], 1) high = np.nanpercentile(X[col], 99) X[col] = X[col].clip(low, high) return X src_mod = types.ModuleType("src") preprocessing_mod = types.ModuleType("src.preprocessing") pipeline_mod = types.ModuleType("src.preprocessing.preprocessing_pipeline") pipeline_mod.boosting_deterministic_preproc = boosting_deterministic_preproc pipeline_mod.cap_outliers_numeric = cap_outliers_numeric src_mod.preprocessing = preprocessing_mod preprocessing_mod.preprocessing_pipeline = pipeline_mod sys.modules.setdefault("src", src_mod) sys.modules.setdefault("src.preprocessing", preprocessing_mod) sys.modules.setdefault("src.preprocessing.preprocessing_pipeline", pipeline_mod) # 2. Descargar y cargar el pipeline ruta = hf_hub_download( repo_id="Jesusrodriguezf90/lgbm-diabetes-early-detection", filename="lgbm_diabetes_pipeline.pkl" ) pipeline = joblib.load(ruta) # 3. Preparar datos de entrada # X debe ser un DataFrame con las 22 variables del BRFSS 2015 # en su codificación original (valores enteros según el codebook del CDC) X = pd.DataFrame([{ "GENHLTH": 3, "BPHIGH4": 3, "BPMEDS": 2, "BLOODCHO": 1, "HAVARTH3": 2, "ADDEPEV2": 2, "SEX": 1, "QLACTLM2": 2, "USEEQUIP": 2, "BLIND": 2, "DECIDE": 2, "DIFFWALK": 2, "DIFFDRES": 2, "DIFFALON": 2, "SMOKE100": 2, "EXEROFT1": 3.0, "_RACE": 1, "_AGEG5YR": 7, "_BMI5CAT": 2, "_FRUTSUM": 1.0, "_VEGESUM": 2.0, "_PACAT1": 2, }]) # 4. Inferencia con threshold dinámico proba = pipeline.predict_proba(X)[0][1] threshold = 0.4733 # threshold por defecto — ajustable según contexto asistencial decision = "Realizar prueba HbA1c" if proba >= threshold else "No realizar prueba HbA1c" print(f"Probabilidad estimada de riesgo: {proba * 100:.1f}%") print(f"Decisión (threshold={threshold}): {decision}") ``` --- ## Arquitectura y entrenamiento ### Pipeline sklearn El pipeline encapsula todas las transformaciones para garantizar ausencia de data leakage: | Step | Componente | Descripción | |---|---|---| | 1 | `FunctionTransformer` | Preprocesamiento determinista: reemplaza -1 por NaN, normaliza binarias a 0/1, fuerza tipo `category` en nominales | | 2 | `ColumnTransformer` | Imputación (moda / mediana) + capping de outliers (P1-P99) + encoding nativo para boosting | | 3 | `LGBMClassifier` | Clasificador final con codificación nativa de categóricas | ### Selección de threshold El threshold óptimo se determina maximizando el **F2-score** (β=2) sobre la curva Precision–Recall del conjunto de validación. Con β=2, el recall tiene cuatro veces más peso que la precisión — refleja el coste clínico asimétrico donde un falso negativo (diabetes no detectada) genera un coste estimado de ~2.817€/paciente/año, frente a ~30€ de una prueba HbA1c innecesaria. El pipeline soporta **threshold dinámico** (rango 0.10–0.90) para que el profesional sanitario pueda ajustar la sensibilidad según la capacidad asistencial disponible. ### Parámetros de entrenamiento | Parámetro | Valor | |---|---| | Dataset | BRFSS 2015 (CDC) | | Observaciones | 257.709 | | Variables de entrada | 22 (clínicas y demográficas) | | Split | 60% train / 20% val / 20% test — estratificado | | Tamaño conjunto test | 51.542 individuos | | Validación cruzada | 5 folds estratificados sobre train | | Desbalance clase positiva | Tratado con `class_weight` en LightGBM | | Threshold por defecto | 0.4733 (F2-score óptimo en validación) | | random_state | 42 | | Hardware | Google Colab (CPU) | --- ## Interpretabilidad El modelo incluye análisis de interpretabilidad con **Feature Importance** y **SHAP** (SHapley Additive exPlanations), que permite explicar tanto el comportamiento global del modelo como predicciones individuales. Las variables con mayor influencia sobre el riesgo de diabetes identificadas por el modelo: | Variable | Importancia | Interpretación clínica | |---|---|---| | `_VEGESUM` | 1143 | Total porciones de vegetales diarias | | `_FRUTSUM` | 917 | Total porciones de frutas diarias | | `_AGEG5YR` | 712 | Edad en grupos de 5 años | | `EXEROFT1` | 617 | Frecuencia de ejercicio semanal | | `GENHLTH` | 477 | Estado general de salud autorreportado | | `_BMI5CAT` | 358 | Categoría de IMC | La demo interactiva incluye un **gráfico SHAP waterfall individual** que explica en tiempo real qué variables aumentaron o redujeron el riesgo para cada predicción concreta. --- ## Limitaciones - Entrenado sobre datos del BRFSS 2015 (población estadounidense) — puede no generalizar directamente a otras poblaciones sin recalibración - Precision baja (0.256) con threshold=0.4733 — diseño deliberado para maximizar recall en cribado; genera falsos positivos asumibles dado el bajo coste de la prueba HbA1c confirmatoria - Las 22 variables son autorreportadas — sujetas a sesgo de respuesta del encuestado - El modelo es un apoyo al cribado y no reemplaza el diagnóstico médico; la decisión clínica final corresponde siempre al profesional sanitario - Sin datos de seguimiento longitudinal — no predice progresión ni complicaciones --- ## Dataset **BRFSS 2015 — Behavioral Risk Factor Surveillance System** Centers for Disease Control and Prevention (CDC), Estados Unidos 🔗 [CDC BRFSS 2015](https://www.cdc.gov/brfss/annual_data/annual_2015.html) - 257.709 observaciones clínicas autorreportadas - 22 variables de entrada (hábitos de vida, condiciones médicas, datos demográficos) - Variable objetivo: `DIABETE3` — riesgo de diabetes (binaria: 1=sí, 3=no en codificación original CDC) - Dataset procesado disponible en: [Jesusrodriguezf90/brfss2015-diabetes-detection](https://huggingface.co/datasets/Jesusrodriguezf90/brfss2015-diabetes-detection) --- ## Autor **Jesús Rodríguez Fernández** — Data Scientist 🔗 [LinkedIn](https://linkedin.com/in/jesus-rf) · [GitHub](https://github.com/Jesusrodriguezf90) · [HF Profile](https://huggingface.co/Jesusrodriguezf90) --- ## Licencia Uso no comercial. Dataset BRFSS 2015 de dominio público (CDC). Ver términos de uso del CDC para redistribución de datos derivados.