{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Script para generar la solución del Tercer Benchmark de la Competencia\n\n## Si no presentaste aún tu primera solución, tenes la oportunidad de hacerlo en pocos Clicks!\n\n**Hola! **  \n  \nEste Script es un Ejemplo de Procesamiento de los Datos, Modelado y Generación de una Solución.\n\nAgregamos una pequeña explicación de lo que se hace en cada paso para ayudar a los que están comenzando ahora\n"},{"metadata":{},"cell_type":"markdown","source":"### Importamos las librerías que vamos a utilizar"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import pandas as pd\nfrom lightgbm import LGBMClassifier\nfrom sklearn.metrics import roc_auc_score\nfrom sklearn.model_selection import KFold\nimport re","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Lectura de las Bases\n\nObservamos los datos que tenemos disponibles en https://www.kaggle.com/c/interbank20/data\n\n\n#### En este caso, vamos a descartar la base de censo, dado que empeora lo resultados. Este tipo de comportamiento es inusual pero no inesperado. Merecería un estudio detallado para saber con más precisión por qué esto ocurre, pero a priori, parecería ser que las variables del censo son relevantes (ergo, el modelo las usa) pero, por su antigüedad, son menos relevantes para el período de test (probablemente por no estar ya vigentes). Por lo tanto, estaríamos frente a una perturbación de la distribución del input, lo que perjudica al modelo que depende de las variables alteradas."},{"metadata":{"trusted":true},"cell_type":"code","source":"rcc_train = pd.read_csv(\"/kaggle/input/interbank20/rcc_train.csv\")\nse_train = pd.read_csv(\"/kaggle/input/interbank20/se_train.csv\", index_col=\"key_value\")\nsunat_train = pd.read_csv(\"/kaggle/input/interbank20/sunat_train.csv\")\ny_train = pd.read_csv(\"/kaggle/input/interbank20/y_train.csv\", index_col=\"key_value\").target\n\nrcc_test= pd.read_csv(\"/kaggle/input/interbank20/rcc_test.csv\")\nse_test= pd.read_csv(\"/kaggle/input/interbank20/se_test.csv\", index_col=\"key_value\")\nsunat_test= pd.read_csv(\"/kaggle/input/interbank20/sunat_test.csv\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Vamos a trabajar ahora con la base de **RCC**:\n\nEl principal problema que tiene esta base es su estructura temporal, que consiste de múltiples series de tiempo, una por cada producto en cada banco. \n"},{"metadata":{"trusted":true},"cell_type":"code","source":"rcc_train[(rcc_train.key_value == 4) & (rcc_train.cod_instit_financiera == 33)].sort_values(\"codmes\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"rcc_train[(rcc_train.key_value == 4) & (rcc_train.cod_instit_financiera == 61)].sort_values(\"codmes\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nPrimero discretizamos los días de atraso para poder manipularla mejor."},{"metadata":{"trusted":true},"cell_type":"code","source":"bins = [-1, 0, 10, 20, 30, 60, 90, 180, 360, 720, float(\"inf\")]\nrcc_train[\"condicion\"] = pd.cut(rcc_train.condicion, bins)\nrcc_train[\"condicion\"] = rcc_train[\"condicion\"].cat.codes\nrcc_test[\"condicion\"] = pd.cut(rcc_test.condicion, bins)\nrcc_test[\"condicion\"] = rcc_test[\"condicion\"].cat.codes","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### ¿Cómo podemos procesar rcc para extraer información útil?"},{"metadata":{"trusted":true},"cell_type":"code","source":"def makeCt(df, c, aggfunc=sum):\n    try:\n        ct = pd.crosstab(df.key_value, df[c].fillna(\"N/A\"), values=df.saldo, aggfunc=aggfunc)\n    except:\n        ct = pd.crosstab(df.key_value, df[c], values=df.saldo, aggfunc=aggfunc)\n    ct.columns = [f\"{c}_{aggfunc.__name__}_{v}\" for v in ct.columns]\n    return ct","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = []\ntest = []\naggfuncs = [len, sum, min, max]\nfor c in rcc_train.drop([\"codmes\", \"key_value\", \"saldo\"], axis=1):\n    print(\"haciendo\", c)\n    train.extend([makeCt(rcc_train, c, aggfunc) for aggfunc in aggfuncs])\n    test.extend([makeCt(rcc_test, c, aggfunc) for aggfunc in aggfuncs])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pd.crosstab(rcc_train.key_value, rcc_train.condicion.fillna(\"N/A\"), values=rcc_train.saldo, aggfunc=\"mean\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pd.crosstab(rcc_train.key_value, rcc_train.condicion.fillna(\"N/A\"), values=rcc_train.saldo, aggfunc=len)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import gc\n\ndel rcc_train, rcc_test\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.concat(train, axis=1)\ntest = pd.concat(test, axis=1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Sunat\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"sunat_train","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"En este caso, no es una serie de tiempo pero tenemos multiples filas por cada persona, dadas por la multiplicidad de rubros anotados"},{"metadata":{"trusted":true},"cell_type":"code","source":"pd.crosstab(sunat_train.key_value, sunat_train.ciiu)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Incorporamos la Información adicional existente en las tablas socio económicas y del censo. Es un simple join porque ambas tienen key_value únicos\n#### Por el momento no incorporamos la información tributaria porque requiere un tratamiento más complejo que queda para futuras revisiones"},{"metadata":{"trusted":true},"cell_type":"code","source":"train = train.join(pd.crosstab(sunat_train.key_value, sunat_train.ciiu)).join(se_train)\ntest = test.join(pd.crosstab(sunat_test.key_value, sunat_test.ciiu)).join(se_test)\n\ndel sunat_train, se_train, sunat_test, se_test\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Por la naturaleza de las variables creadas, nos aseguramos que solo se utilicen variables existentes en ambos conjuntos de datos (train y test)"},{"metadata":{"trusted":true},"cell_type":"code","source":"keep_cols = list(set(train.columns).intersection(set(test.columns)))\ntrain = train[keep_cols]\ntest = test[keep_cols]\nlen(set(train.columns) - set(test.columns)) , len(set(test.columns) - set(train.columns))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.columns = [str(c) for c in train.columns]\ntrain = train.rename(columns = lambda x:re.sub('[^A-Za-z0-9_-]+', '', x))\n\ntest.columns = [str(c) for c in test.columns]\ntest = test.rename(columns = lambda x:re.sub('[^A-Za-z0-9_-]+', '', x))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"folds = [train.index[t] for t, v in KFold(5).split(train)]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Entrenamiento del Modelo\n\nPara entrenar nuestro modelo vamos a usar LightGBM. A diferencia del notebook anterior, esta vez vamos a agregar la optimización de hyper-parámetro. Se usan sólo dos con algunos pocos posibles valores, a modo de ejemplo para que los participantes lo puedan ir mejorando. "},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import ParameterGrid\n\nparams = ParameterGrid({\"min_child_samples\": [150, 250, 500, 1000], \"boosting_type\": [\"gbdt\", \"goss\"]})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"best_score = 0\nbest_probs = []\nfor param in params:\n    test_probs = []\n    train_probs = []\n    p  = \"///\".join([f\"{k}={v}\" for k, v in param.items()])\n    print(\"*\"*10, p, \"*\"*10)\n    for i, idx in enumerate(folds):\n        Xt = train.loc[idx]\n        yt = y_train.loc[Xt.index]\n\n        Xv = train.drop(Xt.index)\n        yv = y_train.loc[Xv.index]\n\n        learner = LGBMClassifier(n_estimators=1000, **param)\n        learner.fit(Xt, yt,  early_stopping_rounds=10, eval_metric=\"auc\",\n                    eval_set=[(Xt, yt), (Xv, yv)], verbose=False)\n        test_probs.append(pd.Series(learner.predict_proba(test)[:, -1], index=test.index, name=\"fold_\" + str(i)))\n        train_probs.append(pd.Series(learner.predict_proba(Xv)[:, -1], index=Xv.index, name=\"probs\"))\n\n    test_probs = pd.concat(test_probs, axis=1).mean(axis=1)\n    train_probs = pd.concat(train_probs)\n    score = roc_auc_score(y_train, train_probs.loc[y_train.index])\n    print(f\"roc auc estimado para {p}: {score}\")\n    if score > best_score:\n        print(\"*\"*10, f\"{p} es el nuevo mejor modelo\", \"*\"*10)\n        best_score = score\n        best_probs = test_probs\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Guardado de las predicciones modelo para hacer la presentación\n\nFinalmente creamos el archivo CSV que podemos subir como nuestra Solución a la competencia"},{"metadata":{"trusted":true},"cell_type":"code","source":"best_probs.name = \"target\"\nbest_probs.to_csv(\"benchmark3.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}