Spaces:
Sleeping
Sleeping
| import importlib.util | |
| import os | |
| import sys | |
| from pathlib import Path | |
| PROJECT_ROOT = Path(__file__).resolve().parent.parent | |
| VENV_PYTHON = PROJECT_ROOT / 'env_academia' / 'bin' / 'python' | |
| if ( | |
| (importlib.util.find_spec('seaborn') is None or importlib.util.find_spec('shap') is None) | |
| and VENV_PYTHON.exists() | |
| and Path(sys.executable).resolve() != VENV_PYTHON.resolve() | |
| ): | |
| os.execv(str(VENV_PYTHON), [str(VENV_PYTHON), str(Path(__file__).resolve()), *sys.argv[1:]]) | |
| import pandas as pd | |
| import joblib | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| from sklearn.metrics import confusion_matrix, roc_curve, auc | |
| from sklearn.model_selection import train_test_split | |
| import shap | |
| # 1. Configuración de rutas (Ajustadas a tu árbol actual) | |
| MODEL_PATH = 'modelos/modelo_rf_optimizado.pkl' | |
| DATA_PATH = 'datos_procesados/dataset_preparado_ml.csv' | |
| OUTPUT_DIR = 'outputs/charts' | |
| os.makedirs(OUTPUT_DIR, exist_ok=True) | |
| print("📦 Cargando modelo y datos preparados...") | |
| model = joblib.load(MODEL_PATH) | |
| df = pd.read_csv(DATA_PATH) | |
| # 2. Separar características (X) y variable objetivo (y) | |
| target_column = 'churn' if 'churn' in df.columns else 'abandono' | |
| if target_column not in df.columns: | |
| raise KeyError("No se encontró la columna objetivo 'churn' ni 'abandono' en el dataset preparado") | |
| X = df.drop(columns=[target_column, 'estudiante_id'], errors='ignore') | |
| y = df[target_column] | |
| # 3. REPLICAR LA DIVISIÓN DEL NOTEBOOK | |
| # ¡Crucial! Usa el mismo test_size y random_state que usaste en app.ipynb | |
| X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) | |
| print(f"📊 Evaluando sobre {len(X_test)} registros de validación...") | |
| # Predicciones sobre el conjunto de test | |
| y_pred = model.predict(X_test) | |
| y_prob = model.predict_proba(X_test)[:, 1] | |
| print("🎨 Generando representaciones gráficas...") | |
| # ----------------------------------------------------------------- | |
| # GRÁFICO 1: MATRIZ DE CONFUSIÓN | |
| # ----------------------------------------------------------------- | |
| plt.figure(figsize=(6, 5)) | |
| cm = confusion_matrix(y_test, y_pred) | |
| sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', | |
| xticklabels=['No Abandona', 'Abandona'], | |
| yticklabels=['No Abandona', 'Abandona']) | |
| plt.title('Matriz de Confusión - Predicción de Abandono') | |
| plt.ylabel('Clase Real') | |
| plt.xlabel('Clase Predicha') | |
| plt.tight_layout() | |
| plt.savefig(os.path.join(OUTPUT_DIR, 'matriz_confusion.png'), dpi=300) | |
| plt.close() | |
| print("✅ Matriz de confusión guardada.") | |
| # ----------------------------------------------------------------- | |
| # GRÁFICO 2: CURVA ROC | |
| # ----------------------------------------------------------------- | |
| fpr, tpr, _ = roc_curve(y_test, y_prob) | |
| roc_auc = auc(fpr, tpr) | |
| plt.figure(figsize=(6, 5)) | |
| plt.plot(fpr, tpr, color='darkorange', lw=2, label=f'Curva ROC (AUC = {roc_auc:.4f})') | |
| 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('Tasa de Falsos Positivos (FPR)') | |
| plt.ylabel('Tasa de Verdaderos Positivos (TPR)') | |
| plt.title('Curva ROC (Receiver Operating Characteristic)') | |
| plt.legend(loc="lower right") | |
| plt.grid(True, linestyle='--', alpha=0.6) | |
| plt.tight_layout() | |
| plt.savefig(os.path.join(OUTPUT_DIR, 'curva_roc.png'), dpi=300) | |
| plt.close() | |
| print("✅ Curva ROC guardada.") | |
| # ----------------------------------------------------------------- | |
| # GRÁFICO 3: IMPORTANCIA DE VARIABLES (SHAP VALUES) | |
| # ----------------------------------------------------------------- | |
| # SHAP puede ser computacionalmente pesado, usamos el X_test | |
| explainer = shap.TreeExplainer(model) | |
| shap_values = explainer.shap_values(X_test) | |
| plt.figure(figsize=(8, 5)) | |
| if isinstance(shap_values, list): | |
| shap_vals_to_plot = shap_values[1] | |
| else: | |
| shap_vals_to_plot = shap_values | |
| shap.summary_plot(shap_vals_to_plot, X_test, plot_type="bar", show=False) | |
| plt.title('Importancia Global de las Variables (SHAP)', fontsize=14, pad=15) | |
| plt.tight_layout() | |
| plt.savefig(os.path.join(OUTPUT_DIR, 'importancia_shap.png'), dpi=300) | |
| plt.close() | |
| print("✅ Gráfico de importancia SHAP guardado.") | |
| print(f"\n🎉 ¡Proceso completado! Archivos listos en: '{OUTPUT_DIR}/'") |