{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":50160,"databundleVersionId":7921029,"sourceType":"competition"},{"sourceId":8523002,"sourceType":"datasetVersion","datasetId":4947589}],"dockerImageVersionId":30698,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import os\nfrom glob import glob\nimport gc\nimport pickle as pkl\n\nimport numpy as np\nimport pandas as pd\n\nimport lightgbm as lgb\n\ntry:\n    import cupy\n    device = \"cuda\"\nexcept:\n    device = \"cpu\"\n\nfrom sklearn import metrics\nfrom matplotlib import pyplot as plt\nfrom tqdm import tqdm\n\ndomain = \"test\"\nmainvars_path = \"/kaggle/input/hccrm-dataset/MAINVARS.pkl\"\nlgbm_models_path = \"/kaggle/input/hccrm-dataset/lgbm_models.pkl\"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data retrieval","metadata":{}},{"cell_type":"code","source":"description = pd.read_csv(\"/kaggle/input/home-credit-credit-risk-model-stability/feature_definitions.csv\").set_index(\"Variable\", drop=True)\ndef dv(name):\n    return description.loc[name,\"Description\"]\n\nPATH = lambda domain: f\"/kaggle/input/home-credit-credit-risk-model-stability/csv_files/{domain}\"\n\n\ndef files_n_df(domain):\n    files = glob(PATH(domain)+\"/*.csv\")\n    files = sorted(files)\n    schema = dict()\n    for file in files:\n        name = file.split(\"/\")[-1]\n        cols = []\n        with pd.read_csv(file, chunksize=2) as r:\n            for c in r:\n                cols = c.columns.to_list()\n                break\n        schema[name] = cols\n\n    tp = []\n    ee = 0\n    main_files = []\n    for e,(k,v) in enumerate(schema.items()):\n        if \"base\" not in k:\n            depth = int([i for i in k.split(\".\")[0].split(\"_\") if i.isnumeric()][0])\n        else:\n            depth = -1\n\n        true_name = \"_\".join([i for i in k.split(\".\")[0].split(\"_\") if i.isalpha()])\n        name = true_name + \"_\" + str(depth) if depth>-1  else true_name\n\n        if name not in tp:\n            tp.append(name)\n            ee = 0\n            main_files.append({\n                \"file_index\": e,\n                \"part_index\": ee,\n                \"true_name\": name,\n                \"file_name\": k,\n                \"depth\": depth,\n                \"num_cols\": len(v),\n            })\n        else:\n            ee += 1\n            main_files.append({\n                \"file_index\": e,\n                \"part_index\": ee,\n                \"true_name\": name,\n                \"file_name\": k,\n                \"depth\": depth,\n                \"num_cols\": len(v),\n            })\n\n    file_df = pd.DataFrame(main_files).sort_values([\"file_name\", \"depth\"], ignore_index=True)\n    return schema, files, file_df\n\nschema, files, files_df = files_n_df(domain)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"file_indices_dict = dict()\nfor tn in files_df.true_name.unique():\n    depths = files_df.loc[files_df[\"true_name\"]==tn,\"depth\"].unique()\n    for depth in depths:\n        file_indices = files_df.loc[(files_df[\"true_name\"]==tn) & (files_df[\"depth\"]==depth), \"file_index\"].values\n        file_indices_dict[tn] = file_indices.tolist()\n\nprint(\"File name: File index\")\nprint(\"_\"*25)\nfile_indices_dict","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_merged_data():\n    base_file_id = [i for i in range(len(files)) if \"_base.csv\" in files[i]][0]\n    \n    base = pd.read_csv(files[base_file_id], usecols=[\"case_id\", \"date_decision\"] + ([\"target\"] if domain==\"train\" else []), header=0, engine=\"pyarrow\")\n    base[\"case_id\"] = base[\"case_id\"].astype('float64')\n    base[\"date_decision\"] = base[\"date_decision\"].astype('datetime64[ns]')\n    \n    if domain==\"train\":\n        base[\"target\"] = base[\"target\"].astype('bool')\n\n    cat_cols = []\n    num_cols = []\n    to_drop = [\"periodicityofpmts_997L\"]\n\n    for k, v in schema.items():\n        if \"base\" in k:\n            continue\n        for vv in v:\n            if vv[-1] == \"L\":\n                if vv in to_drop:\n                    continue\n                if any([c in dv(vv).lower() for c in \"type,flag,status,gender,state,role,indicate,employment industry,employment length\".split(\",\")]) or any([c in vv.lower() for c in \"type,flag,status,gender,state,role,indicate,employment industry,employment length\".split(\",\")]):\n                    cat_cols.append(vv)\n\n    for k, v in schema.items():\n        if \"base\" in k:\n            continue\n        for vv in v:\n            if vv not in cat_cols:\n                if vv[-1] == \"L\":\n                    if vv in to_drop:\n                        continue\n                    num_cols.append(vv)  \n\n    for k, v in schema.items():\n        if \"base\" in k:\n            continue\n        for vv in v:\n            if vv[-1] == \"T\":\n                if vv in to_drop:\n                    continue\n                if any([c in dv(vv).lower() for c in \"type,relationship,month\".split(\",\")]) or any([c in vv.lower() for c in \"type,flag,status,gender,state,role,indicate,employment industry,employment length\".split(\",\")]):\n                    if \"probability\" in dv(vv):\n                        continue\n                    cat_cols.append(vv)\n\n    for k, v in schema.items():\n        if \"base\" in k:\n            continue\n        for vv in v:\n            if vv not in cat_cols:\n                if vv[-1] == \"T\":\n                    if vv in to_drop:\n                        continue\n                    num_cols.append(vv)\n\n    num_cols = list(set(num_cols))\n    cat_cols = list(set(cat_cols))\n\n    for file_name, file_indices in file_indices_dict.items():\n        if \"_base\" in file_name:\n            continue\n            \n        fs = []    \n        print(\"Name: \", file_name)\n        print(\" \"*5, \"number of files: \", len(file_indices))\n\n        for file_index in file_indices:\n            \n            print(\"Starting file index: \", file_index)\n            ng_cols = [n for n in schema[files[file_index].split(\"/\")[-1]] if \"num_group\" in n]\n            f = pd.read_csv(files[file_index], low_memory=False)\n            columns = f.columns\n            \n            # Setting dtype of columns\n            f[\"case_id\"] = f[\"case_id\"].astype('float64')\n            if len(ng_cols)>0:\n                f[ng_cols] = f[ng_cols].astype('float64')\n\n            if \"riskassesment_302T\" in \",\".join(columns):\n                n = [c for c in columns if \"riskassesment_302T\" in c][0]\n                f[n] = f[n].apply(lambda x: x.split(\"%\") if type(x)==str else None\n                                 ).apply(lambda x: int(x[0].strip())*0.5 + int(x[-2].split(\"-\")[-1].strip())*0.5 if x is not None else None\n                                        ).astype(\"float16\")\n\n            # Setting columns dtype\n            for col in columns:\n                if col in \"case_id,num_group1,num_group2\":\n                    continue\n                    \n                if col[-1]==\"D\":\n                    f[col] = f[col].astype('datetime64[ns]')\n\n                elif col[-1] in \"AP\":\n                    f[col] = f[col].astype(\"float32\")\n\n                elif col[-1] == \"M\":\n                    if any([col == k for k in \"name_4527232M,name_4917606M,employername_160M\".split(\",\")]):\n                        f.drop(col, axis=1, inplace=True)\n                    else:    \n                        f[col] = f[col].astype(\"object\")\n                    \n                elif col[-1] in \"LT\":\n                    if col not in to_drop:\n                        if col in num_cols:\n                            f[col] = f[col].astype(\"float32\")\n\n                        elif col in cat_cols:\n                            f[col] = f[col].astype(\"object\")\n                            \n                        else:\n                            f.drop(col, axis=1, inplace=True)\n                    else:\n                        f.drop(col, axis=1, inplace=True)\n                else:\n                    f.drop(col, axis=1, inplace=True)\n                    \n            print(\" \"*8, \"Successfully changed dtypes...\")\n\n            # Aggregating data based on `ng_cols and case_id`\n            \n            if len(ng_cols)>0:\n                fcols = [cn for cn,cd in zip(f.columns,f.dtypes) if (str(cd)==\"float32\" or str(cd)==\"datetime64[ns]\") and not ('num_group' in cn and cn == 'case_id')]\n                ccols = [cn for cn,cd in zip(f.columns,f.dtypes) if str(cd)==\"category\" or str(cd)==\"object\"]\n                \n                if len(fcols)>0:\n                    fnum = f.loc[:, fcols+[\"case_id\"]+ng_cols].sort_values(by=[\"case_id\"]+ng_cols).drop(ng_cols, axis=1).groupby(by=\"case_id\", as_index=False).agg(\n                            **{\n                                \"counts\"+\"_\"+\"_\".join(file_name.split(\"_\")[1:]):pd.NamedAgg(column=\"case_id\", aggfunc=\"count\")\n                            },\n\n                            **{\n                                \"max_\"+str(k):pd.NamedAgg(column=k, aggfunc=\"max\") for k in fcols if k != \"case_id\"\n                            },\n\n                            **{\n                                \"min_\"+str(k):pd.NamedAgg(column=k, aggfunc=\"min\") for k in fcols if k != \"case_id\"\n                            },\n\n                            **{\n                                \"last_\"+str(k):pd.NamedAgg(column=k, aggfunc=\"last\") for k in fcols if k != \"case_id\"\n                            }\n                    )\n                    \n                    print(\" \"*8, \"Successfully aggregated numeric columns...\")\n                else:\n                    fnum = None\n                    \n                if len(ccols)>0:\n                    fcat = f.loc[:, ccols+[\"case_id\"]+ng_cols].sort_values(by=[\"case_id\"]+ng_cols).drop(ng_cols, axis=1).groupby(by=\"case_id\", as_index=False).agg(\n                            **{\n                                k+\"_last\":pd.NamedAgg(column=k, aggfunc=\"last\") for k in ccols\n                            },\n\n                            **{\n                                k+\"_first\":pd.NamedAgg(column=k, aggfunc=\"first\") for k in ccols\n                            },\n                    )\n                    \n                    fcat[[c for c in fcat.columns if c!=\"case_id\"]] = fcat[[c for c in fcat.columns if c!=\"case_id\"]].astype(\"object\")\n                    \n                    print(\" \"*8, \"Successfully aggregated categoric columns...\")\n                    \n                else:\n                    fcat = None\n                \n                if all([fnum is not None, fcat is not None]):\n                    f = fnum.merge(right=fcat, on=\"case_id\", how='outer')\n                elif fnum is not None:\n                    f = fnum\n                elif fcat is not None:\n                    f = fcat\n                else:\n                    print(\"No numeric and categorical columns found\")\n\n            fs.append(f)\n\n        # Combining similar files\n        if len(fs)>1:\n            fs = pd.concat(fs, axis=0)\n        else:\n            fs = fs[0]\n        \n        ccols = [cn for cn,cd in zip(fs.columns,fs.dtypes) if str(cd).lower()==\"object\"]\n        if len(ccols)>0:\n            fs[ccols] = fs[ccols].astype(\"category\")\n            \n        print(\" \"*8, \"Successfully concatenated dataframes...\")\n        \n        base = base.merge(right=fs, on=\"case_id\", how=\"left\")\n        print(\" \"*5, \"Successfully merged dataframes...\")\n        \n        date_decision = base[\"date_decision\"]\n        base = base.apply(lambda x: ((x-base[\"date_decision\"]).dt.days).astype('float16') if str(x.dtype)==\"datetime64[ns]\" else x).drop(\"date_decision\", axis=1)\n        base[\"date_decision\"] = date_decision\n        print(\" \"*5, \"Successfully converted dates to numbers...\")\n\n        del f, fs\n        gc.collect()\n    \n    base.drop(\"date_decision\", axis=1, inplace=True)\n    gc.collect()\n    print(\"Completed\")\n    return base","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nif not os.path.isfile(\"trds.pkl\") or domain == \"test\":\n    base = get_merged_data()\n    \n    if domain==\"train\":\n        with open(\"trds.pkl\", 'wb') as file:\n            pkl.dump(base, file)\n        \nelif domain==\"train\" and os.path.isfile(\"trds.pkl\"):\n    try:\n        base.shape\n    except:\n        with open(\"trds.pkl\", 'rb') as file:\n            base = pkl.load(file)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_file_id = [i for i in range(len(files)) if \"_base.csv\" in files[i]][0]\nbbb = pd.read_csv(files[base_file_id])\n\nccc = pd.DataFrame().assign(case_id=bbb.case_id.astype(\"int64\"), date_decision=bbb.date_decision.astype(\"datetime64[ns]\"))\n\ndef count_cases_per_day(x):\n    counts = x.value_counts()\n    counts = pd.DataFrame({\"day\":counts.index, \"day_counts\":counts.values})\n    return pd.DataFrame({\"day\":x.values}).merge(right=counts, how=\"left\", on=\"day\").day_counts\n\ndef count_cases_per_week(day,week,cpd):\n    counts = pd.DataFrame({\"day\": day.values, \"week\":week.values, \"num_cases_this_day\":cpd.values}\n                         ).groupby(\"day\").first().groupby(\"week\", as_index=False\n                                  ).sum()\n    return pd.DataFrame({\"week\":week.values}).merge(right=counts, how=\"left\", on=\"week\").num_cases_this_day\n    \nccc = ccc.assign(day=lambda x: (x.date_decision-x.date_decision[0]).dt.days\n          ).assign(day=lambda x: x.day + abs(x.day.min())\n                  ).drop(\"date_decision\", axis=1\n                        ).assign(week=lambda x: x.day//7\n                                ).reset_index(drop=True\n                                             ).assign(num_cases_this_day=lambda x: count_cases_per_day(x.day)\n                                                     ).assign(num_cases_this_week=lambda x: count_cases_per_week(x.day, x.week, x.num_cases_this_day)\n                                                             ).assign(num_cases_this_day=lambda x: x.num_cases_this_day/1000\n                                                                     ).assign(num_cases_this_week=lambda x: x.num_cases_this_week/7000)\n\nbase = base.merge(right=ccc, on=\"case_id\", how=\"left\")\n\nbase[\"case_id\"] = base[\"case_id\"].astype(\"int64\")\nccols = [a for a,b in zip(base.columns, base.dtypes.to_list()) if b==\"object\"]\nif len(ccols)>0:\n    base[ccols] = base[ccols].astype(\"category\")\n\nprint(\"Null Report\")\n(base.isnull().sum(axis=0)/base.shape[0]).describe()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if domain==\"train\":\n    base_cat_cols = [a for a,b in zip(base.columns, base.dtypes.to_list()) if b==\"category\" or b==\"object\"]\n    base_num_cols = [a for a in base.columns if a not in base_cat_cols]\n    \n    if not os.path.isfile(mainvars_path):\n        \n#         # {column: dict} -> dict: {category: index}\n        category_dict = {c:{ccc:cccc for cccc,ccc in enumerate(sorted([str(cc) for cc in base[c].unique().tolist()]), start=1) if ccc != \"nan\"} for c in base_cat_cols}\n        print(f\"{len(base_cat_cols)=}, {len(base_num_cols)=}\")\n        \n        print(\"total categories of columns with less than 50 categories\", sum([len(v)+1 for k,v in category_dict.items() if k in base_cat_cols if len(v)<50]))\n        print(\"total categories of columns with greater than 50 categories\", sum([len(v)+1 for k,v in category_dict.items() if k in base_cat_cols if len(v)>50]))\n        \n        fs = []\n        for c in [k for k,v in category_dict.items() if k in base_cat_cols]:\n            x = base[c]\n            x = x.value_counts()\n            f = pd.DataFrame({\"column\":[c for _ in x.index],\"category\": x.index, \"frequency\": x.values})\n            fs.append(f.assign(frequency=lambda x: x.frequency/base.shape[0]))\n        fs = pd.concat(fs, axis=0)\n\n        col_to_be_removed = []\n        fs1 = fs.loc[(fs.frequency==0) | (fs.frequency==1)].sort_values(by=\"frequency\")\n        col_to_be_removed.extend(fs1.column.unique().tolist())\n        \n        fs1 = fs.loc[(fs.frequency>0) & (fs.frequency<1)].sort_values(by=\"frequency\")\n\n        col_cat_pair_for_reduction={}\n    \n        def t6575foo(x):\n            global col_cat_pair_for_reduction\n            col_cat_pair_for_reduction[x[\"column\"].values[0]] = x.category.tolist()\n            return x.iloc[0,:]\n        \n        other_cols = [\"contaddr_zipcode_807M_first\", \"registaddr_zipcode_184M_first\"]\n        \n        th = 300\n        n_other_cols = [c for c in fs1.column.unique().tolist() if c not in other_cols]\n        ffs1 = fs1.loc[[str(c) in n_other_cols for c in fs1.column.values.tolist()]]\n        _ = ffs1.loc[(ffs1.frequency<(th/base.shape[0]))].sort_values(by=\"frequency\").groupby(by=\"column\").apply(t6575foo)        \n        \n        th = 800\n        ffs1 = fs1.loc[[str(c) in other_cols for c in fs1.column.values.tolist()]]\n        _ = ffs1.loc[(ffs1.frequency<(th/base.shape[0]))].sort_values(by=\"frequency\").groupby(by=\"column\").apply(t6575foo)        \n        \n        base_cat_cols = [c for c in base_cat_cols if c not in col_to_be_removed]\n        \n        def t6575foo(c,cc):\n            try:\n                return str(cc) in col_cat_pair_for_reduction[c]\n            except:\n                return False\n        \n        category_dict = {c:{ccc:cccc for cccc,ccc in enumerate(sorted([\n                                                                        str(cc) for cc in base[c].unique().tolist() if not t6575foo(c,cc)\n                                                                      ]), start=1\n                                                              ) if ccc != \"nan\"\n                           } for c in base_cat_cols\n                        }\n        \n        for k,vs in col_cat_pair_for_reduction.items():\n            if k in category_dict.keys():\n                l = len(category_dict[k])\n                for v in vs:\n                    category_dict[k][str(v)] = l\n        \n        \n        \n        mx = base[[c for c in base_num_cols if c != \"case_id\" and c != \"target\"]].max()\n        mn = base[[c for c in base_num_cols if c != \"case_id\" and c != \"target\"]].min()\n    \n        MAINVARS = {\n            \"base_cat_cols\": base_cat_cols,\n            \"base_num_cols\": base_num_cols,\n            \"category_dict\": category_dict,\n            \"mx\": mx,\n            \"mn\": mn\n        }\n\n        with open(\"MAINVARS.pkl\", 'wb') as file:\n            pkl.dump(MAINVARS, file)\n    else:\n        with open(mainvars_path, 'rb') as file:\n            MAINVARS = pkl.load(file)\nelse:\n    with open(mainvars_path, 'rb') as file:\n        MAINVARS = pkl.load(file)\n\nbase_cat_cols = MAINVARS[\"base_cat_cols\"]\nbase_num_cols = MAINVARS[\"base_num_cols\"]\ncategory_dict = MAINVARS[\"category_dict\"]\nmx = MAINVARS[\"mx\"]\nmn = MAINVARS[\"mn\"]\n\nif domain==\"test\":\n    base_num_cols = [c for c in base_num_cols if c!=\"target\"]\n\nprint(f\"{len(base_cat_cols)=}, {len(base_num_cols)=}\")\nprint(\"total categories: \", sum([len(set(v.values())) for k,v in category_dict.items()]))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"NAN2NUM = 0.0\ndef get_processed_cat_f(x):\n    def foo(x1, x2):\n        try:\n            return category_dict[x1.name][x2]\n        except:\n            return 0\n    return x[base_cat_cols].map(str).copy().astype(\"object\").apply(lambda x1: x1.map(lambda x2: foo(x1, x2))).astype(\"int32\").reset_index(drop=True)\n\ndef get_processed_num_f(x_):\n    x = x_[base_num_cols].reset_index(drop=True)\n    x[[c for c in base_num_cols if c != \"case_id\" and c != \"target\"]] = ((x[[c for c in base_num_cols if c != \"case_id\" and c != \"target\"]]-mn)/(mx-mn)).astype(\"float16\")\n    mxmndiff = pd.DataFrame(x[[col for col in base_num_cols if col.split(\"_\")[0]==\"max\"]].values-x[[col for col in base_num_cols if col.split(\"_\")[0]==\"min\"]].values, columns=[\"mxmndiff_\"+col.split(\"max_\")[-1] for col in base_num_cols if col.split(\"_\")[0]==\"max\"]).astype(\"float16\")\n    mxlsdiff = pd.DataFrame(x[[col for col in base_num_cols if col.split(\"_\")[0]==\"max\"]].values-x[[col for col in base_num_cols if col.split(\"_\")[0]==\"last\"]].values, columns=[\"mxlsdiff_\"+col.split(\"max_\")[-1] for col in base_num_cols if col.split(\"_\")[0]==\"max\"]).astype(\"float16\")\n    return pd.concat([x, mxmndiff, mxlsdiff], axis=1).fillna(NAN2NUM)\n\ndef process_batch(x, onehot=True, log=True):\n    if log:\n        print(\"NUM VARS...\")\n    x1 = get_processed_num_f(x)\n    gc.collect()\n    \n    if log:\n        print(\"CAT VARS...\")\n    x2 = get_processed_cat_f(x)\n    gc.collect()\n    \n    if onehot:\n        if log:\n            print(\"   Staring using ONEHOT...\")\n        def foo(x1, x2):\n            x = np.zeros(len(category_dict[x1.name].keys())+1).astype(\"uint8\")\n            try: \n                x[x2] = 1\n            except:\n                x[0] = 1\n            return x\n        x2 = x2.apply(lambda xi: xi.map(lambda xj: foo(xi, xj))).values\n        gc.collect()\n        x2 = np.apply_along_axis(np.concatenate, axis=1, arr=x2).astype(\"uint8\")\n        gc.collect()\n        x2 = pd.DataFrame(x2).astype(\"uint8\")\n        gc.collect()\n        \n        if log:\n            print(\"Returning...\")\n        return pd.concat([x1, x2.astype(\"uint8\")], axis=1)\n    \n    if log:\n        print(\"Returning...\")\n        \n    return pd.concat([x1, x2.astype(\"uint8\").astype(\"category\")], axis=1)\n\ndef performance_check(ya, yp, prnt=True):\n    ths = [0.2,0.3,0.5,0.7,0.9]\n    acc = [metrics.accuracy_score(ya, (yp>th), normalize=True) for th in ths]\n    rec = [metrics.recall_score(ya, (yp>th), zero_division=0) for th in ths]\n    prec = [metrics.precision_score(ya, (yp>th), zero_division=0) for th in ths]\n    auc = metrics.roc_auc_score(ya, yp)\n    gini = 2*auc-1\n    \n    if prnt:\n        print(\"accuracy:   \", [f\"{i:.3f}\" for i in acc])\n        print(\"recall:     \", [f\"{i:.3f}\" for i in rec])    \n        print(\"precision:  \", [f\"{i:.3f}\" for i in prec])\n        print(\"auc & gini: \", f\"{auc:.4f}\", f\"{gini:.4f}\")\n    return gini","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nbase = process_batch(base, onehot=False)\nweeks = base.loc[:, \"case_id,week\".split(\",\")].assign(week=lambda x: (x.week*91).astype(\"int32\"))\n\ncase_ids = base.pop(\"case_id\")\nif domain==\"train\":\n    targets = base.pop(\"target\")\n    targets = targets.astype(\"float32\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if domain==\"train\":\n    FLAG97854 = False\n    if FLAG97854:\n        from sklearn.model_selection import StratifiedGroupKFold\n        cv = StratifiedGroupKFold(n_splits=5, shuffle=False)\n        params = {\n            \"boosting_type\": \"gbdt\",\n            \"objective\": \"binary\",\n            \"metric\": \"auc\",\n            \"max_depth\": -1,\n            \"learning_rate\": 0.025,\n            \"n_estimators\": 5000,\n            \"colsample_bytree\": 0.8, \n            \"colsample_bynode\": 0.8,\n            \"verbose\": -1,\n            \"random_state\": 42,\n            \"device\": \"gpu\",\n            \"max_bin\": 255\n        }\n        fitted_models = []\n\n        for idx_train, idx_valid in cv.split(base, targets, groups=(base.week*91).astype(\"int32\").values):\n            X_train, y_train = base.iloc[idx_train], targets.iloc[idx_train]\n            X_valid, y_valid = base.iloc[idx_valid], targets.iloc[idx_valid]\n            \n            print(\"train, test shapes: \", y_train.shape[0], y_valid.shape[0])\n            model = lgb.LGBMClassifier(**params)\n            model.fit(\n                X_train, y_train,\n                eval_set=[(X_valid, y_valid)],\n                callbacks=[lgb.log_evaluation(100), lgb.early_stopping(100)]\n            )\n\n            fitted_models.append(model)\n            print(\"Done...\")\n            \n        with open(\"lgbm_models.pkl\", \"wb\") as file:\n            pkl.dump(fitted_models, file)\n            ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ModelWrapper():\n    def __init__(self, dat, model_type, from_pickle=False, model_kwargs={}):\n        \"\"\"\n        Parameters:\n        __________\n                    `dat` is a model file it could be state_dict, json file path pr txt file path\n                    `model_type` is string it could be \"linear\", \"xgboost\", \"lgbm\"\n        \"\"\"\n        assert type(model_type)==str\n        self.model_type = model_type\n            \n        if model_type.lower() == \"xgboost\":\n            if not from_pickle:\n                self.model = xgb.XGBClassifier()\n                self.model.load_model(dat)\n            else:\n                self.model = dat\n            self.model.device = device\n            self.predict = self._xgb_predict\n        \n        elif model_type.lower() == \"lgbm\":\n            if not from_pickle:\n                self.model = lgb.Booster(model_file=dat)\n            else:\n                self.model = dat\n            self.model.task_type = 'GPU' if device == \"cuda\" else \"CPU\"\n            self.predict = self._lgbm_predict\n            \n        elif model_type.lower() == \"cgb\":\n            if not from_pickle:\n                self.model = cgb.CatBoostClassifier()\n                self.model.load_model(dat)\n            else:\n                self.model = dat\n            self.model.device = device\n            self.predict = self._cgb_predict\n    \n    def _xgb_predict(self,x):\n        return self.model.predict_proba(x)[:,1]\n    \n    def _lgbm_predict(self,x):\n        try:\n            nx = x.get()\n        except:\n            nx = x\n            \n        return self.model.predict_proba(nx)[:,1]\n    \n    def _cgb_predict(self,x):\n        try:\n            nx = x.get()\n        except:\n            nx = x\n        return self.model.predict_proba(nx)[:,1]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with open(lgbm_models_path, \"rb\") as file:\n    fitted_models = pkl.load(file)\n    \nmodels = [ModelWrapper(dat=m,model_type=\"lgbm\",from_pickle=True) for m in fitted_models]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nif domain==\"train\":\n    Flag768544 = False\n    if Flag768544:\n        yps = []\n        for model in tqdm(models):\n            yp = model.predict(base)\n            yps.append(yp)\n        performance_check(targets, np.concatenate([yp[None] for yp in yps]).mean(0))\n        yp = np.concatenate([yp[None] for yp in yps]).mean(0)\n        week = (base.week*91).astype(int).values\n        fff = pd.DataFrame({\"week\":week, \"pred\":yp, \"actual\":targets})\n        def foo(x):\n            return performance_check(x.actual, x.pred, prnt=False)\n        fff = fff.groupby(by=\"week\").apply(foo)\n        fff.plot()\n        plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Outputs for the model\n* accuracy:    ['0.968', '0.971', '0.969', '0.969', '0.969']\n* recall:      ['0.318', '0.160', '0.033', '0.004', '0.000']\n* precision:   ['0.476', '0.634', '0.810', '0.915', '1.000']\n* auc & gini:  0.9139 0.8278\n\n\n![download.png](attachment:4a5c8f2b-27b5-4111-b950-b876d253b4a4.png)","metadata":{},"attachments":{"4a5c8f2b-27b5-4111-b950-b876d253b4a4.png":{"image/png":"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U0KNPPiQiIgo6aIKWIxGI2bNmoWamhr5bR6PBzU1NZg/f37Yj33ttddgt9txxx139LrmnDlzcPTo0YC3Hzt2DGPHjo3m9hKC5wgRERElX9QloZUrV+Kuu+7C7NmzMXfuXKxfvx42m02eGlq2bBlGjRqF6urqgI/buHEjbrzxRhQUFPS65kMPPYRbbrkFV111Fb70pS9h69at+OMf/4ht27bF9qziRJIkSJCg0XBKiIiIKJmiDlhuueUWNDc349FHH0VDQwNmzJiBrVu3yo24tbW10GoDEzdHjx7Fjh078Je//EX1mjfddBM2bNiA6upqfPe738XEiRPx+9//HldccUUMTyl+NBoN/v7DL8Pp9kCn4Q4WIiKiZNFIkiQl+ybiwWKxICcnBx0dHcjOzk727RAREVEEIn395llCRERElPIYsBAREVHKY8BCREREKY8BCxEREaU8BixERESU8hiwEBERUcpjwEJEREQpjwELERERpTwGLERERJTyGLAQERFRymPAQkRERCmPAQsRERGlPAYsRERElPL0yb6BeBGHTlssliTfCREREUVKvG6L1/FQhkzAYrVaAQBlZWVJvhMiIiKKltVqRU5OTsj3a6S+QppBwuPx4OzZs8jKyoJGo4nbdS0WC8rKylBXV4fs7Oy4XZeix+9F6uD3InXwe5Fa+P2IniRJsFqtGDlyJLTa0J0qQybDotVqMXr06IRdPzs7mz98KYLfi9TB70Xq4PcitfD7EZ1wmRWBTbdERESU8hiwEBERUcpjwNIHk8mEqqoqmEymZN/KsMfvRerg9yJ18HuRWvj9SJwh03RLREREQxczLERERJTyGLAQERFRymPAQkRERCmPAQsRERGlPAYsfXjmmWdQXl4Os9mMefPmYffu3cm+pSGturoac+bMQVZWFoqLi3HjjTfi6NGjAY/p6enBihUrUFBQgMzMTHzjG99AY2Njku54+Hjqqaeg0WjwwAMPyG/j92Jg1dfX44477kBBQQHS0tIwdepUfPzxx/L7JUnCo48+ihEjRiAtLQ0VFRU4fvx4Eu94aHK73Vi7di3GjRuHtLQ0TJgwAY8//njAWTj8XiSARCFt2rRJMhqN0gsvvCB99tln0j333CPl5uZKjY2Nyb61IWvRokXSiy++KB08eFDat2+ftHjxYmnMmDFSZ2en/Jj77rtPKisrk2pqaqSP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"}}},{"cell_type":"markdown","source":"# Evaluation","metadata":{}},{"cell_type":"code","source":"%%time\nif domain==\"test\":\n    submission = []   \n    \n    yps = []\n    for model in tqdm(models):\n        yp = model.predict(base)\n        yps.append(yp)\n    \n    yps = np.concatenate([yp[None] for yp in yps]).mean(0)\n    \n    for yp, case_id in zip(yps, case_ids):\n        submission.append({\n            \"case_id\": case_id,\n            \"score\": float(yp)\n        })\n    submission = pd.DataFrame(submission)\n    submission = weeks.merge(right=submission, on=\"case_id\", how=\"right\")\n    submission = submission.sort_values(\"week\")\n    submission[\"score\"] = submission[\"score\"] - ((1/np.exp(np.linspace(0,10,submission.shape[0])))*0.073)\n    submission[\"score\"] = submission[\"score\"].clip(0)\n    submission.drop(\"week\", axis=1, inplace=True)\n    submission.to_csv(\"submission.csv\", index=False)\n    display(submission.head(10))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}