{ "cells": [ { "cell_type": "markdown", "id": "3da7c691-7c3c-4ca9-826d-a2e3ed5dc510", "metadata": {}, "source": [ "# Xepelein Challenge \n", "## Respuesta 2: Entrenamiento del modelo" ] }, { "cell_type": "markdown", "id": "cf6f976f-03ca-4717-abdf-e31e01d37f36", "metadata": {}, "source": [ "- Se entrena un modelo de clasificación lightGBM.\n", "- El método prepare_data transforma la variable dependiente overdueDays en 0 / 1 dependiendo de si la mora será mayor a 30 días, de modo que 1, implica una alerta. También elimina los ids de business y payer ya que considero que tenemos información relevante sobre estos en las otras variables (y porque los datos son muy pocos para usar los ids como variables categóricas). \n", "- Se elimina el método impute_missing (sugerido en el template) porque este tipo de modelos no tiene problemas para gestionar nulos y porque no tengo una mejor heurística para rellenarlos (además que los datos proporcionados no tienen nulos). \n", "- En el método fit entrena el modelo LGBM.\n", "- El método model_summary incluye métricas de accuracy, precision, recall, F1 así como una matriz de confusión, todas relevantes para analizar la calidad de un modelo de clasificación\n", "- El método predict realiza predicciones\n", "\n", "- Métricas obtenidas:\n", " - Model Evaluation:\n", " - Accuracy: 0.85\n", " - Precision: 0.32\n", " - Recall: 0.15\n", " - F1 Score: 0.20\n", "\n", "- Comentario: El modelo tiene una buena métrica de accuracy. Sin embargo, dada la naturaleza del problema de negocio, la métrica de precisión es muy importante en este modelo, ya que es relativamente más costoso no detectar un caso de alerta positivo de manera correcta (es decir, prestar dinero y que no te lo devuelvan con menos de 30 días de mora). En este caso el modelo tiene una precisión bastante baja (0.32) pero no me he concentrado en mejorar esta métrica dadas las instrucciones del ejercicio, más centradas en la puesta en producción del modelo que en sus métricas de rendimiento. \n" ] }, { "cell_type": "code", "execution_count": 5, "id": "bef9dcc3-b957-4175-a6aa-ab71c9936617", "metadata": {}, "outputs": [], "source": [ "import pickle\n", "import pandas as pd\n", "import numpy as np\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\n", "from lightgbm import LGBMClassifier\n", "import seaborn as sns\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 6, "id": "6f5eeef6-336f-4cf1-b5db-4e97ba0d3b41", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "count 3000.000000\n", "mean 18.477000\n", "std 77.756463\n", "min -169.000000\n", "25% -7.000000\n", "50% 0.000000\n", "75% 10.000000\n", "max 539.000000\n", "Name: overdueDays, dtype: float64" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.read_csv(\"data/dataTest.csv\")\n", "df = df.drop([\"Unnamed: 0\"], axis = 1)\n", "df.overdueDays.describe()" ] }, { "cell_type": "code", "execution_count": 7, "id": "6f2b16d5-abdd-4282-bb86-fbfb09f7fee4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "invoiceId 0\n", "businessId 0\n", "payerId 0\n", "receiptAmount 0\n", "relationDays 0\n", "relationRecurrence 0\n", "issuerInvoicesAmount 0\n", "issuerCancelledInvoices 0\n", "activityDaysPayer 0\n", "clients12Months 0\n", "overdueDays 0\n", "dtype: int64" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.isna().sum()" ] }, { "cell_type": "code", "execution_count": 8, "id": "193fb0d7-46c7-42c4-90ec-99dc674a4039", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "overdye_days_alert\n", "0 2583\n", "1 417\n", "Name: count, dtype: int64" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v_dep = pd.Series( np.where(df[\"overdueDays\"] > 30, 1, 0), name = \"overdye_days_alert\")\n", "v_dep.value_counts()" ] }, { "cell_type": "code", "execution_count": 9, "id": "a2e49ccb-e67c-48e6-96e0-3876c3bd2199", "metadata": {}, "outputs": [], "source": [ "# guardo algunos datos para validar al final: \n", "df_train, df_validation = train_test_split(df, test_size=0.2, random_state=42)\n" ] }, { "cell_type": "code", "execution_count": 10, "id": "5dd10d85-0b27-4318-899f-93f48a578c5a", "metadata": {}, "outputs": [], "source": [ "class Model:\n", " def __init__(self, sample_df: pd.DataFrame):\n", " \"\"\"\n", " Initialize the class.\n", " \"\"\"\n", " self.data = self.prepare_data(sample_df)\n", " self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(\n", " self.data.drop('overdueDays', axis=1),\n", " self.data['overdueDays'],\n", " test_size=0.2,\n", " random_state=42\n", " )\n", " self.model = None\n", "\n", " def prepare_data(self, df: pd.DataFrame = None) -> pd.DataFrame:\n", " \"\"\"\n", " Prepare data.\n", " \"\"\"\n", " df = df.drop([\"businessId\", \"payerId\"], axis=1)\n", " df = df.set_index(\"invoiceId\")\n", " df[\"overdueDays\"] = np.where(df[\"overdueDays\"] > 30, 1, 0)\n", " return df.copy()\n", "\n", " def fit(self) -> None:\n", " \"\"\"\n", " Fit the model on the training data passed in the constructor, assuming it has\n", " been prepared by the function prepare_data \n", " \"\"\"\n", " self.model = LGBMClassifier()\n", " self.model.fit(self.X_train, self.y_train)\n", "\n", " def model_summary(self) -> str:\n", " \"\"\"\n", " Create a short summary of the model you have fit.\n", " \"\"\"\n", " if self.model is not None:\n", " y_pred = self.model.predict(self.X_test)\n", " confusion_mat = confusion_matrix(self.y_test, y_pred)\n", " accuracy = accuracy_score(self.y_test, y_pred)\n", " precision = precision_score(self.y_test, y_pred)\n", " recall = recall_score(self.y_test, y_pred)\n", " f1 = f1_score(self.y_test, y_pred)\n", "\n", " summary = f\"Model Evaluation:\\nAccuracy: {accuracy}\\nPrecision: {precision}\\nRecall: {recall}\\nF1 Score: {f1}\\n\\nConfusion Matrix:\\n{confusion_mat}\"\n", " return summary\n", " else:\n", " return \"Model not fitted. Call the fit method first.\"\n", "\n", " def predict(self, df: pd.DataFrame = None) -> pd.Series:\n", " \"\"\"\n", " Make a set of predictions with the model.\n", " \"\"\"\n", " if self.model is not None:\n", " predictions = pd.Series(self.model.predict(df), index=df.index)\n", " return predictions\n", " else:\n", " raise ValueError(\"Model not fitted. Call the fit method first.\")\n", "\n", " def save(self, path: str) -> None:\n", " \"\"\"\n", " Save the model as .pkl\n", " \"\"\"\n", " if self.model is not None:\n", " with open(path, 'wb') as file:\n", " pickle.dump(self.model, file)\n", " else:\n", " raise ValueError(\"Model not fitted. Call the fit method first.\")" ] }, { "cell_type": "code", "execution_count": 11, "id": "4374aadd-6b9a-43d6-ad61-e5ed8a6f8596", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[LightGBM] [Info] Number of positive: 275, number of negative: 1645\n", "[LightGBM] [Info] Auto-choosing row-wise multi-threading, the overhead of testing was 0.000389 seconds.\n", "You can set `force_row_wise=true` to remove the overhead.\n", "And if memory is not enough, you can set `force_col_wise=true`.\n", "[LightGBM] [Info] Total Bins 1672\n", "[LightGBM] [Info] Number of data points in the train set: 1920, number of used features: 7\n", "[LightGBM] [Info] [binary:BoostFromScore]: pavg=0.143229 -> initscore=-1.788725\n", "[LightGBM] [Info] Start training from score -1.788725\n", "Model Evaluation:\n", "Accuracy: 0.8541666666666666\n", "Precision: 0.32142857142857145\n", "Recall: 0.15\n", "F1 Score: 0.20454545454545456\n", "\n", "Confusion Matrix:\n", "[[401 19]\n", " [ 51 9]]\n" ] } ], "source": [ "model = Model(df_train)\n", "model.fit()\n", "print(model.model_summary())" ] }, { "cell_type": "code", "execution_count": 12, "id": "73e75a06-d071-473b-b0a2-cf9cf285f7ef", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.15" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "9 / 60" ] }, { "cell_type": "code", "execution_count": 13, "id": "48461cc9-b482-4e55-9838-9a9574d80b3b", "metadata": {}, "outputs": [], "source": [ "df_validation = model.prepare_data(df_validation)\n", "X_validation = df_validation.drop(\"overdueDays\", axis=1)\n", "y_true = df_validation[\"overdueDays\"]\n", "y_pred = model.predict(X_validation)\n" ] }, { "cell_type": "code", "execution_count": 14, "id": "82ed07b1-1016-4f0c-bc8c-bd269e3858fb", "metadata": {}, "outputs": [ { "data": { "image/png": "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Ur6+vpKk33//PV/9f/zxR3l4eKhq1apO7SEhIfL399ePP/7o1F6hQgXTOQICAvTbb79dZcRmPXv2VNOmTfXoo48qODhYvXr10kcfffS3CeOlOGvUqGHaV6tWLf366686c+aMU/tfv0tAQIAkFei73HPPPSpdurQ+/PBDLVy4UHfccYfpXl6Sl5enKVOmqFq1arLb7SpXrpwCAwN14MABZWRk5PuaN910U4EeUnnjjTdUpkwZ7du3T9OnT1dQUFC+jwWAgiJJBAopX19fhYaG6ttvvy3QcX99cORKihUrdtl2wzCu+hqX5std4u3trS1btujLL7/Uww8/rAMHDqhnz55q166dqe+/8W++yyV2u13dunXTggULtHz58itWESVp/PjxioqKUosWLfT+++9rzZo1WrdunW699dZ8V0ylP+5PQezdu1epqamSpIMHDxboWAAoKJJEoBDr3Lmzjh49qri4uH/sGxYWpry8PB0+fNip/eTJk0pPT3c8qXwtBAQEOD0JfMlfq5WS5OHhoTZt2mjy5Mn67rvvNG7cOG3YsEEbN2687LkvxZmQkGDa9/3336tcuXIqVarUv/sCV9C7d2/t3btXv//++2Uf9rnk448/VuvWrfXOO++oV69eat++vdq2bWu6J/lN2PPjzJkz6t+/v8LDw/X4449r4sSJ2r179zU7PwD8FUkiUIg999xzKlWqlB599FGdPHnStP/o0aOaNm2apD+GSyWZnkCePHmyJKlTp07XLK4qVaooIyNDBw4ccLQlJydr+fLlTv3S0tJMx15aVPqvy/JcUr58edWvX18LFixwSrq+/fZbrV271vE9XaF169Z65ZVX9N///lchISFX7FesWDFTlXLJkiX65ZdfnNouJbOXS6gLauTIkUpKStKCBQs0efJkVaxYUZGRkVe8jwDwb7GYNlCIValSRYsWLVLPnj1Vq1YtpzeubN++XUuWLFG/fv0kSfXq1VNkZKTeeustpaenq2XLltq1a5cWLFig+++//4rLq1yNXr16aeTIkerataueeeYZnT17VrNmzVL16tWdHtyIiYnRli1b1KlTJ4WFhSk1NVUzZ87UzTffrGbNml3x/K+//ro6duyoiIgIDRgwQOfOndOMGTPk5+enMWPGXLPv8VceHh4aNWrUP/br3LmzYmJi1L9/fzVp0kQHDx7UwoULVblyZad+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blyZJMbGxsrPz89pi42NvWIsH3zwgb755pvL9klJSZGXl5djit4lwcHBSklJcfT5c4J4af+lfflleZLYpk0bPfHEE/rmm28cbXv27NFTTz1lyp4BAACKmujoaGVkZDht0dHRl+37008/aciQIVq4cKHlywFaniTOnTtXISEhuv3222W322W323XnnXcqODhYc+bMsTo8AADgDmyu2+x2u3x9fZ02u91+2TD27Nmj1NRUNWjQQJ6envL09NTmzZs1ffp0eXp6Kjg4WBcuXFB6errTcSdPnlRISIgkKSQkxDQae+nzpT75Yek6iYZh6Ny5c1q6dKl+/vlnxcfHS/pjbL169epWhgYAAHDdtWnTRgcPHnRq69+/v2rWrKmRI0fqlltuUfHixbV+/Xp1795dkpSQkKCkpCRFRERIkiIiIjRu3DilpqYqKChIkrRu3Tr5+voqPDw837FYniRWrVpVhw4dUrVq1VStWjUrwwEAAG6qsCyBU7p0adWuXduprVSpUipbtqyjfcCAAYqKilKZMmXk6+urwYMHKyIiQo0bN5YktW/fXuHh4Xr44Yc1ceJEpaSkaNSoURo4cOAVK5iXY+lws4eHh6pVq6bTp09bGQYAAECRMWXKFHXu3Fndu3dXixYtFBISomXLljn2FytWTCtXrlSxYsUUERGh//znP+rbt69iYmIKdB2bYRjGtQ6+ID777DNNnDhRs2bNMmXOV8v7tkHX5DwACp/TO2dYHQIAFynpZV017+anV7js3D/PvN9l53Yly9/d3LdvX509e1b16tWTl5eXvL29nfanpaVZFBkAAID7sjxJvPQeQgAAAKsUljmJhYnlSWJkZKTVIQAAAHdHjmhi+TqJknT06FGNGjVKDz30kFJTUyVJq1atKtCrYwAAAHDtWJ4kbt68WXXq1NHOnTu1bNkyZWVlSZL279+vl19+2eLoAACAO3Dla/mKKsuTxOeff16vvvqq1q1bJy8vL0f7XXfdpR07dlgYGQAAgPuyfE7iwYMHtWjRIlN7UFCQfv31VwsiAgAA7qYoV/xcxfJKor+/v5KTk03te/fu1U033WRBRAAAALC8ktirVy+NHDlSS5Yskc1mU15enrZt26bhw4erb9++VocHC7z4xD0a9eQ9Tm0JiSmq3+1VSZLdy1MTorrpgQ4NZffy1Jdx8Roy/kOlpv0uSapT/SYN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ISEhxpYtW4wDBw4Y999/v+Hj4+OYk5iXl2c0a9bMqFevnrFmzRojMTHR2LZtm/HCCy8Yu3fvdsl9A4oikkRc1uWSxMTERMPLy8v4678tPv74YyM8PNwoXry4UaFCBeP111932h8XF2fUrVvXsNvtpmP/asuWLUaHDh0MX19fw8vLy7j11luNN95447LJ1RdffGFIMlq0aGHad+HCBeOll14yKlasaBQvXtwoX7680bVrV+PAgQOGYVw5SUxOTjY6depk2O12o0KFCsa7775rmgT/3XffGREREYa3t7dRv359Y+3atU5JomH88Rdp165dDX9/f8Pb29uoWbOm8eyzz5oSXaAoOn78uBEZGWkEBwcbxYsXN2655RZj8ODBxq+//urU77fffjPsdrtRsmRJ4/fffzedZ+HChUb9+vUNLy8vIyAgwGjRooWxbNkywzCunCTm5OQYQ4YMMXx9fQ1/f38jKirK6Nu3r9Pvq4yMDKNnz56Gr6+vccsttxjz5893enDFMAwjMzPTGDx4sBEaGur4Dn369DGSkpKu2X0CijqbYbhwhWIAAAAUSTzdDAAAABOSRAAAAJiQJAIAAMCEJBEAAAAmJIkAAAAwIUkEAACACUkiAAAATEgSAQAAYEKSCKDQ6tevn+6//37H51atWunZZ5+97nFs2rRJNpvN6T3eAHCjI0kEUGD9+vWTzWaTzWaTl5eXqlatqpiYGF28eNGl1122bJleeeWVfPUlsQOAf8fT6gAAFE1333235s2bp+zsbH3xxRcaOHCgihcvrujoaKd+Fy5ckJeX1zW5ZpkyZa7JeQAA/4xKIoCrYrfbFRISorCwMD311FNq27atPv30U8cQ8bhx4xQaGqoaNWpIkn766Sc9+OCD8vf3V5kyZdSlSxcdP37ccb7c3FxFRUXJ399fZcuW1XPPPae/vlr+r8PN2dnZGjlypG655RbZ7XZVrVpV77zzjo4fP67WrVtLkgICAmSz2dSvXz9JUl5enmJjY1WpUiV5e3urXr16+vjjj52u88UXX6h69ery9vZW69atneIEAHdBkgjgmvD29taFCxckSevXr1dCQoLWrVunlStXKicnRx06dFDp0qX11Vdfadu2bfLx8dHdd9/tOGbSpEmaP3++5s6dq61btyotLU3Lly//22v27dtXixcv1vTp0xUfH68333xTPj4+uuWWW7R06VJJUkJCgpKTkzVt2jRJUmxsrN59913Nnj1bhw4d0tChQ/Wf//xHmzdvlvRHMtutWzfde++92rdvnx599FE9//zzrrptAFBoMdwM4F8xDEPr16/XmjVrNHjwYJ06dUqlSpXSnDlzHMPM77//vvLy8jRnzhzZbDZJ0rx58+Tv769Nmzapffv2mjp1qqKjo9WtWzdJ0uzZs7VmzZorXveHH37QRx99pHXr1qlt27aSpMqVKzv2XxqaDgoKkr+/v6Q/Ko/jx4/Xl19+qYiICMcxW7du1ZtvvqmWLVtq1qxZqlKliiZNmiRJqlGjhg4ePKjXXnvtGt41ACj8SBIBXJWVK1fKx8dHOTk5ysvLU+/evTVmzBgNHDhQderUcZqHuH//fh05ckSlS5d2Osf58+d19OhRZWRkKDk5WY0aNXLs8/T01O23324acr5k3759KlasmFq2bJnvmI8cOaKzZ8+qXbt2Tu0XLlzQbbfdJkmKj493ikOSI6EEAHdCkgjgqrRu3VqzZs2Sl5eXQkND5en5f79OSpUq5dQ3KytLDRs21MKFC03nCQwMvKrre3t7F/iYrKwsSdLnn3+um266yWmf3W6/qjgA4EZFkgjgqpQqVUpVq1bNV98GDRroww8/VFBQkHx9fS/bp3z58tq5c6datGghSbp48aL27NmjBg0aXLZ/nTp1lJeXp82bNzuGm//sUiUzNzfX0RYeHi673a6kpKQrViBr1aqlTz/91Kltx44d//wlAeAGw4MrAFyuT58+KleunLp06aKvvvpKiYmJ2rRpk5555hn9/PPPkqQhQ4ZowoQJWrFihb7//ns9/fTTf7vGYcWKFRUZGalHHnlEK1ascJzzo48+kiSFhYXJZrNp5cqVOnXqlLKyslS6dGkNHz5cQ4cO1YIFC3T06FF98803mjFjhhYsWCBJevLJJ3X48GGNGDFCCQkJWrRokebPn+/qWwQAhQ5JIgCXK1mypLZs2aIKFSqoW7duqlWrlgYMGKDz5887KovDhg3Tww8/rMjISEVERKh06dLq2rXr35531qxZ6tGjh55++mnVrFlTjz32mM6cOSNJuummmzR27Fg9//zzCg4O1qBBgyRJr7zyikaPHq3Y2FjVqlVLd999tz7//HNVqlRJklShQgUtXbpUK1asUL169TR79myNHz/ehXcHAAonm3GlWeEAAABwW1QSAQAAYEKSCAAAABOSRAAAAJiQJAIAAMCEJBEAAAAmJIkAAAAwIUkEAACACUkiAAAATEgSAQAAYEKSCAAAABOSRAAAAJj8P3BMFODPjiZmAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "conf_matrix = confusion_matrix(y_true, y_pred)\n", "plt.figure(figsize=(8, 6))\n", "sns.heatmap(conf_matrix, annot=True, fmt=\"d\", cmap=\"Blues\", xticklabels=[\"Not Overdue\", \"Overdue\"], yticklabels=[\"Not Overdue\", \"Overdue\"])\n", "plt.xlabel(\"Predicted\")\n", "plt.ylabel(\"True\")\n", "plt.title(\"Confusion Matrix\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "e2f7bc84-a889-4de9-b1af-82821be3db34", "metadata": {}, "source": [ "Comentario: Los colores aquí pueden ser engañosos. Es preciso prestar atención a la fila de abajo, que es la que muestra los casos de mora, y en partticular, al cuadrante izquierdo, que muestra los casos en que tuvieron mora pero se predijo que no la tendrían, ya que estos son cruciales para nuestro problema de negocio. " ] }, { "cell_type": "code", "execution_count": 15, "id": "e807f315-7519-4ca7-b167-eaab45107159", "metadata": {}, "outputs": [], "source": [ "model.save(\"model/lightgbm_deuda.pkl\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 5 }