1. Procesamiento de Datos e Ingeniería de Características
El flujo de trabajo comienza con la preparación de un dataset clínico sobre cáncer de pulmón. Esta etapa es crítica para asegurar que los modelos de aprednizaje automático reciban información estructurada y libre de ruido.
import pandas as pd
import numpy as np
from sklearn.preprocessing import LabelEncoder, StandardScaler
# Carga del set de datos
df_pulmon = pd.read_csv("lung_cancer_data.csv")
# Limpieza inicial: eliminación de registros duplicados
df_pulmon.drop_duplicates(inplace=True)
# Transformación de variables categóricas
codificador = LabelEncoder()
df_pulmon["LUNG_CANCER"] = codificador.fit_transform(df_pulmon["LUNG_CANCER"])
# Codificación One-Hot para el género y limpieza de nombres de columnas
df_pulmon = pd.get_dummies(df_pulmon, columns=["GENDER"], prefix="GEN")
df_pulmon.rename(columns={
"GEN_M": "MASCULINO",
"GEN_F": "FEMENINO",
"YELLOW_FINGERS": "FUMADOR_DEDOS_AMARILLOS",
"PEER_PRESSURE": "PRESION_SOCIAL"
}, inplace=True)
# Separación de variables independientes (X) y objetivo (y)
X_clinico = df_pulmon.drop("LUNG_CANCER", axis=1)
y_objetivo = df_pulmon["LUNG_CANCER"]
2. Estrategias de Selección de Variables
Para mejorar la eficiencia del modelo y evitar el sobreajuste, se implementan dos métodos complementarios: la regularización Lasso (L1) y la Eliminación Recursiva de Características (RFE).
Selección mediante Lasso
from sklearn.linear_model import LassoCV
# Normalización de datos para Lasso
normalizador = StandardScaler()
X_escalado = normalizador.fit_transform(X_clinico)
# Optimización de alpha mediante validación cruzada
modelo_lasso = LassoCV(cv=5, random_state=101).fit(X_escalado, y_objetivo)
variables_lasso = X_clinico.columns[np.abs(modelo_lasso.coef_) > 0]
print(f"Variables seleccionadas por Lasso: {list(variables_lasso)}")
Eliminación Recursiva (RFE)
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier
# Uso de Random Forest como estimador base
clasificador_base = RandomForestClassifier(n_estimators=50, random_state=101)
selector_rfe = RFE(estimator=clasificador_base, n_features_to_select=10)
selector_rfe.fit(X_escalado, y_objetivo)
variables_rfe = X_clinico.columns[selector_rfe.support_]
print(f"Variables seleccionadas por RFE: {list(variables_rfe)}")
3. Implementación de Modelos de Clasificación
Se evalúa una amplia gama de algoritmos, desde modelos lineales tradicionales hasta redes neuronales profundas, para identificar la arquitectura óptima para la predicción del riesgo.
from sklearn.model_selection import train_test_split
from xgboost import XGBClassifier
import tensorflow as tf
# Definición de variables comunes tras la selección
vars_finales = list(set(variables_lasso).intersection(set(variables_rfe)))
X_final = X_clinico[vars_finales]
X_entrena, X_prueba, y_entrena, y_prueba = train_test_split(
X_final, y_objetivo, test_size=0.25, random_state=42
)
# Ejemplo: Configuración de una Red Neuronal Densa
red_neuronal = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation='relu', input_shape=(X_entrena.shape[1],)),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])
red_neuronal.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
entrenamiento = red_neuronal.fit(X_entrena, y_entrena, epochs=100, batch_size=32, verbose=0)
4. Evaluación Clínica Avanzada: DCA e Indicadores de Mejora
Más allá de la precisión (Acuracy), en el ámbito médico es vital analizar el beneficio neto mediante el Análisis de Curva de Decisión (DCA) y la capacidad de reclasificación (NRI e IDI).
def calcular_nri(prob_m1, prob_m2, y_real, umbral=0.5):
preds_m1 = (prob_m1 >= umbral).astype(int)
preds_m2 = (prob_m2 >= umbral).astype(int)
# Mejora en eventos (positivos)
nri_pos = (np.sum((preds_m2 > preds_m1) & (y_real == 1)) -
np.sum((preds_m2 < preds_m1) & (y_real == 1))) / np.sum(y_real == 1)
# Mejora en no-eventos (negativos)
nri_neg = (np.sum((preds_m2 < preds_m1) & (y_real == 0)) -
np.sum((preds_m2 > preds_m1) & (y_real == 0))) / np.sum(y_real == 0)
return nri_pos + nri_neg
# Comparación entre XGBoost y Regresión Logística
# nri_final = calcular_nri(probabilidades_logistica, probabilidades_xgb, y_prueba)
5. Análisis de Explicabilidad con SHAP
Para "abrir la caja negra" de los modelos complejos como XGBoost, se utiliza SHAP (SHapley Additive exPlanations), permitiendo cuantificar la contribución exacta de cada síntoma al diagnóstico final.
import shap
# Inicialización del explicador para modelos basados en árboles
explicador = shap.TreeExplainer(modelo_xgb_entrenado)
valores_shap = explicador.shap_values(X_prueba)
# Visualización del impacto de las características (Summary Plot)
shap.summary_plot(valores_shap, X_prueba, plot_type="dot")
# Gráfico de fuerza para un paciente específico
# Analiza por qué el modelo tomó una decisión particular para el individuo 'i'
shap.force_plot(explicador.expected_value, valores_shap[0,:], X_prueba.iloc[0,:], matplotlib=True)
El uso de SHAP no solo valida el modelo frente al conocimiento médico existente, sino que también revela interacciones no lineales entre variables, como la relación entre la edad y enfermedades crónicas concurrentes en el desarrollo del cáncer.