| |
|
|
| import os |
| import joblib |
| import numpy as np |
| import pandas as pd |
| from dotenv import load_dotenv |
|
|
| from sklearn.model_selection import train_test_split |
| from sklearn.preprocessing import OneHotEncoder |
| from sklearn.compose import ColumnTransformer |
| from sklearn.pipeline import Pipeline |
| from sklearn.feature_extraction.text import TfidfVectorizer |
| from sklearn.linear_model import LogisticRegression |
| from sklearn.metrics import classification_report, confusion_matrix |
|
|
| |
| |
| |
| |
| load_dotenv() |
|
|
| |
| ruta_base = os.getenv("PROYECTO_RUTA_BASE") |
|
|
| |
| if not ruta_base: |
| raise ValueError("La variable 'PROYECTO_RUTA_BASE' no se encontró en el archivo .env.") |
|
|
| print(f"Ruta base del proyecto cargada desde .env: {ruta_base}") |
|
|
| |
| CARPETA_PROCESADO = os.path.join(ruta_base, "PROCESADO") |
| ARCHIVO_CSV = os.path.join(CARPETA_PROCESADO, "reporte_mesa_ayuda_COMBINADO.csv") |
| ARCHIVO_PIPELINE = os.path.join(CARPETA_PROCESADO, "modelo_prioridad_pipeline.joblib") |
|
|
| |
| |
| |
| print("📥 Cargando datos desde:", ARCHIVO_CSV) |
| df = pd.read_csv(ARCHIVO_CSV) |
|
|
| |
| cat_cols = ['Area_Solicitante', 'Grupo_Asignado', 'Categoria'] |
| text_col = 'Asunto' if 'Asunto' in df.columns else None |
|
|
| |
| cols_necesarias = cat_cols + ( [text_col] if text_col else [] ) + ['Prioridad'] |
| faltantes = [c for c in cols_necesarias if c not in df.columns] |
| if faltantes: |
| raise ValueError(f"Faltan columnas en el CSV para entrenar: {faltantes}") |
|
|
| |
| df = df.dropna(subset=['Prioridad'] + cat_cols) |
| for c in cat_cols + ( [text_col] if text_col else [] ): |
| if df[c].dtype == 'object': |
| df[c] = df[c].astype(str).str.strip() |
|
|
| print(f"✅ Dataset tras limpieza: {len(df)} filas, {len(df.columns)} columnas.") |
|
|
| X = df[cat_cols + ([text_col] if text_col else [])] |
| y = df['Prioridad'].astype(str).str.strip() |
|
|
| |
| |
| |
| |
| cat_transformer = OneHotEncoder(drop=None, handle_unknown='ignore', sparse_output=True) |
|
|
| |
| transformers = [ |
| ("cat", cat_transformer, cat_cols) |
| ] |
|
|
| if text_col: |
| transformers.append( |
| ("txt", TfidfVectorizer(min_df=2, ngram_range=(1, 2)), text_col) |
| ) |
|
|
| preprocess = ColumnTransformer(transformers) |
|
|
| |
| clf = LogisticRegression( |
| max_iter=2000, |
| class_weight='balanced', |
| solver='liblinear' |
| ) |
|
|
| |
| pipe = Pipeline([ |
| ("pre", preprocess), |
| ("clf", clf) |
| ]) |
|
|
| |
| |
| |
| print("📊 Haciendo split estratificado train/test...") |
| X_train, X_test, y_train, y_test = train_test_split( |
| X, y, test_size=0.2, random_state=42, stratify=y |
| ) |
|
|
| print("🏋️ Entrenando pipeline...") |
| pipe.fit(X_train, y_train) |
|
|
| print("\n🧪 Evaluación en test:") |
| y_pred = pipe.predict(X_test) |
| print(classification_report(y_test, y_pred, digits=4)) |
|
|
| cm = confusion_matrix(y_test, y_pred, labels=sorted(y.unique())) |
| cm_df = pd.DataFrame(cm, index=[f"real_{c}" for c in sorted(y.unique())], |
| columns=[f"pred_{c}" for c in sorted(y.unique())]) |
| print("\nMatriz de confusión:") |
| print(cm_df) |
|
|
| |
| |
| |
| joblib.dump(pipe, ARCHIVO_PIPELINE) |
| print(f"\n💾 Pipeline guardado en:\n{ARCHIVO_PIPELINE}") |
|
|
| |
| |
| |
| def mostrar_pred(nombre, fila_dict): |
| X_in = pd.DataFrame([fila_dict]) |
| proba = pipe.predict_proba(X_in)[0] |
| clases = pipe.classes_ |
| pred = pipe.predict(X_in)[0] |
|
|
| print(f"\n🔎 {nombre}") |
| print(f"🎯 Predicción: {pred}") |
| print("📊 Probabilidades por clase:") |
| for cls, p in sorted(zip(clases, proba), key=lambda x: x[0]): |
| print(f" {cls}: {p:.2f}") |
|
|
| ejemplos = [ |
| ("Ticket Crítico (esperable Alta)", { |
| 'Area_Solicitante': 'TI', |
| 'Grupo_Asignado': 'SGD', |
| 'Categoria': 'Incidente', |
| **({'Asunto': 'No se puede iniciar sesión, error crítico de autenticación'} if text_col else {}) |
| }), |
| ("Ticket Normal (esperable Media)", { |
| 'Area_Solicitante': 'Operaciones', |
| 'Grupo_Asignado': 'APLICATIVO', |
| 'Categoria': 'Requerimiento', |
| **({'Asunto': 'Solicito acceso a nuevo módulo para el equipo'} if text_col else {}) |
| }), |
| ("Ticket Trivial (esperable Baja)", { |
| 'Area_Solicitante': 'Legal', |
| 'Grupo_Asignado': 'ECASILLA', |
| 'Categoria': 'Orientación', |
| **({'Asunto': '¿Dónde encuentro el manual y cómo generar reporte?'} if text_col else {}) |
| }), |
| ] |
|
|
| print("\n================= SANITY CHECK =================") |
| for nombre, fila in ejemplos: |
| mostrar_pred(nombre, fila) |
| print("================================================") |
|
|