{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install polars","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nimport polars as pl","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pl.read_parquet(\"../input/otto-train-and-test-data-for-local-validation/train.parquet\")\nvalid = pl.read_parquet(\"../input/otto-train-and-test-data-for-local-validation/test.parquet\")\nvalid_labels = pl.read_parquet(\"../input/otto-train-and-test-data-for-local-validation/test_labels.parquet\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generating the item features","metadata":{}},{"cell_type":"code","source":"# {'aid':'count', 'session':'nunique', 'type': 'mean'}\nitem_features = pl.concat([train, valid]).groupby('aid').agg([\n    pl.count(\"aid\").alias(\"item_item_count\"), \n    pl.n_unique(\"session\").alias(\"item_user_count\"), \n    pl.mean(\"type\").alias(\"item_buy_ratio\").cast(pl.Float32)\n])\nitem_features.write_parquet('item_features.parquet')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Generating the user features","metadata":{}},{"cell_type":"code","source":"# {'session':'count','aid':'nunique','type':'mean'}\nuser_features = valid.groupby('session').agg([\n    pl.count(\"session\").alias(\"user_user_count\"),\n    pl.n_unique(\"aid\").alias(\"user_item_count\"),\n    pl.mean(\"type\").alias(\"user_buy_ratio\").cast(pl.Float32)\n])\nuser_features.write_parquet('user_features.parquet')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"event_paths = {\n    \"clicks\": \"../input/otto-577-validation-candidates/valid_click_candidates.parquet\",\n    \"carts\": \"../input/otto-577-validation-candidates/valid_carts_candidates.parquet\",\n    \"buys\": \"../input/otto-577-validation-candidates/valid_buys_candidates.parquet\"\n}","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del train, valid\ngc.collect()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import xgboost as xgb\nfrom sklearn.model_selection import GroupKFold\n\n\ndef train_ranker(event, df_cands, n_splits=5):\n    \n    skf = GroupKFold(n_splits=n_splits)\n    FEATURES = [\n        'session', 'item_item_count', 'item_user_count', 'item_buy_ratio', 'user_user_count', 'user_item_count', 'user_buy_ratio'\n    ]\n    TARGET = \"target\"\n    for fold,(train_idx, valid_idx) in enumerate(skf.split(df_cands, df_cands['target'], groups=df_cands['session'])):\n\n        X_train = df_cands.loc[train_idx, FEATURES]\n        y_train = df_cands.loc[train_idx, TARGET]\n        X_valid = df_cands.loc[valid_idx, FEATURES]\n        y_valid = df_cands.loc[valid_idx, TARGET]\n\n        X_train = X_train.sort_values(\"session\").reset_index(drop=True)\n        X_valid = X_valid.sort_values(\"session\").reset_index(drop=True)\n\n        train_group = X_train.groupby('session').session.agg('count').values\n        valid_group = X_valid.groupby('session').session.agg('count').values\n\n        X_train = X_train.drop([\"session\"], axis=1)\n        X_valid = X_valid.drop([\"session\"], axis=1)\n\n        dtrain = xgb.DMatrix(X_train, y_train, group=train_group) # [50] * (len(train_idx)//50) \n        dvalid = xgb.DMatrix(X_valid, y_valid, group=valid_group) # [50] * (len(valid_idx)//50)\n        xgb_parms = {\n            'objective':'rank:pairwise', \n            'tree_method':'hist',\n            'random_state': 42, \n            'learning_rate': 0.1,\n            \"colsample_bytree\": 0.8, \n            'eta': 0.05, \n            'max_depth': 6,\n            'subsample': 0.75,\n            # n_estimators=110,\n        }\n        model = xgb.train(\n            xgb_parms, \n            dtrain=dtrain,\n            evals=[(dtrain,'train'), (dvalid,'valid')],\n            num_boost_round=100,\n            verbose_eval=20\n        )\n        model.save_model(f'XGB_fold{fold}_{event}.xgb')\n        gc.collect()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training the Ranker","metadata":{}},{"cell_type":"code","source":"NEGATIVE_FRAC = 0.15\n\nfor event, path in event_paths.items():\n    print(f\"Started ranking model for: {event}\")\n    # Reading the candidates\n    df_cands = pl.read_parquet(path)\n    df_cands = df_cands.explode(\"candidates\").with_columns([\n        pl.col(\"session\").cast(pl.Int32),\n        pl.col(\"candidates\").cast(pl.Int32).alias(\"aid\")\n    ]).drop(\"candidates\").unique(subset=[\"session\", \"aid\"])\n    # Joining the item features\n    df_cands = df_cands.join(item_features, on='aid', how='left').fill_nan(-1)\n    # Joining the user features\n    df_cands = df_cands.join(user_features, on='session', how='left').fill_nan(-1)\n    cand_labels = valid_labels.filter(valid_labels[\"type\"] == event).explode(\"ground_truth\").with_columns([\n        pl.col(\"session\").cast(pl.Int32),\n        pl.col(\"ground_truth\").cast(pl.Int32)# .alias(\"aid\")\n    ]).rename({\"ground_truth\": \"aid\"})\n    cand_labels = cand_labels.with_column(pl.lit(1).alias(\"target\").cast(pl.Int8)).drop(\"type\")\n    # Joining the labels\n    df_cands = df_cands.join(cand_labels, on=[\"session\", \"aid\"], how=\"left\").fill_null(0)\n    # Negative sampling\n    df_cands = pl.concat([\n        df_cands.filter(df_cands[\"target\"] == 0).sample(frac=NEGATIVE_FRAC, seed=42),\n        df_cands.filter(df_cands[\"target\"] == 1)\n    ])\n    print(df_cands.groupby(\"target\").agg(pl.count()))\n    df_cands = df_cands.to_pandas()\n    df_cands = df_cands.sort_values(\"session\").reset_index(drop=True)\n    print(f\"Event: {event} - started training...\")\n\n    train_ranker(event, df_cands)\n    del df_cands, cand_labels\n    gc.collect()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}