{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "e46f1a08",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: uv in /Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages (0.11.10)\n",
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.3\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.1.1\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n",
      "\u001b[2mUsing Python 3.13.11 environment at: /Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv\u001b[0m\n",
      "\u001b[2mChecked \u001b[1m1 package\u001b[0m \u001b[2min 14ms\u001b[0m\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n",
      "\u001b[2mUsing Python 3.13.11 environment at: /Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv\u001b[0m\n",
      "\u001b[2mChecked \u001b[1m1 package\u001b[0m \u001b[2min 1ms\u001b[0m\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n",
      "\u001b[2mUsing Python 3.13.11 environment at: /Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv\u001b[0m\n",
      "\u001b[2mChecked \u001b[1m1 package\u001b[0m \u001b[2min 3ms\u001b[0m\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n",
      "\u001b[2mUsing Python 3.13.11 environment at: /Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv\u001b[0m\n",
      "\u001b[2mChecked \u001b[1m1 package\u001b[0m \u001b[2min 1ms\u001b[0m\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n",
      "\u001b[2mUsing Python 3.13.11 environment at: /Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv\u001b[0m\n",
      "\u001b[2mChecked \u001b[1m1 package\u001b[0m \u001b[2min 2ms\u001b[0m\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install uv\n",
    "%uv pip install pandas\n",
    "%uv pip install numpy\n",
    "%uv pip install scikit-learn\n",
    "%uv pip install joblib\n",
    "%uv pip install plotly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "cd7de07f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import json\n",
    "import os\n",
    "import joblib\n",
    "\n",
    "from sklearn.linear_model import LinearRegression, LogisticRegression\n",
    "from sklearn.model_selection import train_test_split, cross_val_score\n",
    "from sklearn.metrics import (\n",
    "    mean_absolute_error, mean_squared_error, r2_score,\n",
    "    accuracy_score, classification_report, roc_auc_score,\n",
    "    confusion_matrix, roc_curve,\n",
    ")\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.pipeline import Pipeline\n",
    "\n",
    "import plotly.graph_objects as go\n",
    "import plotly.express as px\n",
    "from plotly.subplots import make_subplots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "a9643e6c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataset: 500 estudiantes, 146 columnas\n",
      "Columnas: ['student_id', 'gender', 'age', 'country', 'city', 'school_name', 'course', 'academic_year', 'class_group', 'homeroom_teacher'] ...\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>student_id</th>\n",
       "      <th>gender</th>\n",
       "      <th>age</th>\n",
       "      <th>country</th>\n",
       "      <th>city</th>\n",
       "      <th>school_name</th>\n",
       "      <th>course</th>\n",
       "      <th>academic_year</th>\n",
       "      <th>class_group</th>\n",
       "      <th>homeroom_teacher</th>\n",
       "      <th>...</th>\n",
       "      <th>failed_subjects</th>\n",
       "      <th>passed_rate</th>\n",
       "      <th>passed_rate_percent</th>\n",
       "      <th>final_grade_std</th>\n",
       "      <th>total_subjects</th>\n",
       "      <th>grade_tendency</th>\n",
       "      <th>sustained_decrease</th>\n",
       "      <th>sustained_increase</th>\n",
       "      <th>support_level_num</th>\n",
       "      <th>notes</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>EA00001</td>\n",
       "      <td>F</td>\n",
       "      <td>16</td>\n",
       "      <td>Tanzania</td>\n",
       "      <td>Dodoma</td>\n",
       "      <td>Kilimanjaro Digital School</td>\n",
       "      <td>Secondary 5</td>\n",
       "      <td>2025-2026</td>\n",
       "      <td>C</td>\n",
       "      <td>Sarah Kamau</td>\n",
       "      <td>...</td>\n",
       "      <td>3</td>\n",
       "      <td>0.823529</td>\n",
       "      <td>82.353</td>\n",
       "      <td>7.044256</td>\n",
       "      <td>17</td>\n",
       "      <td>Uptrend</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>2</td>\n",
       "      <td>Improving steadily throughout the year</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>EA00002</td>\n",
       "      <td>M</td>\n",
       "      <td>15</td>\n",
       "      <td>Kenia</td>\n",
       "      <td>Mombasa</td>\n",
       "      <td>Rift Valley High</td>\n",
       "      <td>Secondary 3</td>\n",
       "      <td>2025-2026</td>\n",
       "      <td>A</td>\n",
       "      <td>Faith Okello</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>100.000</td>\n",
       "      <td>5.956869</td>\n",
       "      <td>16</td>\n",
       "      <td>Uptrend</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>1</td>\n",
       "      <td>Good progress in digital activities</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>EA00003</td>\n",
       "      <td>M</td>\n",
       "      <td>19</td>\n",
       "      <td>Burundi</td>\n",
       "      <td>Ruyigi</td>\n",
       "      <td>Sunrise Learning School</td>\n",
       "      <td>Secondary 6</td>\n",
       "      <td>2025-2026</td>\n",
       "      <td>B</td>\n",
       "      <td>Patrick Bizimana</td>\n",
       "      <td>...</td>\n",
       "      <td>1</td>\n",
       "      <td>0.941176</td>\n",
       "      <td>94.118</td>\n",
       "      <td>8.381247</td>\n",
       "      <td>17</td>\n",
       "      <td>Uptrend</td>\n",
       "      <td>False</td>\n",
       "      <td>False</td>\n",
       "      <td>2</td>\n",
       "      <td>Irregular attendance affects performance</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>3 rows × 146 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "  student_id gender  age   country     city                 school_name  \\\n",
       "0    EA00001      F   16  Tanzania   Dodoma  Kilimanjaro Digital School   \n",
       "1    EA00002      M   15     Kenia  Mombasa            Rift Valley High   \n",
       "2    EA00003      M   19   Burundi   Ruyigi     Sunrise Learning School   \n",
       "\n",
       "        course academic_year class_group  homeroom_teacher  ...  \\\n",
       "0  Secondary 5     2025-2026           C       Sarah Kamau  ...   \n",
       "1  Secondary 3     2025-2026           A      Faith Okello  ...   \n",
       "2  Secondary 6     2025-2026           B  Patrick Bizimana  ...   \n",
       "\n",
       "   failed_subjects passed_rate passed_rate_percent  final_grade_std  \\\n",
       "0                3    0.823529              82.353         7.044256   \n",
       "1                0    1.000000             100.000         5.956869   \n",
       "2                1    0.941176              94.118         8.381247   \n",
       "\n",
       "   total_subjects  grade_tendency  sustained_decrease sustained_increase  \\\n",
       "0              17         Uptrend               False              False   \n",
       "1              16         Uptrend               False              False   \n",
       "2              17         Uptrend               False              False   \n",
       "\n",
       "  support_level_num                                     notes  \n",
       "0                 2    Improving steadily throughout the year  \n",
       "1                 1       Good progress in digital activities  \n",
       "2                 2  Irregular attendance affects performance  \n",
       "\n",
       "[3 rows x 146 columns]"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = pd.read_csv(\"../datasets/clean_dataset.csv\")\n",
    "\n",
    "print(f'Dataset: {df.shape[0]} estudiantes, {df.shape[1]} columnas')\n",
    "print(f'Columnas: {df.columns.tolist()[:10]} ...')\n",
    "\n",
    "df.head(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "8de13c29",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Features calculadas correctamente\n",
      "  gender_encoded       → {0: 263, 1: 237}\n",
      "  grade_tendency_enc   → {1: 361, 0: 139}\n",
      "  mathematics_pass     → {1: 411, 0: 89}\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/var/folders/tc/2x0h87ks0kvbt_p_yzf4kryr0000gp/T/ipykernel_24941/3786651449.py:34: PerformanceWarning: DataFrame is highly fragmented.  This is usually the result of calling `frame.insert` many times, which has poor performance.  Consider joining all columns at once using pd.concat(axis=1) instead. To get a de-fragmented frame, use `newframe = frame.copy()`\n",
      "  df['term3_avg'] = df[term_3_columns].mean(axis = 1)\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Preparación de Features Comunes\n",
    "\"\"\"\n",
    "\n",
    "term_1_columns = [c for c in df.columns if c.endswith('_term1')]\n",
    "term_2_columns = [c for c in df.columns if c.endswith('_term2')]\n",
    "term_3_columns = [c for c in df.columns if c.endswith('_term3')]\n",
    "\n",
    "# Encodings binarios\n",
    "\n",
    "df['gender_encoded']         = (df['gender'] == 'M').astype(int)\n",
    "\n",
    "df['grade_tendency_encoded'] = (df['grade_tendency'] == 'Uptrend').astype(int)\n",
    "\n",
    "df['mathematics_pass']       = (df['Mathematics_status'] == 'Pass').astype(int)\n",
    "\n",
    "# Curso → ordinal\n",
    "\n",
    "course_order = [\n",
    "    'Primary 6', 'Secondary 1', 'Secondary 2', 'Secondary 3',\n",
    "    'Secondary 4', 'Secondary 5', 'Secondary 6',\n",
    "]\n",
    "\n",
    "course_map = {c: i for i, c in enumerate(course_order)}\n",
    "\n",
    "df['course_encoded'] = df['course'].map(course_map)\n",
    "\n",
    "# Promedios por trimestre (ignorando NaN)\n",
    "\n",
    "df['term1_avg'] = df[term_1_columns].mean(axis = 1)\n",
    "\n",
    "df['term2_avg'] = df[term_2_columns].mean(axis = 1)\n",
    "\n",
    "df['term3_avg'] = df[term_3_columns].mean(axis = 1)\n",
    "\n",
    "print('Features calculadas correctamente')\n",
    "\n",
    "print(f'  gender_encoded       → {df[\"gender_encoded\"].value_counts().to_dict()}')\n",
    "\n",
    "print(f'  grade_tendency_enc   → {df[\"grade_tendency_encoded\"].value_counts().to_dict()}')\n",
    "\n",
    "print(f'  mathematics_pass     → {df[\"mathematics_pass\"].value_counts().to_dict()}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "8967a098",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "R²:           0.9228\n",
      "MAE:          0.9877\n",
      "RMSE:         1.2451\n",
      "Intercepto:   39.8974\n",
      "CV R² 5-fold: 0.9050 ± 0.0138\n",
      "\n",
      "Coeficientes (impacto en avg_final por unidad de feature):\n",
      "term1_avg                    0.347438\n",
      "total_subjects               0.282038\n",
      "attendance_absences_total   -0.003121\n",
      "gender_encoded              -0.012608\n",
      "age                         -0.023109\n",
      "support_level_num           -0.023556\n",
      "course_encoded              -1.200219\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Modelo 1 — Regresión Lineal: Predicción de avg_final\n",
    "\n",
    "Predice el promedio de notas finales usando únicamente\n",
    "datos del primer trimestre + demografía + asistencia.\n",
    "Simula una predicción temprana al inicio del curso.\n",
    "\"\"\"\n",
    "\n",
    "FEATURES_M1 = [\n",
    "    'age',\n",
    "    'gender_encoded',\n",
    "    'course_encoded',\n",
    "    'attendance_absences_total',\n",
    "    'support_level_num',\n",
    "    'total_subjects',\n",
    "    'term1_avg',\n",
    "]\n",
    "\n",
    "TARGET_M1 = 'avg_final'\n",
    "\n",
    "X_m1 = df[FEATURES_M1]\n",
    "y_m1 = df[TARGET_M1]\n",
    "\n",
    "X_train_m1, X_test_m1, y_train_m1, y_test_m1 = train_test_split(\n",
    "    X_m1, y_m1, test_size = 0.2, random_state = 42\n",
    ")\n",
    "\n",
    "model_m1 = LinearRegression()\n",
    "model_m1.fit(X_train_m1, y_train_m1)\n",
    "\n",
    "y_pred_m1    = model_m1.predict(X_test_m1)\n",
    "\n",
    "cv_scores_m1 = cross_val_score(model_m1, X_m1, y_m1, cv = 5, scoring = 'r2')\n",
    "\n",
    "r2_m1   = r2_score(y_test_m1, y_pred_m1)\n",
    "mae_m1  = mean_absolute_error(y_test_m1, y_pred_m1)\n",
    "rmse_m1 = np.sqrt(mean_squared_error(y_test_m1, y_pred_m1))\n",
    "\n",
    "# Coeficientes e intercepto\n",
    "\n",
    "coef_m1 = pd.Series(model_m1.coef_, index = FEATURES_M1).sort_values(ascending = False)\n",
    "\n",
    "print(f'R²:           {r2_m1:.4f}')\n",
    "print(f'MAE:          {mae_m1:.4f}')\n",
    "print(f'RMSE:         {rmse_m1:.4f}')\n",
    "print(f'Intercepto:   {model_m1.intercept_:.4f}')\n",
    "print(f'CV R² 5-fold: {cv_scores_m1.mean():.4f} ± {cv_scores_m1.std():.4f}')\n",
    "\n",
    "print('\\nCoeficientes (impacto en avg_final por unidad de feature):')\n",
    "\n",
    "print(coef_m1.to_string())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "fbf210e8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Top 10 estudiantes con mayor error de predicción (avg_final):\n",
      "student_id   real  predicho  error_abs  error_relativo\n",
      "   EA00174 69.650    66.122      3.528            5.06\n",
      "   EA00432 68.800    65.770      3.030            4.40\n",
      "   EA00452 68.619    65.791      2.828            4.12\n",
      "   EA00085 69.076    66.631      2.445            3.54\n",
      "   EA00492 61.322    58.946      2.376            3.87\n",
      "   EA00083 56.681    59.031      2.350            4.15\n",
      "   EA00173 69.900    67.591      2.309            3.30\n",
      "   EA00335 67.329    65.147      2.182            3.24\n",
      "   EA00375 55.894    58.057      2.163            3.87\n",
      "   EA00003 59.582    57.426      2.156            3.62\n",
      "\n",
      "Error medio absoluto en test: 0.988 puntos\n",
      "El 88.0% de predicciones tienen error < 2 puntos\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Insights — Modelo 1: Predicciones y estudiantes con mayor error\n",
    "\"\"\"\n",
    "\n",
    "# Tabla comparativa real vs predicho para el test set completo\n",
    "\n",
    "df_results_m1 = pd.DataFrame({\n",
    "    'student_id':    df.loc[X_test_m1.index, 'student_id'].values,\n",
    "    'real':          y_test_m1.values.round(3),\n",
    "    'predicho':      y_pred_m1.round(3),\n",
    "    'error_abs':     np.abs(y_test_m1.values - y_pred_m1).round(3),\n",
    "    'error_relativo': (np.abs(y_test_m1.values - y_pred_m1) / y_test_m1.values * 100).round(2),\n",
    "}).sort_values('error_abs', ascending = False)\n",
    "\n",
    "print('Top 10 estudiantes con mayor error de predicción (avg_final):')\n",
    "\n",
    "print(df_results_m1.head(10).to_string(index = False))\n",
    "\n",
    "print(f'\\nError medio absoluto en test: {df_results_m1[\"error_abs\"].mean():.3f} puntos')\n",
    "\n",
    "print(f'El {(df_results_m1[\"error_abs\"] < 2).mean()*100:.1f}% de predicciones tienen error < 2 puntos')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "91641683",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "marker": {
          "color": "#4C9BE8",
          "opacity": 0.6
         },
         "mode": "markers",
         "name": "Predicciones",
         "type": "scatter",
         "x": {
          "bdata": "ZmZmZma2UEACK4cW2V5QQN9PjZdu8ktAVOOlm8SATUBGtvP91EhQQCuHFtnOD1FAVOOlm8QQUEB3vp8aL/1PQEa28/3UKE9ARIts5/uZUED8qfHSTYJNQNv5fmq8lFBAAAAAAADATUC+nxov3URRQBKDwMqhJU9AJQaBlUPrTEDNzMzMzCxRQLpJDAIr50tAWDm0yHaeTEDufD81XqpMQEjhehSu90xA8KfGSzepTkDVeOkmMVhNQBKDwMqhVVBAKVyPwvXwUEAAAAAAAOBMQGiR7Xw/RU1Ax0s3iUHoUEAj2/l+ahxMQJMYBFYO1VBAeekmMQjMUEA1XrpJDFJMQM3MzMzMbE5AmpmZmZlpUUDRItv5fspNQJzEILByOFBAmG4Sg8A6UEDfT42XbvJPQGIQWDm0KE5AVOOlm8SATkBaZDvfT11MQAAAAAAAoE9ADi2yne9nTkAzMzMzM/tQQGZmZmZmdktA9P3UeOl2TkBiEFg5tOhLQKrx0k1iwFBAzczMzMwMTkAQWDm0yIZPQFYOLbKdn0tA30+Nl24yS0BGtvP91KhQQGDl0CLbgVFAd76fGi/FUEAAAAAAAMBQQBBYObTIhkxAFK5H4XqETEDl0CLb+RZQQNEi2/l+YlBAqvHSTWKIUEBiEFg5tChOQAisHFpkq01ADi2yne/nTUBCYOXQIttQQN0kBoGVw0xAVg4tsp0nUUBU46WbxGBOQIXrUbgeFU1A1XjpJjGwUEDn+6nx0n1NQJqZmZmZMVFAXrpJDAI7TkDLoUW28y1RQBKDwMqhxU1AEoPAyqFlT0BYObTIdr5KQESLbOf7EVBAmpmZmZmpUEBaZDvfT91PQPCnxks38VBAukkMAitXTEBCYOXQIttOQJqZmZmZeVFAj8L1KFwXUEACK4cW2R5PQArXo3A94lBAXrpJDAKjUEC6SQwCK8dMQJHtfD81Dk5AvHSTGASmUEDdJAaBlXNQQC2yne+n7lBAMzMzMzOzS0AzMzMzMzNRQFTjpZvEgFBAIbByaJGNT0Cq8dJNYiBRQPCnxks3SU5AnMQgsHL4UEA=",
          "dtype": "f8"
         },
         "xaxis": "x",
         "y": {
          "bdata": "9HM1Oif1UEDc+iBsSl1QQEOxKwREB01Aw0bsOA0STkCoMz/5KgxQQJ+tPp/4TFFApWkyrDPLT0C2YoD3CRVQQLi7rwvabE5AaWS0LShlUEAyz1lqKa5NQCOZTaHPk1BAHLRC4qEJTkBrdTkvaahQQAjd0Sk1gU9AIid7eokoTEC0sTefCgFRQEeBt7/bpkxAdiNxUBzzS0AKc9mSLKBLQO1GNIde4U1A04N3mA95TUBiZFc6YmxNQKZM5BzbUlBAn9EE7NuMUEDKHnF408FMQC57y+Fmf0xAoOlJ9fATUUDUAs0ROe5MQDbHPVlwSVBACdssBgDaUEDcndXqaUNMQIr01swUoU1AMDFS0NOHUECw3XCKiLZMQBY6YfbgXFBAmZwIiwf6T0AORdYTextPQBgCGbF3YU5Al03j1f8tT0DQw3nLFqNMQGp8yII/hU9AhncKAEzzTUAqVlW+8KNQQCY+LvChW0xAmof8KkWRTkCtd3wXDoJMQLnmtFHrz1BApJvUfVmdTUA6abmyZ49PQJlR/AZ9IUxAHsUeQXbdSkDcElZry7ZQQGoLMbcQN1FA0ouj8VARUUALd4t5RONQQDK1ZtwrgkxALBpsVgTjS0DAOvvLxVxQQMTLIinJBlBArPEZbuhdUEA8P6g1eB1OQIv8jx/oVE1AF0uW8znyTUDtsWv72HRQQODEXzyIgkxAZIiBSKJyUEAJWn2287NOQBTdOh4LpE1AFC3auMSgUEAnshA+4FNNQElbfaEh4VBA7XkUFaKlTkA4SNyRjAVRQHyoiQKDn01A9tXAUOiKT0DCOmFSSZRLQLatGOHgbk9ANsDUptLWUED0DWkEuFJQQADYIOoHxFBA/yUFWuqDTUAclHfESQpOQK7YqrLX5VBAbGa9Ni5FUEDGyC/YVvFOQL/AlfLcZFBAjoAjDnWKUEB6f/Lw4JVMQBWgCdesY05AyjKlVk2zUEBUhdunyHpQQE5RXwS9yFBAvDE3rkCyS0AWqlUuSnFQQN8jH0qBi1BAGpXCs1HeT0CFumbVfZpQQKS9FCo3YU5Aos7rbOyrUEA=",
          "dtype": "f8"
         },
         "yaxis": "y"
        },
        {
         "line": {
          "color": "#E74C3C",
          "dash": "dash"
         },
         "mode": "lines",
         "name": "Perfecto",
         "type": "scatter",
         "x": [
          53.488,
          70.029
         ],
         "xaxis": "x",
         "y": [
          53.488,
          70.029
         ],
         "yaxis": "y"
        },
        {
         "marker": {
          "color": [
           "#2ECC71",
           "#2ECC71",
           "#E74C3C",
           "#E74C3C",
           "#E74C3C",
           "#E74C3C",
           "#E74C3C"
          ]
         },
         "name": "Coef",
         "type": "bar",
         "x": [
          "term1_avg",
          "total_subjects",
          "attendance_absences_total",
          "gender_encoded",
          "age",
          "support_level_num",
          "course_encoded"
         ],
         "xaxis": "x2",
         "y": {
          "bdata": "9AzdfW081j/03WVK6gzSP+CmiVA0kGm/Au4yGR3Sib9c8CvkwqmXv3FbzJ8eH5i//sMxWhk0878=",
          "dtype": "f8"
         },
         "yaxis": "y2"
        },
        {
         "marker": {
          "color": "#9B59B6"
         },
         "name": "Residuos",
         "nbinsx": 20,
         "type": "histogram",
         "x": {
          "bdata": "AMeG52lg778AYAJjpuqYP0AW5slWTQHA4G3MqBMp8r8AT0FaAlXuPwA6ExTjlO6/wEBXxmKV5T+AeoMwanLWv8BRf0hef/c/gG0T3Nxp6j8AmxK0y+3VvwAAFywmmY0/AAetkHho4r9gSiX8f44DQIB9VsTX5Oa/YOC7YENX+D+AjI3KFuHlP6DxZrUX9ve/QLxiCE9r9T9AnmAmGqMAQKC0LFcONv2/0EHyNHsCA0AAjettEzHEvwBgs+FuNaY/gKKionUG+T8ANuGOhyzOP0DHQmQTu/g/gOxOCbbX5b8g9mRa0jn6v6Arypi/cwFAACDjC6rvy78AsoDJvUS9P2AIu/7/dvk/QA3tKLk4DEAQUqT2Zj8BQAC9OiAjN+K/wCUQx17e7j8gWuF2cN76PwDbeOC7Ydy/YEitR2en9b+A3ZcPu3HhvwCWgzd9wMo/AGLtaeco7T9AQnc3ndD1PwD4+jhxp/y/AKaJJ7Jbyr9g6YzEOyvzvwAe6sMHEs+/QEoMvtPc6z8AVCIA/T2xv2Bo6JnqO/C/QLCimxU+5T8ALLnE2uzLv4B99ueasvI/wFbzwHUI87+AhbvFPKLhvwB4i0pfc6I/AH1yW9Eu9D/Adho2/HLxv0DDFS507fY/AP9/3O885T8ATKJfB3i2P0DfK6MOn+U/ABI8yKuUtL9AlWtedZL5P0D/lylRQ+A/QL5wNW2fBkBArd21xsvkv+AxHr2M3fG/AIKXHtzYzj8A4KTMWfnUP0CUDwf+HfQ/wOOvMgKo6r+Ayaw0kjPkPwBLbRtkD9M/AHIpAEOj0r9ALaA1Ubr6v0AaDbjdovY/AE6TnYac5r/A8dYyBQT5vwD459Kwl+Y/UMSNf/XLAsDAhLmNIRv6P4Ad2N08eAJAgO7R4wbp5r8AHrErH8HWP8CShYMfWP8/ANA5Jv6M2D8AIOWMCKXYPwChLOPlXeW/ABx8I3ySyr8A3IFVm8y8v4BvMJ919eI/AOAugJ9Qfj+gI7GbID0IQAAWgfJcecW/QD750xIw5L+g5IYNj7wAQAC0FU7e/8e/gH49zZAh8z8=",
          "dtype": "f8"
         },
         "xaxis": "x3",
         "yaxis": "y3"
        },
        {
         "marker": {
          "color": "#F39C12"
         },
         "name": "CV R²",
         "type": "bar",
         "x": [
          "Fold 1",
          "Fold 2",
          "Fold 3",
          "Fold 4",
          "Fold 5"
         ],
         "xaxis": "x4",
         "y": {
          "bdata": "/Uoy3eH07D8s6DA5psHsP/qWcOrsVuw/psl946aw7T8Y1H+dlxDtPw==",
          "dtype": "f8"
         },
         "yaxis": "y4"
        }
       ],
       "layout": {
        "annotations": [
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Predicción vs Real (avg_final)",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Coeficientes del Modelo",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Distribución de Residuos",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "CV R² por Fold",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         }
        ],
        "height": 750,
        "showlegend": false,
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Modelo 1 — Regresión Lineal: avg_final | R²=0.9228  MAE=0.9877"
        },
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis2": {
         "anchor": "y2",
         "domain": [
          0.55,
          1
         ]
        },
        "xaxis3": {
         "anchor": "y3",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis4": {
         "anchor": "y4",
         "domain": [
          0.55,
          1
         ]
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis2": {
         "anchor": "x2",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis3": {
         "anchor": "x3",
         "domain": [
          0,
          0.375
         ]
        },
        "yaxis4": {
         "anchor": "x4",
         "domain": [
          0,
          0.375
         ]
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dashboard serializado correctamente\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Visualización — Modelo 1 (Regresión Lineal: avg_final)\n",
    "\"\"\"\n",
    "\n",
    "residuals_m1  = y_test_m1.values - y_pred_m1\n",
    "\n",
    "bar_colors_m1 = ['#2ECC71' if v >= 0 else '#E74C3C' for v in coef_m1.values]\n",
    "\n",
    "fig_m1 = make_subplots(\n",
    "    rows = 2, cols = 2,\n",
    "    subplot_titles = [\n",
    "        'Predicción vs Real (avg_final)',\n",
    "        'Coeficientes del Modelo',\n",
    "        'Distribución de Residuos',\n",
    "        'CV R² por Fold',\n",
    "    ],\n",
    ")\n",
    "\n",
    "fig_m1.add_trace(\n",
    "\n",
    "    go.Scatter(\n",
    "        x = y_test_m1.values, y = y_pred_m1,\n",
    "        mode = 'markers',\n",
    "        marker = dict(color = '#4C9BE8', opacity = 0.6),\n",
    "        name = 'Predicciones',\n",
    "    ),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m1.add_trace(\n",
    "\n",
    "    go.Scatter(\n",
    "        x = [y_test_m1.min(), y_test_m1.max()],\n",
    "        y = [y_test_m1.min(), y_test_m1.max()],\n",
    "        mode = 'lines',\n",
    "        line = dict(color = '#E74C3C', dash = 'dash'),\n",
    "        name = 'Perfecto',\n",
    "    ),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m1.add_trace(\n",
    "\n",
    "    go.Bar(x = coef_m1.index.tolist(), y = coef_m1.values, marker_color = bar_colors_m1, name = 'Coef'),\n",
    "\n",
    "    row = 1, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m1.add_trace(\n",
    "\n",
    "    go.Histogram(x = residuals_m1, nbinsx = 20, marker_color = '#9B59B6', name = 'Residuos'),\n",
    "\n",
    "    row = 2, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m1.add_trace(\n",
    "\n",
    "    go.Bar(\n",
    "        x = [f'Fold {i+1}' for i in range(5)],\n",
    "        y = cv_scores_m1,\n",
    "        marker_color = '#F39C12',\n",
    "        name = 'CV R²',\n",
    "    ),\n",
    "\n",
    "    row = 2, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m1.update_layout(\n",
    "\n",
    "    title_text = f'Modelo 1 — Regresión Lineal: avg_final | R²={r2_m1:.4f}  MAE={mae_m1:.4f}',\n",
    "    height = 750,\n",
    "    showlegend = False,\n",
    "\n",
    ")\n",
    "\n",
    "with open('dashboard_ml_avg_final.json', 'w') as f:\n",
    "    json.dump(json.loads(fig_m1.to_json()), f)\n",
    "\n",
    "fig_m1.show(renderer = 'plotly_mimetype')\n",
    "\n",
    "print('Dashboard serializado correctamente')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "ccd23d3c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy:     0.9300\n",
      "ROC-AUC:      0.9935\n",
      "CV Acc 5-fold: 0.9680 ± 0.0172\n",
      "\n",
      "Odds Ratios (> 1 → aumenta probabilidad de Uptrend):\n",
      "                  feature  coeficiente  odds_ratio\n",
      "                term3_avg       4.8014    121.6772\n",
      "        support_level_num       0.2985      1.3479\n",
      "           gender_encoded      -0.0828      0.9205\n",
      "                term2_avg      -0.0866      0.9170\n",
      "                      age      -0.1378      0.8713\n",
      "attendance_absences_total      -0.2365      0.7894\n",
      "                term1_avg      -4.7144      0.0090\n",
      "\n",
      "Reporte de clasificación:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "   Downtrend       1.00      0.77      0.87        31\n",
      "     Uptrend       0.91      1.00      0.95        69\n",
      "\n",
      "    accuracy                           0.93       100\n",
      "   macro avg       0.95      0.89      0.91       100\n",
      "weighted avg       0.94      0.93      0.93       100\n",
      "\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Modelo 2 — Regresión Logística: Predicción de grade_tendency (Uptrend / Downtrend)\n",
    "\n",
    "Predice si el estudiante mejora o empeora a lo largo del curso.\n",
    "Extrae probabilidades individuales y odds ratios de cada feature.\n",
    "\"\"\"\n",
    "\n",
    "FEATURES_M2 = [\n",
    "    'term1_avg',\n",
    "    'term2_avg',\n",
    "    'term3_avg',\n",
    "    'attendance_absences_total',\n",
    "    'support_level_num',\n",
    "    'age',\n",
    "    'gender_encoded',\n",
    "]\n",
    "\n",
    "TARGET_M2 = 'grade_tendency_encoded'\n",
    "\n",
    "X_m2 = df[FEATURES_M2]\n",
    "y_m2 = df[TARGET_M2]\n",
    "\n",
    "X_train_m2, X_test_m2, y_train_m2, y_test_m2 = train_test_split(\n",
    "    X_m2, y_m2, test_size = 0.2, random_state = 42\n",
    ")\n",
    "\n",
    "scaler_m2     = StandardScaler()\n",
    "X_train_m2_sc = scaler_m2.fit_transform(X_train_m2)\n",
    "X_test_m2_sc  = scaler_m2.transform(X_test_m2)\n",
    "\n",
    "model_m2 = LogisticRegression(max_iter = 1000, random_state = 42)\n",
    "model_m2.fit(X_train_m2_sc, y_train_m2)\n",
    "\n",
    "y_pred_m2  = model_m2.predict(X_test_m2_sc)\n",
    "y_proba_m2 = model_m2.predict_proba(X_test_m2_sc)\n",
    "\n",
    "cv_pipe_m2 = Pipeline([('sc', StandardScaler()), ('clf', LogisticRegression(max_iter = 1000, random_state = 42))])\n",
    "cv_acc_m2  = cross_val_score(cv_pipe_m2, X_m2, y_m2, cv = 5, scoring = 'accuracy')\n",
    "\n",
    "acc_m2 = accuracy_score(y_test_m2, y_pred_m2)\n",
    "auc_m2 = roc_auc_score(y_test_m2, y_proba_m2[:, 1])\n",
    "fpr_m2, tpr_m2, _ = roc_curve(y_test_m2, y_proba_m2[:, 1])\n",
    "\n",
    "# Coeficientes y odds ratios\n",
    "\n",
    "coef_m2     = pd.Series(model_m2.coef_[0], index = FEATURES_M2).sort_values(ascending = False)\n",
    "\n",
    "odds_m2     = np.exp(model_m2.coef_[0])\n",
    "odds_df_m2  = pd.DataFrame({\n",
    "    'feature':    FEATURES_M2,\n",
    "    'coeficiente': model_m2.coef_[0].round(4),\n",
    "    'odds_ratio':  odds_m2.round(4),\n",
    "}).sort_values('odds_ratio', ascending = False)\n",
    "\n",
    "print(f'Accuracy:     {acc_m2:.4f}')\n",
    "print(f'ROC-AUC:      {auc_m2:.4f}')\n",
    "print(f'CV Acc 5-fold: {cv_acc_m2.mean():.4f} ± {cv_acc_m2.std():.4f}')\n",
    "\n",
    "print('\\nOdds Ratios (> 1 → aumenta probabilidad de Uptrend):')\n",
    "\n",
    "print(odds_df_m2.to_string(index = False))\n",
    "\n",
    "print('\\nReporte de clasificación:')\n",
    "\n",
    "print(classification_report(y_test_m2, y_pred_m2, target_names = ['Downtrend', 'Uptrend']))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "cb5467f2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Estudiantes con mayor incertidumbre en grade_tendency:\n",
      "student_id  prob_downtrend  prob_uptrend predicted    actual\n",
      "   EA00491          0.5163        0.4837 Downtrend Downtrend\n",
      "   EA00016          0.4633        0.5367   Uptrend Downtrend\n",
      "   EA00495          0.4399        0.5601   Uptrend Downtrend\n",
      "   EA00451          0.4233        0.5767   Uptrend Downtrend\n",
      "   EA00269          0.4228        0.5772   Uptrend   Uptrend\n",
      "   EA00056          0.5912        0.4088 Downtrend Downtrend\n",
      "   EA00125          0.4086        0.5914   Uptrend   Uptrend\n",
      "   EA00476          0.5977        0.4023 Downtrend Downtrend\n",
      "   EA00439          0.3915        0.6085   Uptrend Downtrend\n",
      "   EA00149          0.6124        0.3876 Downtrend Downtrend\n",
      "\n",
      "Falsos Uptrend (riesgo no detectado): 7 estudiantes\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Insights — Modelo 2: Probabilidades por estudiante\n",
    "\"\"\"\n",
    "\n",
    "# Probabilidades para todos los estudiantes del test set\n",
    "\n",
    "df_proba_m2 = pd.DataFrame({\n",
    "    'student_id':    df.loc[X_test_m2.index, 'student_id'].values,\n",
    "    'prob_downtrend': y_proba_m2[:, 0].round(4),\n",
    "    'prob_uptrend':   y_proba_m2[:, 1].round(4),\n",
    "    'predicted':      ['Uptrend' if p == 1 else 'Downtrend' for p in y_pred_m2],\n",
    "    'actual':         ['Uptrend' if p == 1 else 'Downtrend' for p in y_test_m2.values],\n",
    "})\n",
    "\n",
    "# Estudiantes con mayor incertidumbre (probabilidad cercana a 0.5)\n",
    "\n",
    "df_proba_m2['incertidumbre'] = np.abs(df_proba_m2['prob_uptrend'] - 0.5)\n",
    "\n",
    "df_uncertain_m2 = df_proba_m2.sort_values('incertidumbre').head(10)\n",
    "\n",
    "print('Estudiantes con mayor incertidumbre en grade_tendency:')\n",
    "\n",
    "print(df_uncertain_m2[['student_id', 'prob_downtrend', 'prob_uptrend', 'predicted', 'actual']].to_string(index = False))\n",
    "\n",
    "# Falsos negativos: predichos Uptrend pero real Downtrend\n",
    "\n",
    "df_fn_m2 = df_proba_m2[(df_proba_m2['predicted'] == 'Uptrend') & (df_proba_m2['actual'] == 'Downtrend')]\n",
    "\n",
    "print(f'\\nFalsos Uptrend (riesgo no detectado): {len(df_fn_m2)} estudiantes')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "d510c9fc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "line": {
          "color": "#9B59B6"
         },
         "mode": "lines",
         "name": "ROC",
         "type": "scatter",
         "x": {
          "bdata": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhBBCCCGEoD+EEEIIIYSgP8YYY4wxxrg/xhhjjDHGuD+EEEIIIYTAP4QQQgghhMA/AAAAAAAA8D8=",
          "dtype": "f8"
         },
         "xaxis": "x",
         "y": {
          "bdata": "AAAAAAAAAADbgbl2YK6NP9Ob3vSmN+0/05ve9KY37T/qTW9605vuP+pNb3rTm+4/8TNK/IwS7z/xM0r8jBLvPwAAAAAAAPA/AAAAAAAA8D8=",
          "dtype": "f8"
         },
         "yaxis": "y"
        },
        {
         "line": {
          "color": "#95A5A6",
          "dash": "dash"
         },
         "mode": "lines",
         "name": "Random",
         "type": "scatter",
         "x": [
          0,
          1
         ],
         "xaxis": "x",
         "y": [
          0,
          1
         ],
         "yaxis": "y"
        },
        {
         "marker": {
          "color": "#4C9BE8"
         },
         "name": "Odds Ratio",
         "type": "bar",
         "x": [
          "term3_avg",
          "support_level_num",
          "gender_encoded",
          "term2_avg",
          "age",
          "attendance_absences_total",
          "term1_avg"
         ],
         "xaxis": "x2",
         "y": [
          121.6772,
          1.3479,
          0.9205,
          0.917,
          0.8713,
          0.7894,
          0.009
         ],
         "yaxis": "y2"
        },
        {
         "colorscale": [
          [
           0,
           "rgb(247,251,255)"
          ],
          [
           0.125,
           "rgb(222,235,247)"
          ],
          [
           0.25,
           "rgb(198,219,239)"
          ],
          [
           0.375,
           "rgb(158,202,225)"
          ],
          [
           0.5,
           "rgb(107,174,214)"
          ],
          [
           0.625,
           "rgb(66,146,198)"
          ],
          [
           0.75,
           "rgb(33,113,181)"
          ],
          [
           0.875,
           "rgb(8,81,156)"
          ],
          [
           1,
           "rgb(8,48,107)"
          ]
         ],
         "showscale": false,
         "text": {
          "bdata": "GAcARQ==",
          "dtype": "i1",
          "shape": "2, 2"
         },
         "texttemplate": "%{text}",
         "type": "heatmap",
         "x": [
          "Pred Downtrend",
          "Pred Uptrend"
         ],
         "xaxis": "x3",
         "y": [
          "Real Downtrend",
          "Real Uptrend"
         ],
         "yaxis": "y3",
         "z": {
          "bdata": "GAcARQ==",
          "dtype": "i1",
          "shape": "2, 2"
         }
        },
        {
         "marker": {
          "color": "#2ECC71"
         },
         "name": "P(Uptrend)",
         "nbinsx": 20,
         "type": "histogram",
         "x": {
          "bdata": "URNIOEslvz9XqmJpmPrvP/GB/ev9vMo/1YB4LMDG4z+pdbIgBp3CPwo7S1Syiow/36ueKJKn7z9n31oCy+ziP9tikN2O+O8/5kXxhKJ04j/+1rDVpDnsP8l5Ock3w+8/QFYwVl+d6T86fYLAmNTtP00gcNFtb5M/tyz80lT27z+kBiAh6/HFP0ERRb6wpuU/E2UZKtKm7j9/UyWNKZvvP/7pjuNb9N4/Fy0Xn+//7z8hIfPnO0XpP7oLcXtWTG4/xJ3ope8Y7j+JyhQc8wqbP/LNnhjrMe4/suybvLyM2D+eb59w9JPoP/oVJsct++8/1qE3V7O+2T9/IgZJfB7vP7fb/fPrRO8/sRshP7T77z8lf1n7ETHvP6wLHc4B/u8/LnE6MuaL7z9d4KGSgv7vP+wjfVLy/e8/wHgPHsm97z9Q+Tb0sAZ3P55gS+ZJ9tY/hJU7wpz17z8AqZ8YTsvvP0xpDymDRe4/0lbqukD/7z8mcCtK4fzvP2oUP69Qsu8/fciGZBT/7z8SD78PU/rvP02z3KEHY+Y/gw5LbPL/7z/Jqr9EvfHvP29oU5FR57Q/wjObY0Gwhz+uliJGDCnaP28JuOX2zdg/gaouIBAG6j9zhLeSBaznPyz4kVYhX+4/a2oivRfs4T9z9QlHTPXvPwUPFg8H6u8/wAZRsacn7D80/pVxrv/vP7Zq1Ql2deo/1Z8bCxvu7z9hlqVpswTSP70oD2aIWO8/9pNGJE/77z8kGEwMvRzoPxKmrlqRru4/iUOygz+d0j9P/r34S27uPxxNQS4tku4/WVmZDd/C7z/UzrxJnnjiP637Kx1Ei+8/C1snfq9h5T+toyFPpW60Px3dC3UrlJg/GxKSvJjv6z/zxhFUqP/vP4+qULagzu8/Np7PX1A47z+JRX+BWJxnP5nujH0h+u8/aIevetb+7z9E7lf7IxXvP/KvlW+T+u8/snFVkYvpwD9Yr3yn5azuP+9ELzPTie0/YVFv2QoP5z+sT2FgJP/vP3/WqEh0CuU/H+QlctE5kj+yUqbPE/HvPwHvNcUDeeM/tZ3bEWws4T8=",
          "dtype": "f8"
         },
         "xaxis": "x4",
         "yaxis": "y4"
        }
       ],
       "layout": {
        "annotations": [
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Curva ROC (AUC = 0.9935)",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Odds Ratios por Feature",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Matriz de Confusión",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Distribución P(Uptrend) — Test Set",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         }
        ],
        "height": 750,
        "showlegend": false,
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Modelo 2 — Regresión Logística: grade_tendency | Acc=0.9300  AUC=0.9935"
        },
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis2": {
         "anchor": "y2",
         "domain": [
          0.55,
          1
         ]
        },
        "xaxis3": {
         "anchor": "y3",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis4": {
         "anchor": "y4",
         "domain": [
          0.55,
          1
         ]
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis2": {
         "anchor": "x2",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis3": {
         "anchor": "x3",
         "domain": [
          0,
          0.375
         ]
        },
        "yaxis4": {
         "anchor": "x4",
         "domain": [
          0,
          0.375
         ]
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dashboard serializado correctamente\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Visualización — Modelo 2 (Regresión Logística: grade_tendency)\n",
    "\"\"\"\n",
    "\n",
    "cm_m2         = confusion_matrix(y_test_m2, y_pred_m2)\n",
    "\n",
    "bar_colors_m2 = ['#2ECC71' if v >= 0 else '#E74C3C' for v in coef_m2.values]\n",
    "\n",
    "fig_m2 = make_subplots(\n",
    "    rows = 2, cols = 2,\n",
    "    subplot_titles = [\n",
    "        f'Curva ROC (AUC = {auc_m2:.4f})',\n",
    "        'Odds Ratios por Feature',\n",
    "        'Matriz de Confusión',\n",
    "        'Distribución P(Uptrend) — Test Set',\n",
    "    ],\n",
    ")\n",
    "\n",
    "fig_m2.add_trace(\n",
    "\n",
    "    go.Scatter(x = fpr_m2, y = tpr_m2, mode = 'lines', line = dict(color = '#9B59B6'), name = 'ROC'),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m2.add_trace(\n",
    "\n",
    "    go.Scatter(x = [0, 1], y = [0, 1], mode = 'lines', line = dict(color = '#95A5A6', dash = 'dash'), name = 'Random'),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m2.add_trace(\n",
    "\n",
    "    go.Bar(\n",
    "        x = odds_df_m2['feature'].tolist(),\n",
    "        y = odds_df_m2['odds_ratio'].tolist(),\n",
    "        marker_color = '#4C9BE8',\n",
    "        name = 'Odds Ratio',\n",
    "    ),\n",
    "\n",
    "    row = 1, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m2.add_trace(\n",
    "\n",
    "    go.Heatmap(\n",
    "        z = cm_m2,\n",
    "        x = ['Pred Downtrend', 'Pred Uptrend'],\n",
    "        y = ['Real Downtrend', 'Real Uptrend'],\n",
    "        colorscale = 'Blues',\n",
    "        showscale = False,\n",
    "        text = cm_m2,\n",
    "        texttemplate = '%{text}',\n",
    "    ),\n",
    "\n",
    "    row = 2, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m2.add_trace(\n",
    "\n",
    "    go.Histogram(x = y_proba_m2[:, 1], nbinsx = 20, marker_color = '#2ECC71', name = 'P(Uptrend)'),\n",
    "\n",
    "    row = 2, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m2.update_layout(\n",
    "\n",
    "    title_text = f'Modelo 2 — Regresión Logística: grade_tendency | Acc={acc_m2:.4f}  AUC={auc_m2:.4f}',\n",
    "    height = 750,\n",
    "    showlegend = False,\n",
    "\n",
    ")\n",
    "\n",
    "with open('dashboard_ml_grade_tendency.json', 'w') as f:\n",
    "    json.dump(json.loads(fig_m2.to_json()), f)\n",
    "\n",
    "fig_m2.show(renderer = 'plotly_mimetype')\n",
    "\n",
    "print('Dashboard serializado correctamente')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "2b6bd2df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy:      0.9700\n",
      "ROC-AUC:       0.9976\n",
      "CV Acc 5-fold: 0.9820 ± 0.0117\n",
      "\n",
      "Odds Ratios (> 1 → aumenta probabilidad de Pass):\n",
      "                  feature  coeficiente  odds_ratio\n",
      "        Mathematics_term1       3.5193     33.7608\n",
      "        Mathematics_term2       3.0046     20.1787\n",
      "        Mathematics_term3       2.5755     13.1379\n",
      "attendance_absences_total       0.1220      1.1298\n",
      "        support_level_num      -0.1135      0.8927\n",
      "\n",
      "Reporte de clasificación:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "        Fail       1.00      0.80      0.89        15\n",
      "        Pass       0.97      1.00      0.98        85\n",
      "\n",
      "    accuracy                           0.97       100\n",
      "   macro avg       0.98      0.90      0.94       100\n",
      "weighted avg       0.97      0.97      0.97       100\n",
      "\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Modelo 3 — Regresión Logística: Predicción de Mathematics Pass / Fail\n",
    "\n",
    "Predice si el estudiante aprobará Matemáticas.\n",
    "Identifica estudiantes en riesgo de suspenso a partir de sus notas trimestrales.\n",
    "\"\"\"\n",
    "\n",
    "FEATURES_M3 = [\n",
    "    'Mathematics_term1',\n",
    "    'Mathematics_term2',\n",
    "    'Mathematics_term3',\n",
    "    'attendance_absences_total',\n",
    "    'support_level_num',\n",
    "]\n",
    "\n",
    "TARGET_M3 = 'mathematics_pass'\n",
    "\n",
    "X_m3 = df[FEATURES_M3]\n",
    "y_m3 = df[TARGET_M3]\n",
    "\n",
    "X_train_m3, X_test_m3, y_train_m3, y_test_m3 = train_test_split(\n",
    "    X_m3, y_m3, test_size = 0.2, random_state = 42\n",
    ")\n",
    "\n",
    "scaler_m3     = StandardScaler()\n",
    "X_train_m3_sc = scaler_m3.fit_transform(X_train_m3)\n",
    "X_test_m3_sc  = scaler_m3.transform(X_test_m3)\n",
    "\n",
    "model_m3 = LogisticRegression(max_iter = 1000, random_state = 42)\n",
    "model_m3.fit(X_train_m3_sc, y_train_m3)\n",
    "\n",
    "y_pred_m3  = model_m3.predict(X_test_m3_sc)\n",
    "y_proba_m3 = model_m3.predict_proba(X_test_m3_sc)\n",
    "\n",
    "cv_pipe_m3 = Pipeline([('sc', StandardScaler()), ('clf', LogisticRegression(max_iter = 1000, random_state = 42))])\n",
    "cv_acc_m3  = cross_val_score(cv_pipe_m3, X_m3, y_m3, cv = 5, scoring = 'accuracy')\n",
    "\n",
    "acc_m3 = accuracy_score(y_test_m3, y_pred_m3)\n",
    "auc_m3 = roc_auc_score(y_test_m3, y_proba_m3[:, 1])\n",
    "fpr_m3, tpr_m3, _ = roc_curve(y_test_m3, y_proba_m3[:, 1])\n",
    "\n",
    "# Odds ratios\n",
    "\n",
    "odds_m3    = np.exp(model_m3.coef_[0])\n",
    "odds_df_m3 = pd.DataFrame({\n",
    "    'feature':     FEATURES_M3,\n",
    "    'coeficiente': model_m3.coef_[0].round(4),\n",
    "    'odds_ratio':  odds_m3.round(4),\n",
    "}).sort_values('odds_ratio', ascending = False)\n",
    "\n",
    "print(f'Accuracy:      {acc_m3:.4f}')\n",
    "print(f'ROC-AUC:       {auc_m3:.4f}')\n",
    "print(f'CV Acc 5-fold: {cv_acc_m3.mean():.4f} ± {cv_acc_m3.std():.4f}')\n",
    "\n",
    "print('\\nOdds Ratios (> 1 → aumenta probabilidad de Pass):')\n",
    "\n",
    "print(odds_df_m3.to_string(index = False))\n",
    "\n",
    "print('\\nReporte de clasificación:')\n",
    "\n",
    "print(classification_report(y_test_m3, y_pred_m3, target_names = ['Fail', 'Pass']))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "63415e45",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Distribución de nivel de riesgo de suspenso en Matemáticas:\n",
      "risk_level\n",
      "Bajo     377\n",
      "Alto      83\n",
      "Medio     40\n",
      "\n",
      "Estudiantes con riesgo ALTO de suspenso en Matemáticas: 83\n",
      "student_id  Math_term1  Math_term2  Math_term3  prob_fail real_status\n",
      "   EA00261        38.7        23.4        45.6     1.0000        Fail\n",
      "   EA00325        33.0        36.5        48.3     0.9997        Fail\n",
      "   EA00039        45.7        44.3        27.5     0.9997        Fail\n",
      "   EA00269        33.4        32.2        53.4     0.9997        Fail\n",
      "   EA00339        33.3        43.4        42.6     0.9996        Fail\n",
      "   EA00124        36.4        35.9        49.0     0.9994        Fail\n",
      "   EA00350        34.7        54.1        32.1     0.9993        Fail\n",
      "   EA00390        47.4        42.5        31.1     0.9991        Fail\n",
      "   EA00201        31.8        44.1        48.3     0.9990        Fail\n",
      "   EA00396        39.2        55.4        27.7     0.9989        Fail\n",
      "   EA00230        38.0        41.6        43.7     0.9989        Fail\n",
      "   EA00117        48.3        29.3        48.1     0.9973        Fail\n",
      "   EA00149        31.7        48.7        47.5     0.9963        Fail\n",
      "   EA00304        38.2        42.6        48.0     0.9962        Fail\n",
      "   EA00394        44.0        41.8        42.7     0.9952        Fail\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Insights — Modelo 3: Estudiantes en riesgo de suspenso en Matemáticas\n",
    "\"\"\"\n",
    "\n",
    "# Probabilidades para todos los estudiantes (train + test, predicción sobre dataset completo)\n",
    "\n",
    "X_all_m3_sc  = scaler_m3.transform(X_m3)\n",
    "\n",
    "proba_all_m3 = model_m3.predict_proba(X_all_m3_sc)\n",
    "\n",
    "df_risk_m3 = pd.DataFrame({\n",
    "    'student_id':     df['student_id'].values,\n",
    "    'Math_term1':     df['Mathematics_term1'].values,\n",
    "    'Math_term2':     df['Mathematics_term2'].values,\n",
    "    'Math_term3':     df['Mathematics_term3'].values,\n",
    "    'Math_final':     df['Mathematics_final'].values,\n",
    "    'real_status':    df['Mathematics_status'].values,\n",
    "    'prob_fail':      proba_all_m3[:, 0].round(4),\n",
    "    'prob_pass':      proba_all_m3[:, 1].round(4),\n",
    "    'risk_level':     pd.cut(\n",
    "        proba_all_m3[:, 0],\n",
    "        bins   = [0, 0.2, 0.5, 1.0],\n",
    "        labels = ['Bajo', 'Medio', 'Alto'],\n",
    "    ),\n",
    "})\n",
    "\n",
    "# Distribución de niveles de riesgo\n",
    "\n",
    "print('Distribución de nivel de riesgo de suspenso en Matemáticas:')\n",
    "\n",
    "print(df_risk_m3['risk_level'].value_counts().to_string())\n",
    "\n",
    "# Estudiantes con riesgo alto (P(Fail) > 0.5) ordenados por mayor riesgo\n",
    "\n",
    "df_high_risk_m3 = df_risk_m3[df_risk_m3['prob_fail'] > 0.5].sort_values('prob_fail', ascending = False)\n",
    "\n",
    "print(f'\\nEstudiantes con riesgo ALTO de suspenso en Matemáticas: {len(df_high_risk_m3)}')\n",
    "\n",
    "print(df_high_risk_m3[['student_id', 'Math_term1', 'Math_term2', 'Math_term3', 'prob_fail', 'real_status']].head(15).to_string(index = False))\n",
    "\n",
    "# Exportar scores de riesgo\n",
    "\n",
    "df_risk_m3.to_csv('../datasets/math_risk_scores.csv', index = False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "78295d95",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "line": {
          "color": "#E74C3C"
         },
         "mode": "lines",
         "name": "ROC",
         "type": "scatter",
         "x": {
          "bdata": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERERERERsT8RERERERGxPxEREREREcE/ERERERERwT8AAAAAAADwPw==",
          "dtype": "f8"
         },
         "xaxis": "x",
         "y": {
          "bdata": "AAAAAAAAAAAYGBgYGBiIPz8/Pz8/P+8/Pz8/Pz8/7z+gn5+fn5/vP6Cfn5+fn+8/AAAAAAAA8D8AAAAAAADwPw==",
          "dtype": "f8"
         },
         "yaxis": "y"
        },
        {
         "line": {
          "color": "#95A5A6",
          "dash": "dash"
         },
         "mode": "lines",
         "name": "Random",
         "type": "scatter",
         "x": [
          0,
          1
         ],
         "xaxis": "x",
         "y": [
          0,
          1
         ],
         "yaxis": "y"
        },
        {
         "marker": {
          "color": "#E74C3C"
         },
         "name": "Odds Ratio",
         "type": "bar",
         "x": [
          "Mathematics_term1",
          "Mathematics_term2",
          "Mathematics_term3",
          "attendance_absences_total",
          "support_level_num"
         ],
         "xaxis": "x2",
         "y": [
          33.7608,
          20.1787,
          13.1379,
          1.1298,
          0.8927
         ],
         "yaxis": "y2"
        },
        {
         "colorscale": [
          [
           0,
           "rgb(255,245,240)"
          ],
          [
           0.125,
           "rgb(254,224,210)"
          ],
          [
           0.25,
           "rgb(252,187,161)"
          ],
          [
           0.375,
           "rgb(252,146,114)"
          ],
          [
           0.5,
           "rgb(251,106,74)"
          ],
          [
           0.625,
           "rgb(239,59,44)"
          ],
          [
           0.75,
           "rgb(203,24,29)"
          ],
          [
           0.875,
           "rgb(165,15,21)"
          ],
          [
           1,
           "rgb(103,0,13)"
          ]
         ],
         "showscale": false,
         "text": {
          "bdata": "DAMAVQ==",
          "dtype": "i1",
          "shape": "2, 2"
         },
         "texttemplate": "%{text}",
         "type": "heatmap",
         "x": [
          "Pred Fail",
          "Pred Pass"
         ],
         "xaxis": "x3",
         "y": [
          "Real Fail",
          "Real Pass"
         ],
         "yaxis": "y3",
         "z": {
          "bdata": "DAMAVQ==",
          "dtype": "i1",
          "shape": "2, 2"
         }
        },
        {
         "marker": {
          "color": {
           "bdata": "[encoded data, 5336 characters]",
           "dtype": "f8"
          },
          "colorscale": [
           [
            0,
            "rgb(0,104,55)"
           ],
           [
            0.1,
            "rgb(26,152,80)"
           ],
           [
            0.2,
            "rgb(102,189,99)"
           ],
           [
            0.3,
            "rgb(166,217,106)"
           ],
           [
            0.4,
            "rgb(217,239,139)"
           ],
           [
            0.5,
            "rgb(255,255,191)"
           ],
           [
            0.6,
            "rgb(254,224,139)"
           ],
           [
            0.7,
            "rgb(253,174,97)"
           ],
           [
            0.8,
            "rgb(244,109,67)"
           ],
           [
            0.9,
            "rgb(215,48,39)"
           ],
           [
            1,
            "rgb(165,0,38)"
           ]
          ],
          "showscale": true
         },
         "mode": "markers",
         "name": "P(Fail)",
         "type": "scatter",
         "x": [
          0,
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11,
          12,
          13,
          14,
          15,
          16,
          17,
          18,
          19,
          20,
          21,
          22,
          23,
          24,
          25,
          26,
          27,
          28,
          29,
          30,
          31,
          32,
          33,
          34,
          35,
          36,
          37,
          38,
          39,
          40,
          41,
          42,
          43,
          44,
          45,
          46,
          47,
          48,
          49,
          50,
          51,
          52,
          53,
          54,
          55,
          56,
          57,
          58,
          59,
          60,
          61,
          62,
          63,
          64,
          65,
          66,
          67,
          68,
          69,
          70,
          71,
          72,
          73,
          74,
          75,
          76,
          77,
          78,
          79,
          80,
          81,
          82,
          83,
          84,
          85,
          86,
          87,
          88,
          89,
          90,
          91,
          92,
          93,
          94,
          95,
          96,
          97,
          98,
          99,
          100,
          101,
          102,
          103,
          104,
          105,
          106,
          107,
          108,
          109,
          110,
          111,
          112,
          113,
          114,
          115,
          116,
          117,
          118,
          119,
          120,
          121,
          122,
          123,
          124,
          125,
          126,
          127,
          128,
          129,
          130,
          131,
          132,
          133,
          134,
          135,
          136,
          137,
          138,
          139,
          140,
          141,
          142,
          143,
          144,
          145,
          146,
          147,
          148,
          149,
          150,
          151,
          152,
          153,
          154,
          155,
          156,
          157,
          158,
          159,
          160,
          161,
          162,
          163,
          164,
          165,
          166,
          167,
          168,
          169,
          170,
          171,
          172,
          173,
          174,
          175,
          176,
          177,
          178,
          179,
          180,
          181,
          182,
          183,
          184,
          185,
          186,
          187,
          188,
          189,
          190,
          191,
          192,
          193,
          194,
          195,
          196,
          197,
          198,
          199,
          200,
          201,
          202,
          203,
          204,
          205,
          206,
          207,
          208,
          209,
          210,
          211,
          212,
          213,
          214,
          215,
          216,
          217,
          218,
          219,
          220,
          221,
          222,
          223,
          224,
          225,
          226,
          227,
          228,
          229,
          230,
          231,
          232,
          233,
          234,
          235,
          236,
          237,
          238,
          239,
          240,
          241,
          242,
          243,
          244,
          245,
          246,
          247,
          248,
          249,
          250,
          251,
          252,
          253,
          254,
          255,
          256,
          257,
          258,
          259,
          260,
          261,
          262,
          263,
          264,
          265,
          266,
          267,
          268,
          269,
          270,
          271,
          272,
          273,
          274,
          275,
          276,
          277,
          278,
          279,
          280,
          281,
          282,
          283,
          284,
          285,
          286,
          287,
          288,
          289,
          290,
          291,
          292,
          293,
          294,
          295,
          296,
          297,
          298,
          299,
          300,
          301,
          302,
          303,
          304,
          305,
          306,
          307,
          308,
          309,
          310,
          311,
          312,
          313,
          314,
          315,
          316,
          317,
          318,
          319,
          320,
          321,
          322,
          323,
          324,
          325,
          326,
          327,
          328,
          329,
          330,
          331,
          332,
          333,
          334,
          335,
          336,
          337,
          338,
          339,
          340,
          341,
          342,
          343,
          344,
          345,
          346,
          347,
          348,
          349,
          350,
          351,
          352,
          353,
          354,
          355,
          356,
          357,
          358,
          359,
          360,
          361,
          362,
          363,
          364,
          365,
          366,
          367,
          368,
          369,
          370,
          371,
          372,
          373,
          374,
          375,
          376,
          377,
          378,
          379,
          380,
          381,
          382,
          383,
          384,
          385,
          386,
          387,
          388,
          389,
          390,
          391,
          392,
          393,
          394,
          395,
          396,
          397,
          398,
          399,
          400,
          401,
          402,
          403,
          404,
          405,
          406,
          407,
          408,
          409,
          410,
          411,
          412,
          413,
          414,
          415,
          416,
          417,
          418,
          419,
          420,
          421,
          422,
          423,
          424,
          425,
          426,
          427,
          428,
          429,
          430,
          431,
          432,
          433,
          434,
          435,
          436,
          437,
          438,
          439,
          440,
          441,
          442,
          443,
          444,
          445,
          446,
          447,
          448,
          449,
          450,
          451,
          452,
          453,
          454,
          455,
          456,
          457,
          458,
          459,
          460,
          461,
          462,
          463,
          464,
          465,
          466,
          467,
          468,
          469,
          470,
          471,
          472,
          473,
          474,
          475,
          476,
          477,
          478,
          479,
          480,
          481,
          482,
          483,
          484,
          485,
          486,
          487,
          488,
          489,
          490,
          491,
          492,
          493,
          494,
          495,
          496,
          497,
          498,
          499
         ],
         "xaxis": "x4",
         "y": {
          "bdata": "[encoded data, 5336 characters]",
          "dtype": "f8"
         },
         "yaxis": "y4"
        }
       ],
       "layout": {
        "annotations": [
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Curva ROC (AUC = 0.9976)",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Odds Ratios por Feature",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Matriz de Confusión",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "P(Fail) por Estudiante — Todos",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         }
        ],
        "height": 750,
        "showlegend": false,
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Modelo 3 — Regresión Logística: Mathematics Pass/Fail | Acc=0.9700  AUC=0.9976"
        },
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis2": {
         "anchor": "y2",
         "domain": [
          0.55,
          1
         ]
        },
        "xaxis3": {
         "anchor": "y3",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis4": {
         "anchor": "y4",
         "domain": [
          0.55,
          1
         ]
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis2": {
         "anchor": "x2",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis3": {
         "anchor": "x3",
         "domain": [
          0,
          0.375
         ]
        },
        "yaxis4": {
         "anchor": "x4",
         "domain": [
          0,
          0.375
         ]
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dashboard serializado correctamente\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Visualización — Modelo 3 (Regresión Logística: Mathematics Pass/Fail)\n",
    "\"\"\"\n",
    "\n",
    "cm_m3         = confusion_matrix(y_test_m3, y_pred_m3)\n",
    "\n",
    "bar_colors_m3 = ['#2ECC71' if v >= 0 else '#E74C3C' for v in odds_df_m3['coeficiente'].values]\n",
    "\n",
    "fig_m3 = make_subplots(\n",
    "    rows = 2, cols = 2,\n",
    "    subplot_titles = [\n",
    "        f'Curva ROC (AUC = {auc_m3:.4f})',\n",
    "        'Odds Ratios por Feature',\n",
    "        'Matriz de Confusión',\n",
    "        'P(Fail) por Estudiante — Todos',\n",
    "    ],\n",
    ")\n",
    "\n",
    "fig_m3.add_trace(\n",
    "\n",
    "    go.Scatter(x = fpr_m3, y = tpr_m3, mode = 'lines', line = dict(color = '#E74C3C'), name = 'ROC'),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m3.add_trace(\n",
    "\n",
    "    go.Scatter(x = [0, 1], y = [0, 1], mode = 'lines', line = dict(color = '#95A5A6', dash = 'dash'), name = 'Random'),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m3.add_trace(\n",
    "\n",
    "    go.Bar(\n",
    "        x = odds_df_m3['feature'].tolist(),\n",
    "        y = odds_df_m3['odds_ratio'].tolist(),\n",
    "        marker_color = '#E74C3C',\n",
    "        name = 'Odds Ratio',\n",
    "    ),\n",
    "\n",
    "    row = 1, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m3.add_trace(\n",
    "\n",
    "    go.Heatmap(\n",
    "        z = cm_m3,\n",
    "        x = ['Pred Fail', 'Pred Pass'],\n",
    "        y = ['Real Fail', 'Real Pass'],\n",
    "        colorscale = 'Reds',\n",
    "        showscale = False,\n",
    "        text = cm_m3,\n",
    "        texttemplate = '%{text}',\n",
    "    ),\n",
    "\n",
    "    row = 2, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m3.add_trace(\n",
    "\n",
    "    go.Scatter(\n",
    "        x = list(range(len(df_risk_m3))),\n",
    "        y = df_risk_m3.sort_values('prob_fail', ascending = False)['prob_fail'].values,\n",
    "        mode = 'markers',\n",
    "        marker = dict(\n",
    "            color     = df_risk_m3.sort_values('prob_fail', ascending = False)['prob_fail'].values,\n",
    "            colorscale = 'RdYlGn_r',\n",
    "            showscale  = True,\n",
    "        ),\n",
    "        name = 'P(Fail)',\n",
    "    ),\n",
    "\n",
    "    row = 2, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m3.update_layout(\n",
    "\n",
    "    title_text = f'Modelo 3 — Regresión Logística: Mathematics Pass/Fail | Acc={acc_m3:.4f}  AUC={auc_m3:.4f}',\n",
    "    height = 750,\n",
    "    showlegend = False,\n",
    "\n",
    ")\n",
    "\n",
    "with open('dashboard_ml_math_pass.json', 'w') as f:\n",
    "    json.dump(json.loads(fig_m3.to_json()), f)\n",
    "\n",
    "fig_m3.show(renderer = 'plotly_mimetype')\n",
    "\n",
    "print('Dashboard serializado correctamente')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "4e926db5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "R²:           1.0000\n",
      "MAE:          0.0217\n",
      "RMSE:         0.0268\n",
      "Intercepto:   0.0016\n",
      "CV R² 5-fold: 1.0000 ± 0.0000\n",
      "\n",
      "Coeficientes (peso de cada trimestre en la nota final):\n",
      "Mathematics_term2            0.333349\n",
      "Mathematics_term3            0.333324\n",
      "Mathematics_term1            0.333279\n",
      "attendance_absences_total    0.000028\n",
      "\n",
      "Top 10 mayor error de predicción (Mathematics_final):\n",
      "student_id  real  predicho  error_abs\n",
      "   EA00492  59.3     59.26       0.04\n",
      "   EA00001  60.9     60.86       0.04\n",
      "   EA00363  71.0     70.96       0.04\n",
      "   EA00074  71.7     71.66       0.04\n",
      "   EA00451  68.4     68.36       0.04\n",
      "   EA00496  64.8     64.76       0.04\n",
      "   EA00407  52.3     52.26       0.04\n",
      "   EA00205  63.7     63.67       0.03\n",
      "   EA00047  50.6     50.63       0.03\n",
      "   EA00452  70.5     70.53       0.03\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Modelo 4 — Regresión Lineal: Predicción de Mathematics_final\n",
    "\n",
    "Predice la nota final de Matemáticas desde las notas trimestrales.\n",
    "Analiza qué trimestre tiene mayor peso en la nota final.\n",
    "\"\"\"\n",
    "\n",
    "FEATURES_M4 = [\n",
    "    'Mathematics_term1',\n",
    "    'Mathematics_term2',\n",
    "    'Mathematics_term3',\n",
    "    'attendance_absences_total',\n",
    "]\n",
    "\n",
    "TARGET_M4 = 'Mathematics_final'\n",
    "\n",
    "X_m4 = df[FEATURES_M4]\n",
    "y_m4 = df[TARGET_M4]\n",
    "\n",
    "X_train_m4, X_test_m4, y_train_m4, y_test_m4 = train_test_split(\n",
    "    X_m4, y_m4, test_size = 0.2, random_state = 42\n",
    ")\n",
    "\n",
    "model_m4 = LinearRegression()\n",
    "model_m4.fit(X_train_m4, y_train_m4)\n",
    "\n",
    "y_pred_m4    = model_m4.predict(X_test_m4)\n",
    "\n",
    "cv_scores_m4 = cross_val_score(model_m4, X_m4, y_m4, cv = 5, scoring = 'r2')\n",
    "\n",
    "r2_m4   = r2_score(y_test_m4, y_pred_m4)\n",
    "mae_m4  = mean_absolute_error(y_test_m4, y_pred_m4)\n",
    "rmse_m4 = np.sqrt(mean_squared_error(y_test_m4, y_pred_m4))\n",
    "\n",
    "coef_m4 = pd.Series(model_m4.coef_, index = FEATURES_M4).sort_values(ascending = False)\n",
    "\n",
    "print(f'R²:           {r2_m4:.4f}')\n",
    "print(f'MAE:          {mae_m4:.4f}')\n",
    "print(f'RMSE:         {rmse_m4:.4f}')\n",
    "print(f'Intercepto:   {model_m4.intercept_:.4f}')\n",
    "print(f'CV R² 5-fold: {cv_scores_m4.mean():.4f} ± {cv_scores_m4.std():.4f}')\n",
    "\n",
    "print('\\nCoeficientes (peso de cada trimestre en la nota final):')\n",
    "\n",
    "print(coef_m4.to_string())\n",
    "\n",
    "# Comparación real vs predicho\n",
    "\n",
    "df_compare_m4 = pd.DataFrame({\n",
    "    'student_id': df.loc[X_test_m4.index, 'student_id'].values,\n",
    "    'real':       y_test_m4.values.round(2),\n",
    "    'predicho':   y_pred_m4.round(2),\n",
    "    'error_abs':  np.abs(y_test_m4.values - y_pred_m4).round(2),\n",
    "}).sort_values('error_abs', ascending = False)\n",
    "\n",
    "print('\\nTop 10 mayor error de predicción (Mathematics_final):')\n",
    "\n",
    "print(df_compare_m4.head(10).to_string(index = False))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "4d3db566",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "marker": {
          "color": "#E67E22",
          "opacity": 0.6
         },
         "mode": "markers",
         "name": "Predicciones",
         "type": "scatter",
         "x": {
          "bdata": "AAAAAABATEDNzMzMzOxRQM3MzMzMDEtAAAAAAADATEDNzMzMzExJQGZmZmZmJkxAzczMzMyMUUAzMzMzM3NNQGZmZmZmJk5AmpmZmZkZUUAAAAAAAEBNQJqZmZmZmUxAZmZmZmYmSkAAAAAAAIBMQAAAAAAAQExAzczMzMxMTkAzMzMzMzNQQAAAAAAAAEpAAAAAAAAATUAzMzMzM3NIQDMzMzMzc0dAZmZmZmamTUDNzMzMzMxIQJqZmZmZOVFAzczMzMxMTkAAAAAAAIBMQAAAAAAAwEhAMzMzMzPzT0AAAAAAAIBJQDMzMzMzc0lAzczMzMzMUkDNzMzMzExMQAAAAAAAwE5AZmZmZmYmTEAAAAAAAIBJQDMzMzMz80lAMzMzMzNzUEDNzMzMzGxRQM3MzMzMDEpAMzMzMzNzS0AzMzMzM/NIQDMzMzMzs0pAzczMzMzMSkDNzMzMzMxKQAAAAAAAAEdAzczMzMxMSkAzMzMzM3NOQGZmZmZmpkxAzczMzMyMSEAzMzMzMzNMQJqZmZmZ2UZAzczMzMyMRkDNzMzMzCxQQM3MzMzM7FBAZmZmZmYmS0DNzMzMzExNQM3MzMzMTEVAmpmZmZkZTUCamZmZmdlPQDMzMzMzM0pAMzMzMzMTUUAAAAAAAIBNQM3MzMzMzEtAAAAAAACASEAAAAAAAABMQGZmZmZmZk9AAAAAAACgUUDNzMzMzExJQGZmZmZmpkpAzczMzMzMT0CamZmZmRlJQM3MzMzMTE5AMzMzMzPzSUCamZmZmVlRQGZmZmZm5kxAZmZmZmamT0CamZmZmdlDQM3MzMzMTFBAMzMzMzOzS0AzMzMzM7NIQAAAAAAAwExAzczMzMxMTECamZmZmVlPQAAAAAAAQE5AMzMzMzNzSUAzMzMzMzNMQJqZmZmZ2UtAAAAAAACgUkAAAAAAAMBIQGZmZmZmJk9AZmZmZmamTEDNzMzMzExOQAAAAAAAwFFAZmZmZmamQ0AAAAAAAABOQM3MzMzMDEtAzczMzMzMTUCamZmZmZlNQGZmZmZm5kxAMzMzMzMTUEA=",
          "dtype": "f8"
         },
         "xaxis": "x",
         "y": {
          "bdata": "AFeYAhZETEDSBdcvhepRQGcpklgREUtAcEh9Pcq/TEDJjU3HX0hJQBe6gpr+IUxAVYrpL9GOUUA0Cz5bUXdNQIbwPsrsIU5A61ZT/VAXUUA07Kl++D9NQIbmOq3TnUxA+9wFk+khSkCWhYNI+H9MQAkgi/T2P0xAWGAOh6hMTkBVFWec8jBQQFNv7zWJ+0lAXuiLTpX7TEAmWiAbWXNIQN6lV7TVbkdA3lZ8BOmhTUCTj0yco8xIQOkIMImLOVFAmm3oz6VMTkAZuyGOCoBMQNu+c7CHu0hA8sm+bE/3T0BAEW6A/X9JQJ4ImC0Sc0lANZsxHazMUkDovfTyo0xMQPAX1snZv05AFqt7YnEmTEBRDlHXq3tJQKbt7+9N90lAy3H8dUd1UEBQvrJMp2pRQMZkili8DEpAiCVVtFN3S0BwXnOs4u5IQLlH2xDRrkpAnHsMT1/ISkDOhhib7NBKQLz6V2S3+0ZAQlSRt3hISkDcQ5tfs25OQGta0N9WpkxAiLl3FSORSEBnjfub7C5MQDw6rTff3UZAaGOHOZ6IRkBD2EB4tyxQQLnfPIK37FBAGN6jZ2UmS0CLM4Wk9FBNQPxMkwojUUVAKNh200EVTUCQAQ8OJtVPQH40rrY0M0pARWAkhjwVUUBiByOu7H9NQJbhN7V4yEtAkmMqHLF7SEA+6V9J3/9LQKSN0LyQak9AUWRyVwKiUUC6cWL6A1FJQOmzja5NpkpA71EbCrzMT0DUKSd2SxVJQJ5Dlzr4UE5A4dgMwFP3SUAcxZecd1lRQKKhXcIx5kxA1PeY8CamT0AW5SEvWtVDQJXRjN/ETlBAsVcOddmuS0CMl6i2a7dIQHCgPBP6v0xAzu05/39ITEBPQZKrVlVPQNJ0nvHwP05AiO8j0jNzSUDaSIhi6C5MQBeu5mhX2UtA7QGhtxSiUkBhY6ViM8RIQJOml/RbKk9AVrMbHyOiTECQGo4RVUhOQGk23U/AvVFAVPbJuTSiQ0DwAl/TLgROQB77KtXEDEtACFn89a3MTUD0QMOrp5lNQPDfi9CP6kxAUDpDpfwQUEA=",
          "dtype": "f8"
         },
         "yaxis": "y"
        },
        {
         "line": {
          "color": "#E74C3C",
          "dash": "dash"
         },
         "mode": "lines",
         "name": "Perfecto",
         "type": "scatter",
         "x": [
          39.3,
          75.2
         ],
         "xaxis": "x",
         "y": [
          39.3,
          75.2
         ],
         "yaxis": "y"
        },
        {
         "marker": {
          "color": [
           "#2ECC71",
           "#2ECC71",
           "#2ECC71",
           "#2ECC71"
          ]
         },
         "name": "Coef",
         "type": "bar",
         "x": [
          "Mathematics_term2",
          "Mathematics_term3",
          "Mathematics_term1",
          "attendance_absences_total"
         ],
         "xaxis": "x2",
         "y": {
          "bdata": "sBqofpVV1T+nqQUQLlXVP59UHjhwVNU/zTCA2RbH/T4=",
          "dtype": "f8"
         },
         "yaxis": "y2"
        },
        {
         "marker": {
          "color": "#1ABC9C"
         },
         "name": "Residuos",
         "nbinsx": 20,
         "type": "histogram",
         "x": {
          "bdata": "AABcYQpYoL8A2Deu5zyiPwBochUvEqG/AADIW0HhWj8AEPz8FbShPwA8sY4vn6E/AEDs5RgjoL8ABGAroHigvwCA151w5qE/AHgVMuJEoj8AADBPWAUuPwCwM4VO6KC/AKwlgk3zoT8AAKjp8d0uPwAA7r/pFjI/AIA6Nt8iUj8A8O5gtgSiPwC0QkIo26E/AIhe0MWqoT8AgHmT9vNSvwBUNW77daE/ACA+qIf1oT8AAJ0eQJhUPwAAYiHTIEw/AICZL3J+Uz8AADJ2Qxw1vwCUBDE+4aE/APxaLuZwoL8AAAB2j/wTPwCASpXNglA/AADMmM1XYD8AgHIH7GxUPwAACPQUG1M/AABgiSr4Nb8AvMa7olChPwDM6fLyaqC/AMD0SRaioL8A6HPQACyhPwAAB2hCdEA/AFTJhwSCoL8ADFP/GkKhPwDorV+JiKE/AMREAfe1oT8ABOguOX+gvwAQFaBuIqE/ACzi7VRQoT8AXL1fTv+hPwAA9hcsDT8/AOyyqyJZob8AMJfeXBqhPwCIgk54FqG/AJSlFU26oD8AAIr0i1RVPwAAFO2PSlU/AADACVHY/z4A+JrhXp+gvwC8ABr3WKG/AMgFixhfoT8AKGAqLs6hPwAAsBSwNwi/AJBoiZdKoL8AAJ743FFDPwDcrFNeUKE/ALhxVo87oT8AAGELUFtQPwD4nKhZqaC/AIgik7sSoL8AtJNWttygvwAAfbLYt0g/AADeerHCQD8AGL/JjTihPwBE2ym3raC/ALiWZjOCoL8AAD/qgP5gPwAAYmIEUlo/AABJt+a6Xz8AENLeqf2gPwCATAAsgZ+/AAhuk/hmoT8AZJHVDeKgvwAAQH4Nsyc/APx7SzYzoT8ALGEduAuhPwAAXBbDHD4/AACgihfe874AZKmrQiuhPwDA4LosjGA/AGgPCL2loL8AhI2Vis2gvwBoAYpxrJ+/AEDMKh0NoT8A9Mj67N6hPwC4TBaB/aE/AEjAcbLGoD8AwAt8TbugvwAAvEaH3i8/AADFc9DWTj8AALROUyQ8vwAo5pWopaC/ABjHf2+0oT8=",
          "dtype": "f8"
         },
         "xaxis": "x3",
         "yaxis": "y3"
        },
        {
         "marker": {
          "color": "#F39C12"
         },
         "name": "Error Absoluto",
         "type": "bar",
         "x": [
          0,
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11,
          12,
          13,
          14,
          15,
          16,
          17,
          18,
          19,
          20,
          21,
          22,
          23,
          24,
          25,
          26,
          27,
          28,
          29,
          30,
          31,
          32,
          33,
          34,
          35,
          36,
          37,
          38,
          39,
          40,
          41,
          42,
          43,
          44,
          45,
          46,
          47,
          48,
          49,
          50,
          51,
          52,
          53,
          54,
          55,
          56,
          57,
          58,
          59,
          60,
          61,
          62,
          63,
          64,
          65,
          66,
          67,
          68,
          69,
          70,
          71,
          72,
          73,
          74,
          75,
          76,
          77,
          78,
          79,
          80,
          81,
          82,
          83,
          84,
          85,
          86,
          87,
          88,
          89,
          90,
          91,
          92,
          93,
          94,
          95,
          96,
          97,
          98,
          99
         ],
         "xaxis": "x4",
         "y": {
          "bdata": "AABcYQpYoD8A2Deu5zyiPwBochUvEqE/AADIW0HhWj8AEPz8FbShPwA8sY4vn6E/AEDs5RgjoD8ABGAroHigPwCA151w5qE/AHgVMuJEoj8AADBPWAUuPwCwM4VO6KA/AKwlgk3zoT8AAKjp8d0uPwAA7r/pFjI/AIA6Nt8iUj8A8O5gtgSiPwC0QkIo26E/AIhe0MWqoT8AgHmT9vNSPwBUNW77daE/ACA+qIf1oT8AAJ0eQJhUPwAAYiHTIEw/AICZL3J+Uz8AADJ2Qxw1PwCUBDE+4aE/APxaLuZwoD8AAAB2j/wTPwCASpXNglA/AADMmM1XYD8AgHIH7GxUPwAACPQUG1M/AABgiSr4NT8AvMa7olChPwDM6fLyaqA/AMD0SRaioD8A6HPQACyhPwAAB2hCdEA/AFTJhwSCoD8ADFP/GkKhPwDorV+JiKE/AMREAfe1oT8ABOguOX+gPwAQFaBuIqE/ACzi7VRQoT8AXL1fTv+hPwAA9hcsDT8/AOyyqyJZoT8AMJfeXBqhPwCIgk54FqE/AJSlFU26oD8AAIr0i1RVPwAAFO2PSlU/AADACVHY/z4A+JrhXp+gPwC8ABr3WKE/AMgFixhfoT8AKGAqLs6hPwAAsBSwNwg/AJBoiZdKoD8AAJ743FFDPwDcrFNeUKE/ALhxVo87oT8AAGELUFtQPwD4nKhZqaA/AIgik7sSoD8AtJNWttygPwAAfbLYt0g/AADeerHCQD8AGL/JjTihPwBE2ym3raA/ALiWZjOCoD8AAD/qgP5gPwAAYmIEUlo/AABJt+a6Xz8AENLeqf2gPwCATAAsgZ8/AAhuk/hmoT8AZJHVDeKgPwAAQH4Nsyc/APx7SzYzoT8ALGEduAuhPwAAXBbDHD4/AACgihfe8z4AZKmrQiuhPwDA4LosjGA/AGgPCL2loD8AhI2Vis2gPwBoAYpxrJ8/AEDMKh0NoT8A9Mj67N6hPwC4TBaB/aE/AEjAcbLGoD8AwAt8TbugPwAAvEaH3i8/AADFc9DWTj8AALROUyQ8PwAo5pWopaA/ABjHf2+0oT8=",
          "dtype": "f8"
         },
         "yaxis": "y4"
        }
       ],
       "layout": {
        "annotations": [
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Predicción vs Real (Mathematics_final)",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Peso de cada Trimestre (Coeficientes)",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Distribución de Residuos",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Error Absoluto — Test Set",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         }
        ],
        "height": 750,
        "showlegend": false,
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Modelo 4 — Regresión Lineal: Mathematics_final | R²=1.0000  MAE=0.0217"
        },
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis2": {
         "anchor": "y2",
         "domain": [
          0.55,
          1
         ]
        },
        "xaxis3": {
         "anchor": "y3",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis4": {
         "anchor": "y4",
         "domain": [
          0.55,
          1
         ]
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis2": {
         "anchor": "x2",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis3": {
         "anchor": "x3",
         "domain": [
          0,
          0.375
         ]
        },
        "yaxis4": {
         "anchor": "x4",
         "domain": [
          0,
          0.375
         ]
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dashboard serializado correctamente\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Visualización — Modelo 4 (Regresión Lineal: Mathematics_final)\n",
    "\"\"\"\n",
    "\n",
    "residuals_m4  = y_test_m4.values - y_pred_m4\n",
    "\n",
    "bar_colors_m4 = ['#2ECC71' if v >= 0 else '#E74C3C' for v in coef_m4.values]\n",
    "\n",
    "fig_m4 = make_subplots(\n",
    "    rows = 2, cols = 2,\n",
    "    subplot_titles = [\n",
    "        'Predicción vs Real (Mathematics_final)',\n",
    "        'Peso de cada Trimestre (Coeficientes)',\n",
    "        'Distribución de Residuos',\n",
    "        'Error Absoluto — Test Set',\n",
    "    ],\n",
    ")\n",
    "\n",
    "fig_m4.add_trace(\n",
    "\n",
    "    go.Scatter(\n",
    "        x = y_test_m4.values, y = y_pred_m4,\n",
    "        mode = 'markers',\n",
    "        marker = dict(color = '#E67E22', opacity = 0.6),\n",
    "        name = 'Predicciones',\n",
    "    ),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m4.add_trace(\n",
    "\n",
    "    go.Scatter(\n",
    "        x = [y_test_m4.min(), y_test_m4.max()],\n",
    "        y = [y_test_m4.min(), y_test_m4.max()],\n",
    "        mode = 'lines',\n",
    "        line = dict(color = '#E74C3C', dash = 'dash'),\n",
    "        name = 'Perfecto',\n",
    "    ),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m4.add_trace(\n",
    "\n",
    "    go.Bar(x = coef_m4.index.tolist(), y = coef_m4.values, marker_color = bar_colors_m4, name = 'Coef'),\n",
    "\n",
    "    row = 1, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m4.add_trace(\n",
    "\n",
    "    go.Histogram(x = residuals_m4, nbinsx = 20, marker_color = '#1ABC9C', name = 'Residuos'),\n",
    "\n",
    "    row = 2, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m4.add_trace(\n",
    "\n",
    "    go.Bar(\n",
    "        x = list(range(len(y_test_m4))),\n",
    "        y = np.abs(residuals_m4),\n",
    "        marker_color = '#F39C12',\n",
    "        name = 'Error Absoluto',\n",
    "    ),\n",
    "\n",
    "    row = 2, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m4.update_layout(\n",
    "\n",
    "    title_text = f'Modelo 4 — Regresión Lineal: Mathematics_final | R²={r2_m4:.4f}  MAE={mae_m4:.4f}',\n",
    "    height = 750,\n",
    "    showlegend = False,\n",
    "\n",
    ")\n",
    "\n",
    "with open('dashboard_ml_math_final.json', 'w') as f:\n",
    "    json.dump(json.loads(fig_m4.to_json()), f)\n",
    "\n",
    "fig_m4.show(renderer = 'plotly_mimetype')\n",
    "\n",
    "print('Dashboard serializado correctamente')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "c37bf5e9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Odds ratios clase Low:\n",
      "  term1_avg                           0.7737\n",
      "  attendance_absences_total           0.9750\n",
      "  failed_subjects                     0.7885\n",
      "  grade_tendency_encoded              0.8412\n",
      "  age                                 0.9880\n",
      "  gender_encoded                      0.9267\n",
      "\n",
      "Odds ratios clase Medium:\n",
      "  term1_avg                           0.9292\n",
      "  attendance_absences_total           0.9599\n",
      "  failed_subjects                     0.8874\n",
      "  grade_tendency_encoded              1.0171\n",
      "  age                                 1.0528\n",
      "  gender_encoded                      1.0237\n",
      "\n",
      "Odds ratios clase High:\n",
      "  term1_avg                           1.3909\n",
      "  attendance_absences_total           1.0684\n",
      "  failed_subjects                     1.4291\n",
      "  grade_tendency_encoded              1.1688\n",
      "  age                                 0.9614\n",
      "  gender_encoded                      1.0542\n",
      "\n",
      "Accuracy:      0.5000\n",
      "CV Acc 5-fold: 0.5340 ± 0.0049\n",
      "\n",
      "Reporte de clasificación:\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "         Low       0.00      0.00      0.00        26\n",
      "      Medium       0.50      1.00      0.67        50\n",
      "        High       0.00      0.00      0.00        24\n",
      "\n",
      "    accuracy                           0.50       100\n",
      "   macro avg       0.17      0.33      0.22       100\n",
      "weighted avg       0.25      0.50      0.33       100\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/metrics/_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/metrics/_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/metrics/_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Modelo 5 — Regresión Logística Multinomial: Predicción de support_level\n",
    "\n",
    "Predice qué nivel de soporte (Low / Medium / High) debería asignarse\n",
    "a un estudiante usando su rendimiento en el primer trimestre y asistencia.\n",
    "Extrae la probabilidad de cada una de las tres clases.\n",
    "\"\"\"\n",
    "\n",
    "FEATURES_M5 = [\n",
    "    'term1_avg',\n",
    "    'attendance_absences_total',\n",
    "    'failed_subjects',\n",
    "    'grade_tendency_encoded',\n",
    "    'age',\n",
    "    'gender_encoded',\n",
    "]\n",
    "\n",
    "TARGET_M5      = 'support_level_num'\n",
    "SUPPORT_LABELS = ['Low', 'Medium', 'High']\n",
    "\n",
    "X_m5 = df[FEATURES_M5]\n",
    "y_m5 = df[TARGET_M5]\n",
    "\n",
    "X_train_m5, X_test_m5, y_train_m5, y_test_m5 = train_test_split(\n",
    "    X_m5, y_m5, test_size = 0.2, random_state = 42\n",
    ")\n",
    "\n",
    "scaler_m5     = StandardScaler()\n",
    "X_train_m5_sc = scaler_m5.fit_transform(X_train_m5)\n",
    "X_test_m5_sc  = scaler_m5.transform(X_test_m5)\n",
    "\n",
    "model_m5 = LogisticRegression(solver = 'lbfgs', max_iter = 1000, random_state = 42)\n",
    "model_m5.fit(X_train_m5_sc, y_train_m5)\n",
    "\n",
    "y_pred_m5  = model_m5.predict(X_test_m5_sc)\n",
    "y_proba_m5 = model_m5.predict_proba(X_test_m5_sc)\n",
    "\n",
    "cv_pipe_m5 = Pipeline([\n",
    "    ('sc',  StandardScaler()),\n",
    "    ('clf', LogisticRegression(solver = 'lbfgs', max_iter = 1000, random_state = 42)),\n",
    "])\n",
    "\n",
    "cv_acc_m5 = cross_val_score(cv_pipe_m5, X_m5, y_m5, cv = 5, scoring = 'accuracy')\n",
    "\n",
    "acc_m5 = accuracy_score(y_test_m5, y_pred_m5)\n",
    "\n",
    "# Coeficientes por clase\n",
    "\n",
    "for i, label in enumerate(SUPPORT_LABELS):\n",
    "\n",
    "    odds_class = np.exp(model_m5.coef_[i])\n",
    "\n",
    "    print(f'Odds ratios clase {label}:')\n",
    "\n",
    "    for feat, odd in zip(FEATURES_M5, odds_class):\n",
    "\n",
    "        print(f'  {feat:<35} {odd:.4f}')\n",
    "\n",
    "    print()\n",
    "\n",
    "print(f'Accuracy:      {acc_m5:.4f}')\n",
    "print(f'CV Acc 5-fold: {cv_acc_m5.mean():.4f} ± {cv_acc_m5.std():.4f}')\n",
    "\n",
    "print('\\nReporte de clasificación:')\n",
    "\n",
    "print(classification_report(y_test_m5, y_pred_m5, target_names = SUPPORT_LABELS))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "05ab2057",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Estudiantes posiblemente infra-soportados (P(High) > 0.4, soporte actual < High): 5\n",
      "student_id real_support  prob_low  prob_medium  prob_high  term1_avg  failed_subjects\n",
      "   EA00429       Medium    0.1390       0.3637     0.4973      54.29                6\n",
      "   EA00180       Medium    0.1112       0.4247     0.4641      65.14                3\n",
      "   EA00104       Medium    0.1175       0.4278     0.4547      54.16                6\n",
      "   EA00269       Medium    0.1574       0.4218     0.4208      52.99                6\n",
      "   EA00032       Medium    0.1284       0.4551     0.4165      71.45                0\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Insights — Modelo 5: Estudiantes que deberían recibir soporte High\n",
    "\"\"\"\n",
    "\n",
    "# Probabilidades para todos los estudiantes\n",
    "\n",
    "X_all_m5_sc  = scaler_m5.transform(X_m5)\n",
    "\n",
    "proba_all_m5 = model_m5.predict_proba(X_all_m5_sc)\n",
    "\n",
    "df_support_m5 = pd.DataFrame({\n",
    "    'student_id':     df['student_id'].values,\n",
    "    'real_support':   df['support_level'].values,\n",
    "    'prob_low':       proba_all_m5[:, 0].round(4),\n",
    "    'prob_medium':    proba_all_m5[:, 1].round(4),\n",
    "    'prob_high':      proba_all_m5[:, 2].round(4),\n",
    "    'predicted':      [SUPPORT_LABELS[p] for p in model_m5.predict(X_all_m5_sc)],\n",
    "    'term1_avg':      df['term1_avg'].values.round(2),\n",
    "    'failed_subjects': df['failed_subjects'].values,\n",
    "})\n",
    "\n",
    "# Estudiantes con alta probabilidad de necesitar soporte High pero asignados como Low/Medium\n",
    "\n",
    "df_underserved = df_support_m5[\n",
    "    (df_support_m5['prob_high'] > 0.4) &\n",
    "    (df_support_m5['real_support'].isin(['Low', 'Medium']))\n",
    "].sort_values('prob_high', ascending = False)\n",
    "\n",
    "print(f'Estudiantes posiblemente infra-soportados (P(High) > 0.4, soporte actual < High): {len(df_underserved)}')\n",
    "\n",
    "print(df_underserved[['student_id', 'real_support', 'prob_low', 'prob_medium', 'prob_high', 'term1_avg', 'failed_subjects']].head(15).to_string(index = False))\n",
    "\n",
    "# Exportar predicciones de soporte\n",
    "\n",
    "df_support_m5.to_csv('../datasets/support_level_predictions.csv', index = False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "a1a4bbc6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "marker": {
          "color": "#E74C3C"
         },
         "name": "P(Low)",
         "nbinsx": 20,
         "type": "histogram",
         "x": {
          "bdata": "[encoded data, 5336 characters]",
          "dtype": "f8"
         },
         "xaxis": "x",
         "yaxis": "y"
        },
        {
         "marker": {
          "color": "#2ECC71"
         },
         "name": "P(High)",
         "nbinsx": 20,
         "type": "histogram",
         "x": {
          "bdata": "[encoded data, 5336 characters]",
          "dtype": "f8"
         },
         "xaxis": "x2",
         "yaxis": "y2"
        },
        {
         "colorscale": [
          [
           0,
           "rgb(247,252,245)"
          ],
          [
           0.125,
           "rgb(229,245,224)"
          ],
          [
           0.25,
           "rgb(199,233,192)"
          ],
          [
           0.375,
           "rgb(161,217,155)"
          ],
          [
           0.5,
           "rgb(116,196,118)"
          ],
          [
           0.625,
           "rgb(65,171,93)"
          ],
          [
           0.75,
           "rgb(35,139,69)"
          ],
          [
           0.875,
           "rgb(0,109,44)"
          ],
          [
           1,
           "rgb(0,68,27)"
          ]
         ],
         "showscale": false,
         "text": {
          "bdata": "ABoAADIAABgA",
          "dtype": "i1",
          "shape": "3, 3"
         },
         "texttemplate": "%{text}",
         "type": "heatmap",
         "x": [
          "Pred Low",
          "Pred Medium",
          "Pred High"
         ],
         "xaxis": "x3",
         "y": [
          "Real Low",
          "Real Medium",
          "Real High"
         ],
         "yaxis": "y3",
         "z": {
          "bdata": "ABoAADIAABgA",
          "dtype": "i1",
          "shape": "3, 3"
         }
        },
        {
         "marker": {
          "color": "#F39C12"
         },
         "name": "P(Medium)",
         "nbinsx": 20,
         "type": "histogram",
         "x": {
          "bdata": "[encoded data, 5336 characters]",
          "dtype": "f8"
         },
         "xaxis": "x4",
         "yaxis": "y4"
        }
       ],
       "layout": {
        "annotations": [
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Distribución P(Low) — Todos los estudiantes",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Distribución P(High) — Todos los estudiantes",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Matriz de Confusión",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Distribución P(Medium)",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.375,
          "yanchor": "bottom",
          "yref": "paper"
         }
        ],
        "height": 750,
        "showlegend": false,
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Modelo 5 — Logística Multinomial: support_level | Acc=0.5000"
        },
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis2": {
         "anchor": "y2",
         "domain": [
          0.55,
          1
         ]
        },
        "xaxis3": {
         "anchor": "y3",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis4": {
         "anchor": "y4",
         "domain": [
          0.55,
          1
         ]
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis2": {
         "anchor": "x2",
         "domain": [
          0.625,
          1
         ]
        },
        "yaxis3": {
         "anchor": "x3",
         "domain": [
          0,
          0.375
         ]
        },
        "yaxis4": {
         "anchor": "x4",
         "domain": [
          0,
          0.375
         ]
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dashboard serializado correctamente\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Visualización — Modelo 5 (Regresión Logística Multinomial: support_level)\n",
    "\"\"\"\n",
    "\n",
    "cm_m5 = confusion_matrix(y_test_m5, y_pred_m5)\n",
    "\n",
    "fig_m5 = make_subplots(\n",
    "    rows = 2, cols = 2,\n",
    "    subplot_titles = [\n",
    "        'Distribución P(Low) — Todos los estudiantes',\n",
    "        'Distribución P(High) — Todos los estudiantes',\n",
    "        'Matriz de Confusión',\n",
    "        'Distribución P(Medium)',\n",
    "    ],\n",
    ")\n",
    "\n",
    "fig_m5.add_trace(\n",
    "\n",
    "    go.Histogram(x = proba_all_m5[:, 0], nbinsx = 20, marker_color = '#E74C3C', name = 'P(Low)'),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m5.add_trace(\n",
    "\n",
    "    go.Histogram(x = proba_all_m5[:, 2], nbinsx = 20, marker_color = '#2ECC71', name = 'P(High)'),\n",
    "\n",
    "    row = 1, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m5.add_trace(\n",
    "\n",
    "    go.Heatmap(\n",
    "        z = cm_m5,\n",
    "        x = [f'Pred {l}' for l in SUPPORT_LABELS],\n",
    "        y = [f'Real {l}' for l in SUPPORT_LABELS],\n",
    "        colorscale = 'Greens',\n",
    "        showscale  = False,\n",
    "        text       = cm_m5,\n",
    "        texttemplate = '%{text}',\n",
    "    ),\n",
    "\n",
    "    row = 2, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_m5.add_trace(\n",
    "\n",
    "    go.Histogram(x = proba_all_m5[:, 1], nbinsx = 20, marker_color = '#F39C12', name = 'P(Medium)'),\n",
    "\n",
    "    row = 2, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_m5.update_layout(\n",
    "\n",
    "    title_text = f'Modelo 5 — Logística Multinomial: support_level | Acc={acc_m5:.4f}',\n",
    "    height = 750,\n",
    "    showlegend = False,\n",
    "\n",
    ")\n",
    "\n",
    "with open('dashboard_ml_support_level.json', 'w') as f:\n",
    "    json.dump(json.loads(fig_m5.to_json()), f)\n",
    "\n",
    "fig_m5.show(renderer = 'plotly_mimetype')\n",
    "\n",
    "print('Dashboard serializado correctamente')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "12529bff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Modelos serializados en ../models-pkl/\n",
      "  linear_avg_final.pkl\n",
      "  linear_mathematics_final.pkl\n",
      "  logistic_grade_tendency.pkl\n",
      "  logistic_mathematics_pass.pkl\n",
      "  logistic_support_level.pkl\n",
      "  math_risk_scores.csv\n",
      "  scaler_grade_tendency.pkl\n",
      "  scaler_mathematics_pass.pkl\n",
      "  scaler_support_level.pkl\n",
      "  support_level_predictions.csv\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Serialización de Modelos\n",
    "\"\"\"\n",
    "\n",
    "os.makedirs('../models-pkl', exist_ok = True)\n",
    "\n",
    "# Modelos de regresión (sin scaler, no es necesario para OLS)\n",
    "\n",
    "joblib.dump(model_m1, '../models-pkl/linear_avg_final.pkl')\n",
    "\n",
    "joblib.dump(model_m4, '../models-pkl/linear_mathematics_final.pkl')\n",
    "\n",
    "# Modelos logísticos (guardados con su scaler como tupla)\n",
    "\n",
    "joblib.dump((scaler_m2, model_m2), '../models-pkl/logistic_grade_tendency.pkl')\n",
    "\n",
    "joblib.dump((scaler_m3, model_m3), '../models-pkl/logistic_mathematics_pass.pkl')\n",
    "\n",
    "joblib.dump((scaler_m5, model_m5), '../models-pkl/logistic_support_level.pkl')\n",
    "\n",
    "# Scalers por separado (por si se necesitan independientemente)\n",
    "\n",
    "joblib.dump(scaler_m2, '../models-pkl/scaler_grade_tendency.pkl')\n",
    "\n",
    "joblib.dump(scaler_m3, '../models-pkl/scaler_mathematics_pass.pkl')\n",
    "\n",
    "joblib.dump(scaler_m5, '../models-pkl/scaler_support_level.pkl')\n",
    "\n",
    "print('Modelos serializados en ../models-pkl/')\n",
    "\n",
    "for fname in sorted(os.listdir('../models-pkl/')):\n",
    "\n",
    "    print(f'  {fname}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "1510aae1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Resumen de métricas:\n",
      "                      modelo          tipo     R2    MAE  Acc    AUC\n",
      "         M1 Linear avg_final     Regresión 0.9228 0.9877  NaN    NaN\n",
      "        M4 Linear Math_final     Regresión 1.0000 0.0217  NaN    NaN\n",
      "  M2 Logistic grade_tendency Clasificación    NaN    NaN 0.93 0.9935\n",
      "  M3 Logistic Math Pass/Fail Clasificación    NaN    NaN 0.97 0.9976\n",
      "M5 Multinomial support_level Clasificación    NaN    NaN 0.50    NaN\n"
     ]
    },
    {
     "data": {
      "application/vnd.plotly.v1+json": {
       "config": {
        "plotlyServerURL": "https://plot.ly"
       },
       "data": [
        {
         "marker": {
          "color": "#4C9BE8"
         },
         "name": "R²",
         "type": "bar",
         "x": [
          "M1 Linear\navg_final",
          "M4 Linear\nMath_final"
         ],
         "xaxis": "x",
         "y": [
          0.9227614003642094,
          0.9999863942283865
         ],
         "yaxis": "y"
        },
        {
         "marker": {
          "color": "#2ECC71"
         },
         "name": "Accuracy",
         "type": "bar",
         "x": [
          "M2 Logistic\ngrade_tendency",
          "M3 Logistic\nMath Pass/Fail",
          "M5 Multinomial\nsupport_level"
         ],
         "xaxis": "x2",
         "y": [
          0.93,
          0.97,
          0.5
         ],
         "yaxis": "y2"
        },
        {
         "marker": {
          "color": "#F39C12"
         },
         "name": "MAE",
         "type": "bar",
         "x": [
          "M1 Linear\navg_final",
          "M4 Linear\nMath_final"
         ],
         "xaxis": "x3",
         "y": [
          0.9876913458133582,
          0.02171426400943595
         ],
         "yaxis": "y3"
        },
        {
         "marker": {
          "color": "#E67E22"
         },
         "name": "RMSE",
         "type": "bar",
         "x": [
          "M1 Linear\navg_final",
          "M4 Linear\nMath_final"
         ],
         "xaxis": "x3",
         "y": [
          1.245101863276452,
          0.02678495256950178
         ],
         "yaxis": "y3"
        },
        {
         "marker": {
          "color": "#9B59B6"
         },
         "name": "AUC",
         "type": "bar",
         "x": [
          "M2 Logistic\ngrade_tendency",
          "M3 Logistic\nMath Pass/Fail"
         ],
         "xaxis": "x4",
         "y": [
          0.9934548854604955,
          0.9976470588235294
         ],
         "yaxis": "y4"
        },
        {
         "line": {
          "color": "#9B59B6"
         },
         "mode": "lines",
         "name": "M2 (AUC=0.993)",
         "type": "scatter",
         "x": {
          "bdata": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhBBCCCGEoD+EEEIIIYSgP8YYY4wxxrg/xhhjjDHGuD+EEEIIIYTAP4QQQgghhMA/AAAAAAAA8D8=",
          "dtype": "f8"
         },
         "xaxis": "x5",
         "y": {
          "bdata": "AAAAAAAAAADbgbl2YK6NP9Ob3vSmN+0/05ve9KY37T/qTW9605vuP+pNb3rTm+4/8TNK/IwS7z/xM0r8jBLvPwAAAAAAAPA/AAAAAAAA8D8=",
          "dtype": "f8"
         },
         "yaxis": "y5"
        },
        {
         "line": {
          "color": "#E74C3C"
         },
         "mode": "lines",
         "name": "M3 (AUC=0.998)",
         "type": "scatter",
         "x": {
          "bdata": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERERERERsT8RERERERGxPxEREREREcE/ERERERERwT8AAAAAAADwPw==",
          "dtype": "f8"
         },
         "xaxis": "x5",
         "y": {
          "bdata": "AAAAAAAAAAAYGBgYGBiIPz8/Pz8/P+8/Pz8/Pz8/7z+gn5+fn5/vP6Cfn5+fn+8/AAAAAAAA8D8AAAAAAADwPw==",
          "dtype": "f8"
         },
         "yaxis": "y5"
        },
        {
         "line": {
          "color": "#95A5A6",
          "dash": "dash"
         },
         "mode": "lines",
         "name": "Random",
         "type": "scatter",
         "x": [
          0,
          1
         ],
         "xaxis": "x5",
         "y": [
          0,
          1
         ],
         "yaxis": "y5"
        },
        {
         "error_y": {
          "array": [
           0.01720465053408527,
           0.011661903789690611,
           0.00489897948556636
          ],
          "type": "data",
          "visible": true
         },
         "marker": {
          "color": "#1ABC9C"
         },
         "name": "CV Accuracy",
         "type": "bar",
         "x": [
          "M2 Logistic\ngrade_tendency",
          "M3 Logistic\nMath Pass/Fail",
          "M5 Multinomial\nsupport_level"
         ],
         "xaxis": "x6",
         "y": [
          0.968,
          0.982,
          0.534
         ],
         "yaxis": "y6"
        }
       ],
       "layout": {
        "annotations": [
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "R² — Modelos de Regresión",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Accuracy — Modelos de Clasificación",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 1,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "MAE y RMSE — Modelos de Regresión",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.6111111111111112,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "ROC-AUC — Modelos de Clasificación (binarios)",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.6111111111111112,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "Curvas ROC (M2 y M3)",
          "x": 0.225,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.22222222222222224,
          "yanchor": "bottom",
          "yref": "paper"
         },
         {
          "font": {
           "size": 16
          },
          "showarrow": false,
          "text": "CV Accuracy ± std — Modelos de Clasificación",
          "x": 0.775,
          "xanchor": "center",
          "xref": "paper",
          "y": 0.22222222222222224,
          "yanchor": "bottom",
          "yref": "paper"
         }
        ],
        "barmode": "group",
        "height": 1100,
        "showlegend": true,
        "template": {
         "data": {
          "bar": [
           {
            "error_x": {
             "color": "#2a3f5f"
            },
            "error_y": {
             "color": "#2a3f5f"
            },
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "bar"
           }
          ],
          "barpolar": [
           {
            "marker": {
             "line": {
              "color": "#E5ECF6",
              "width": 0.5
             },
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "barpolar"
           }
          ],
          "carpet": [
           {
            "aaxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "baxis": {
             "endlinecolor": "#2a3f5f",
             "gridcolor": "white",
             "linecolor": "white",
             "minorgridcolor": "white",
             "startlinecolor": "#2a3f5f"
            },
            "type": "carpet"
           }
          ],
          "choropleth": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "choropleth"
           }
          ],
          "contour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "contour"
           }
          ],
          "contourcarpet": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "contourcarpet"
           }
          ],
          "heatmap": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "heatmap"
           }
          ],
          "histogram": [
           {
            "marker": {
             "pattern": {
              "fillmode": "overlay",
              "size": 10,
              "solidity": 0.2
             }
            },
            "type": "histogram"
           }
          ],
          "histogram2d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2d"
           }
          ],
          "histogram2dcontour": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "histogram2dcontour"
           }
          ],
          "mesh3d": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "type": "mesh3d"
           }
          ],
          "parcoords": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "parcoords"
           }
          ],
          "pie": [
           {
            "automargin": true,
            "type": "pie"
           }
          ],
          "scatter": [
           {
            "fillpattern": {
             "fillmode": "overlay",
             "size": 10,
             "solidity": 0.2
            },
            "type": "scatter"
           }
          ],
          "scatter3d": [
           {
            "line": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatter3d"
           }
          ],
          "scattercarpet": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattercarpet"
           }
          ],
          "scattergeo": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergeo"
           }
          ],
          "scattergl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattergl"
           }
          ],
          "scattermap": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermap"
           }
          ],
          "scattermapbox": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scattermapbox"
           }
          ],
          "scatterpolar": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolar"
           }
          ],
          "scatterpolargl": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterpolargl"
           }
          ],
          "scatterternary": [
           {
            "marker": {
             "colorbar": {
              "outlinewidth": 0,
              "ticks": ""
             }
            },
            "type": "scatterternary"
           }
          ],
          "surface": [
           {
            "colorbar": {
             "outlinewidth": 0,
             "ticks": ""
            },
            "colorscale": [
             [
              0,
              "#0d0887"
             ],
             [
              0.1111111111111111,
              "#46039f"
             ],
             [
              0.2222222222222222,
              "#7201a8"
             ],
             [
              0.3333333333333333,
              "#9c179e"
             ],
             [
              0.4444444444444444,
              "#bd3786"
             ],
             [
              0.5555555555555556,
              "#d8576b"
             ],
             [
              0.6666666666666666,
              "#ed7953"
             ],
             [
              0.7777777777777778,
              "#fb9f3a"
             ],
             [
              0.8888888888888888,
              "#fdca26"
             ],
             [
              1,
              "#f0f921"
             ]
            ],
            "type": "surface"
           }
          ],
          "table": [
           {
            "cells": {
             "fill": {
              "color": "#EBF0F8"
             },
             "line": {
              "color": "white"
             }
            },
            "header": {
             "fill": {
              "color": "#C8D4E3"
             },
             "line": {
              "color": "white"
             }
            },
            "type": "table"
           }
          ]
         },
         "layout": {
          "annotationdefaults": {
           "arrowcolor": "#2a3f5f",
           "arrowhead": 0,
           "arrowwidth": 1
          },
          "autotypenumbers": "strict",
          "coloraxis": {
           "colorbar": {
            "outlinewidth": 0,
            "ticks": ""
           }
          },
          "colorscale": {
           "diverging": [
            [
             0,
             "#8e0152"
            ],
            [
             0.1,
             "#c51b7d"
            ],
            [
             0.2,
             "#de77ae"
            ],
            [
             0.3,
             "#f1b6da"
            ],
            [
             0.4,
             "#fde0ef"
            ],
            [
             0.5,
             "#f7f7f7"
            ],
            [
             0.6,
             "#e6f5d0"
            ],
            [
             0.7,
             "#b8e186"
            ],
            [
             0.8,
             "#7fbc41"
            ],
            [
             0.9,
             "#4d9221"
            ],
            [
             1,
             "#276419"
            ]
           ],
           "sequential": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ],
           "sequentialminus": [
            [
             0,
             "#0d0887"
            ],
            [
             0.1111111111111111,
             "#46039f"
            ],
            [
             0.2222222222222222,
             "#7201a8"
            ],
            [
             0.3333333333333333,
             "#9c179e"
            ],
            [
             0.4444444444444444,
             "#bd3786"
            ],
            [
             0.5555555555555556,
             "#d8576b"
            ],
            [
             0.6666666666666666,
             "#ed7953"
            ],
            [
             0.7777777777777778,
             "#fb9f3a"
            ],
            [
             0.8888888888888888,
             "#fdca26"
            ],
            [
             1,
             "#f0f921"
            ]
           ]
          },
          "colorway": [
           "#636efa",
           "#EF553B",
           "#00cc96",
           "#ab63fa",
           "#FFA15A",
           "#19d3f3",
           "#FF6692",
           "#B6E880",
           "#FF97FF",
           "#FECB52"
          ],
          "font": {
           "color": "#2a3f5f"
          },
          "geo": {
           "bgcolor": "white",
           "lakecolor": "white",
           "landcolor": "#E5ECF6",
           "showlakes": true,
           "showland": true,
           "subunitcolor": "white"
          },
          "hoverlabel": {
           "align": "left"
          },
          "hovermode": "closest",
          "mapbox": {
           "style": "light"
          },
          "paper_bgcolor": "white",
          "plot_bgcolor": "#E5ECF6",
          "polar": {
           "angularaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "radialaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "scene": {
           "xaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "yaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           },
           "zaxis": {
            "backgroundcolor": "#E5ECF6",
            "gridcolor": "white",
            "gridwidth": 2,
            "linecolor": "white",
            "showbackground": true,
            "ticks": "",
            "zerolinecolor": "white"
           }
          },
          "shapedefaults": {
           "line": {
            "color": "#2a3f5f"
           }
          },
          "ternary": {
           "aaxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "baxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           },
           "bgcolor": "#E5ECF6",
           "caxis": {
            "gridcolor": "white",
            "linecolor": "white",
            "ticks": ""
           }
          },
          "title": {
           "x": 0.05
          },
          "xaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          },
          "yaxis": {
           "automargin": true,
           "gridcolor": "white",
           "linecolor": "white",
           "ticks": "",
           "title": {
            "standoff": 15
           },
           "zerolinecolor": "white",
           "zerolinewidth": 2
          }
         }
        },
        "title": {
         "text": "Dashboard Consolidado — Modelos de Machine Learning"
        },
        "xaxis": {
         "anchor": "y",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis2": {
         "anchor": "y2",
         "domain": [
          0.55,
          1
         ]
        },
        "xaxis3": {
         "anchor": "y3",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis4": {
         "anchor": "y4",
         "domain": [
          0.55,
          1
         ]
        },
        "xaxis5": {
         "anchor": "y5",
         "domain": [
          0,
          0.45
         ]
        },
        "xaxis6": {
         "anchor": "y6",
         "domain": [
          0.55,
          1
         ]
        },
        "yaxis": {
         "anchor": "x",
         "domain": [
          0.7777777777777778,
          1
         ]
        },
        "yaxis2": {
         "anchor": "x2",
         "domain": [
          0.7777777777777778,
          1
         ]
        },
        "yaxis3": {
         "anchor": "x3",
         "domain": [
          0.3888888888888889,
          0.6111111111111112
         ]
        },
        "yaxis4": {
         "anchor": "x4",
         "domain": [
          0.3888888888888889,
          0.6111111111111112
         ]
        },
        "yaxis5": {
         "anchor": "x5",
         "domain": [
          0,
          0.22222222222222224
         ]
        },
        "yaxis6": {
         "anchor": "x6",
         "domain": [
          0,
          0.22222222222222224
         ]
        }
       }
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dashboard consolidado serializado correctamente\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Dashboard Consolidado de Métricas ML\n",
    "\"\"\"\n",
    "\n",
    "model_names_reg = ['M1 Linear\\navg_final', 'M4 Linear\\nMath_final']\n",
    "r2_values       = [r2_m1, r2_m4]\n",
    "mae_values      = [mae_m1, mae_m4]\n",
    "rmse_values     = [rmse_m1, rmse_m4]\n",
    "\n",
    "model_names_clf = ['M2 Logistic\\ngrade_tendency', 'M3 Logistic\\nMath Pass/Fail', 'M5 Multinomial\\nsupport_level']\n",
    "acc_values      = [acc_m2, acc_m3, acc_m5]\n",
    "cv_acc_means    = [cv_acc_m2.mean(), cv_acc_m3.mean(), cv_acc_m5.mean()]\n",
    "cv_acc_stds     = [cv_acc_m2.std(),  cv_acc_m3.std(),  cv_acc_m5.std()]\n",
    "\n",
    "# Tabla resumen de métricas\n",
    "\n",
    "summary = pd.DataFrame([\n",
    "    {'modelo': 'M1 Linear avg_final',        'tipo': 'Regresión',       'R2': round(r2_m1, 4),  'MAE': round(mae_m1, 4),  'Acc': None,           'AUC': None},\n",
    "    {'modelo': 'M4 Linear Math_final',        'tipo': 'Regresión',       'R2': round(r2_m4, 4),  'MAE': round(mae_m4, 4),  'Acc': None,           'AUC': None},\n",
    "    {'modelo': 'M2 Logistic grade_tendency',  'tipo': 'Clasificación',   'R2': None,             'MAE': None,              'Acc': round(acc_m2, 4), 'AUC': round(auc_m2, 4)},\n",
    "    {'modelo': 'M3 Logistic Math Pass/Fail',  'tipo': 'Clasificación',   'R2': None,             'MAE': None,              'Acc': round(acc_m3, 4), 'AUC': round(auc_m3, 4)},\n",
    "    {'modelo': 'M5 Multinomial support_level','tipo': 'Clasificación',   'R2': None,             'MAE': None,              'Acc': round(acc_m5, 4), 'AUC': None},\n",
    "])\n",
    "\n",
    "print('Resumen de métricas:')\n",
    "\n",
    "print(summary.to_string(index = False))\n",
    "\n",
    "fig_dash = make_subplots(\n",
    "    rows = 3, cols = 2,\n",
    "    subplot_titles = [\n",
    "        'R² — Modelos de Regresión',\n",
    "        'Accuracy — Modelos de Clasificación',\n",
    "        'MAE y RMSE — Modelos de Regresión',\n",
    "        'ROC-AUC — Modelos de Clasificación (binarios)',\n",
    "        'Curvas ROC (M2 y M3)',\n",
    "        'CV Accuracy ± std — Modelos de Clasificación',\n",
    "    ],\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Bar(x = model_names_reg, y = r2_values, marker_color = '#4C9BE8', name = 'R²'),\n",
    "\n",
    "    row = 1, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Bar(x = model_names_clf, y = acc_values, marker_color = '#2ECC71', name = 'Accuracy'),\n",
    "\n",
    "    row = 1, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Bar(x = model_names_reg, y = mae_values, name = 'MAE', marker_color = '#F39C12'),\n",
    "\n",
    "    row = 2, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Bar(x = model_names_reg, y = rmse_values, name = 'RMSE', marker_color = '#E67E22'),\n",
    "\n",
    "    row = 2, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Bar(\n",
    "        x = [model_names_clf[0], model_names_clf[1]],\n",
    "        y = [auc_m2, auc_m3],\n",
    "        marker_color = '#9B59B6',\n",
    "        name = 'AUC',\n",
    "    ),\n",
    "\n",
    "    row = 2, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Scatter(x = fpr_m2, y = tpr_m2, mode = 'lines', line = dict(color = '#9B59B6'), name = f'M2 (AUC={auc_m2:.3f})'),\n",
    "\n",
    "    row = 3, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Scatter(x = fpr_m3, y = tpr_m3, mode = 'lines', line = dict(color = '#E74C3C'), name = f'M3 (AUC={auc_m3:.3f})'),\n",
    "\n",
    "    row = 3, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Scatter(x = [0, 1], y = [0, 1], mode = 'lines', line = dict(color = '#95A5A6', dash = 'dash'), name = 'Random'),\n",
    "\n",
    "    row = 3, col = 1\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.add_trace(\n",
    "\n",
    "    go.Bar(\n",
    "        x = model_names_clf,\n",
    "        y = cv_acc_means,\n",
    "        error_y = dict(type = 'data', array = cv_acc_stds, visible = True),\n",
    "        marker_color = '#1ABC9C',\n",
    "        name = 'CV Accuracy',\n",
    "    ),\n",
    "\n",
    "    row = 3, col = 2\n",
    "\n",
    ")\n",
    "\n",
    "fig_dash.update_layout(\n",
    "\n",
    "    title_text = 'Dashboard Consolidado — Modelos de Machine Learning',\n",
    "    height     = 1100,\n",
    "    barmode    = 'group',\n",
    "    showlegend = True,\n",
    "\n",
    ")\n",
    "\n",
    "with open('dashboard_ml_models.json', 'w') as f:\n",
    "    json.dump(json.loads(fig_dash.to_json()), f)\n",
    "\n",
    "fig_dash.show(renderer = 'plotly_mimetype')\n",
    "\n",
    "print('Dashboard consolidado serializado correctamente')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "b7663b64",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Predicción de ejemplo (Female, 16 años, Secondary 3):\n",
      "  avg_final_pred                 61.965\n",
      "  grade_tendency_pred            Uptrend\n",
      "  prob_uptrend                   0.9891\n",
      "  prob_downtrend                 0.0109\n",
      "  mathematics_status_pred        Pass\n",
      "  prob_math_pass                 1.0\n",
      "  prob_math_fail                 0.0\n",
      "  mathematics_final_pred         67.665\n",
      "  support_level_pred             Medium\n",
      "  prob_support_low               0.2299\n",
      "  prob_support_medium            0.5682\n",
      "  prob_support_high              0.2019\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/utils/validation.py:2691: UserWarning: X does not have valid feature names, but LinearRegression was fitted with feature names\n",
      "  warnings.warn(\n",
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/utils/validation.py:2691: UserWarning: X does not have valid feature names, but StandardScaler was fitted with feature names\n",
      "  warnings.warn(\n",
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/utils/validation.py:2691: UserWarning: X does not have valid feature names, but StandardScaler was fitted with feature names\n",
      "  warnings.warn(\n",
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/utils/validation.py:2691: UserWarning: X does not have valid feature names, but LinearRegression was fitted with feature names\n",
      "  warnings.warn(\n",
      "/Users/diqueran1/Desktop/Universidad/2do año/2do Cuatri/Open Data II/Proyecto Final/internal/internal_venv/lib/python3.13/site-packages/sklearn/utils/validation.py:2691: UserWarning: X does not have valid feature names, but StandardScaler was fitted with feature names\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "\"\"\"\n",
    "Función de Predicción para Nuevos Estudiantes\n",
    "\"\"\"\n",
    "\n",
    "def predict_student(\n",
    "    age:                       int,\n",
    "    gender:                    str,\n",
    "    course:                    str,\n",
    "    attendance_absences_total: int,\n",
    "    support_level:             str,\n",
    "    total_subjects:            int,\n",
    "    math_term1:                float,\n",
    "    math_term2:                float,\n",
    "    math_term3:                float,\n",
    "    term1_avg:                 float,\n",
    "    term2_avg:                 float,\n",
    "    term3_avg:                 float,\n",
    "    failed_subjects:           int   = 0,\n",
    "    grade_tendency:            str   = 'Uptrend',\n",
    ") -> dict:\n",
    "\n",
    "    course_map_local = {\n",
    "        'Primary 6':   0, 'Secondary 1': 1, 'Secondary 2': 2, 'Secondary 3': 3,\n",
    "        'Secondary 4': 4, 'Secondary 5': 5, 'Secondary 6': 6,\n",
    "    }\n",
    "\n",
    "    support_map_local = {'Low': 0, 'Medium': 1, 'High': 2}\n",
    "\n",
    "    gender_enc   = 1 if gender == 'M' else 0\n",
    "    course_enc   = course_map_local.get(course, 3)\n",
    "    support_enc  = support_map_local.get(support_level, 1)\n",
    "    tendency_enc = 1 if grade_tendency == 'Uptrend' else 0\n",
    "\n",
    "    # Modelo 1: avg_final\n",
    "\n",
    "    x1       = np.array([[age, gender_enc, course_enc, attendance_absences_total, support_enc, total_subjects, term1_avg]])\n",
    "    avg_pred = float(model_m1.predict(x1)[0])\n",
    "\n",
    "    # Modelo 2: grade_tendency\n",
    "\n",
    "    x2         = np.array([[term1_avg, term2_avg, term3_avg, attendance_absences_total, support_enc, age, gender_enc]])\n",
    "    x2_sc      = scaler_m2.transform(x2)\n",
    "    tend_pred  = model_m2.predict(x2_sc)[0]\n",
    "    tend_proba = model_m2.predict_proba(x2_sc)[0]\n",
    "\n",
    "    # Modelo 3: Mathematics Pass/Fail\n",
    "\n",
    "    x3         = np.array([[math_term1, math_term2, math_term3, attendance_absences_total, support_enc]])\n",
    "    x3_sc      = scaler_m3.transform(x3)\n",
    "    math_pred  = model_m3.predict(x3_sc)[0]\n",
    "    math_proba = model_m3.predict_proba(x3_sc)[0]\n",
    "\n",
    "    # Modelo 4: Mathematics_final\n",
    "\n",
    "    x4         = np.array([[math_term1, math_term2, math_term3, attendance_absences_total]])\n",
    "    math_final = float(model_m4.predict(x4)[0])\n",
    "\n",
    "    # Modelo 5: support_level\n",
    "\n",
    "    x5         = np.array([[term1_avg, attendance_absences_total, failed_subjects, tendency_enc, age, gender_enc]])\n",
    "    x5_sc      = scaler_m5.transform(x5)\n",
    "    supp_pred  = model_m5.predict(x5_sc)[0]\n",
    "    supp_proba = model_m5.predict_proba(x5_sc)[0]\n",
    "\n",
    "    return {\n",
    "        'avg_final_pred':          round(avg_pred, 3),\n",
    "        'grade_tendency_pred':     'Uptrend' if tend_pred == 1 else 'Downtrend',\n",
    "        'prob_uptrend':            round(float(tend_proba[1]), 4),\n",
    "        'prob_downtrend':          round(float(tend_proba[0]), 4),\n",
    "        'mathematics_status_pred': 'Pass' if math_pred == 1 else 'Fail',\n",
    "        'prob_math_pass':          round(float(math_proba[1]), 4),\n",
    "        'prob_math_fail':          round(float(math_proba[0]), 4),\n",
    "        'mathematics_final_pred':  round(math_final, 3),\n",
    "        'support_level_pred':      SUPPORT_LABELS[supp_pred],\n",
    "        'prob_support_low':        round(float(supp_proba[0]), 4),\n",
    "        'prob_support_medium':     round(float(supp_proba[1]), 4),\n",
    "        'prob_support_high':       round(float(supp_proba[2]), 4),\n",
    "    }\n",
    "\n",
    "\n",
    "# Test con un estudiante de ejemplo\n",
    "\n",
    "resultado = predict_student(\n",
    "\n",
    "    age                       = 16,\n",
    "    gender                    = 'F',\n",
    "    course                    = 'Secondary 3',\n",
    "    attendance_absences_total = 5,\n",
    "    support_level             = 'Low',\n",
    "    total_subjects            = 16,\n",
    "    math_term1                = 65.0,\n",
    "    math_term2                = 68.0,\n",
    "    math_term3                = 70.0,\n",
    "    term1_avg                 = 62.0,\n",
    "    term2_avg                 = 64.0,\n",
    "    term3_avg                 = 66.0,\n",
    "    failed_subjects           = 1,\n",
    "    grade_tendency            = 'Uptrend',\n",
    "\n",
    ")\n",
    "\n",
    "print('Predicción de ejemplo (Female, 16 años, Secondary 3):')\n",
    "\n",
    "for k, v in resultado.items():\n",
    "\n",
    "    print(f'  {k:<30} {v}')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.13.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
