{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":114135,"databundleVersionId":14604229,"sourceType":"competition"}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"In this notebook, I will show you how to submit csv file for submission in competition. This is a simple way, first you have to train dataset provided, and second you have to use test dataset to create CSV submission for submit in the competition.","metadata":{}},{"cell_type":"code","source":"# packages\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom sklearn.preprocessing import LabelEncoder\nfrom catboost import CatBoostClassifier\nfrom sklearn.utils.class_weight import compute_class_weight\nimport matplotlib.pyplot as plt\nimport math\nfrom sklearn.metrics import f1_score, accuracy_score, balanced_accuracy_score, matthews_corrcoef\nimport time\nfrom sklearn.feature_selection import mutual_info_classif\n\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:21:38.857299Z","iopub.execute_input":"2026-01-07T10:21:38.857659Z","iopub.status.idle":"2026-01-07T10:21:40.173162Z","shell.execute_reply.started":"2026-01-07T10:21:38.857632Z","shell.execute_reply":"2026-01-07T10:21:40.172198Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_train = pd.read_csv('/kaggle/input/detecting-reversal-points-in-us-equities/competition_data/competition_data/train.csv')\ndf_train.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:21:40.175132Z","iopub.execute_input":"2026-01-07T10:21:40.175665Z","iopub.status.idle":"2026-01-07T10:25:35.752679Z","shell.execute_reply.started":"2026-01-07T10:21:40.175626Z","shell.execute_reply":"2026-01-07T10:25:35.751576Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test = pd.read_csv('/kaggle/input/detecting-reversal-points-in-us-equities/new_comptetition_data/test.csv')\ndf_test.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:25:35.753568Z","iopub.execute_input":"2026-01-07T10:25:35.753853Z","iopub.status.idle":"2026-01-07T10:27:55.005899Z","shell.execute_reply.started":"2026-01-07T10:25:35.753827Z","shell.execute_reply":"2026-01-07T10:27:55.004685Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_train.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:27:55.008443Z","iopub.execute_input":"2026-01-07T10:27:55.008836Z","iopub.status.idle":"2026-01-07T10:27:55.058121Z","shell.execute_reply.started":"2026-01-07T10:27:55.008807Z","shell.execute_reply":"2026-01-07T10:27:55.057212Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:27:55.059506Z","iopub.execute_input":"2026-01-07T10:27:55.059866Z","iopub.status.idle":"2026-01-07T10:27:55.087882Z","shell.execute_reply.started":"2026-01-07T10:27:55.059833Z","shell.execute_reply":"2026-01-07T10:27:55.086769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:27:55.089416Z","iopub.execute_input":"2026-01-07T10:27:55.089976Z","iopub.status.idle":"2026-01-07T10:27:57.509713Z","shell.execute_reply.started":"2026-01-07T10:27:55.089939Z","shell.execute_reply":"2026-01-07T10:27:57.508571Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:27:57.511093Z","iopub.execute_input":"2026-01-07T10:27:57.511504Z","iopub.status.idle":"2026-01-07T10:27:59.672880Z","shell.execute_reply.started":"2026-01-07T10:27:57.511467Z","shell.execute_reply":"2026-01-07T10:27:59.671628Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Data Preprocessing","metadata":{}},{"cell_type":"markdown","source":"# Exploratory Data Analysis (EDA)","metadata":{}},{"cell_type":"code","source":"#check duplicated for training data\ndf_train.duplicated().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:27:59.674221Z","iopub.execute_input":"2026-01-07T10:27:59.674606Z","iopub.status.idle":"2026-01-07T10:28:04.704230Z","shell.execute_reply.started":"2026-01-07T10:27:59.674555Z","shell.execute_reply":"2026-01-07T10:28:04.703377Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#check duplicated for testing data\ndf_test.duplicated().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:04.705232Z","iopub.execute_input":"2026-01-07T10:28:04.705496Z","iopub.status.idle":"2026-01-07T10:28:08.769104Z","shell.execute_reply.started":"2026-01-07T10:28:04.705464Z","shell.execute_reply":"2026-01-07T10:28:08.768257Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#show columns that have null values more for training data\nfor column in df_train.columns:\n    if df_train[column].isnull().sum() > 0:\n        print(f'{column} has {df_train[column].isnull().sum()} of null values')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:08.772271Z","iopub.execute_input":"2026-01-07T10:28:08.772627Z","iopub.status.idle":"2026-01-07T10:28:13.815099Z","shell.execute_reply.started":"2026-01-07T10:28:08.772598Z","shell.execute_reply":"2026-01-07T10:28:13.814053Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#show columns that have null values more for testing data\nfor column in df_test.columns:\n    if df_test[column].isnull().sum() > 0:\n        print(f'{column} has {df_test[column].isnull().sum()} of null values')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:13.816309Z","iopub.execute_input":"2026-01-07T10:28:13.816773Z","iopub.status.idle":"2026-01-07T10:28:18.725852Z","shell.execute_reply.started":"2026-01-07T10:28:13.816733Z","shell.execute_reply":"2026-01-07T10:28:18.724586Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#change datetime column type into datetime from string type\ndf_train['t'] = pd.to_datetime(df_train['t'])\ndf_test['t'] = pd.to_datetime(df_test['t'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:18.727106Z","iopub.execute_input":"2026-01-07T10:28:18.727465Z","iopub.status.idle":"2026-01-07T10:28:18.808225Z","shell.execute_reply.started":"2026-01-07T10:28:18.727436Z","shell.execute_reply":"2026-01-07T10:28:18.807034Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install rapidfuzz","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:18.809846Z","iopub.execute_input":"2026-01-07T10:28:18.810338Z","iopub.status.idle":"2026-01-07T10:28:26.989983Z","shell.execute_reply.started":"2026-01-07T10:28:18.810307Z","shell.execute_reply":"2026-01-07T10:28:26.988548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from rapidfuzz import fuzz\n\ndef find_string_similarities(df, threshold=80):\n    results = []\n\n    # Select only string (object) columns\n    string_cols = df.select_dtypes(include=\"object\").columns\n    string_cols = [i for i in string_cols if i != 'class_label']\n\n    values = []\n    for col in string_cols:\n        for idx, val in df[col].items():\n            if pd.notna(val):  # ignore NaN\n                values.append((idx, col, str(val)))\n\n    # Compare all values\n    for i in range(len(values)):\n        for j in range(i + 1, len(values)):\n            idx1, col1, val1 = values[i]\n            idx2, col2, val2 = values[j]\n\n            score = fuzz.ratio(val1, val2)\n            if score >= threshold:\n                results.append({\n                    \"row1\": idx1,\n                    \"column1\": col1,\n                    \"value1\": val1,\n                    \"row2\": idx2,\n                    \"column2\": col2,\n                    \"value2\": val2,\n                    \"similarity_%\": score\n                })\n\n    return results\n    \nsimm = find_string_similarities(df_train, threshold=80)\nprint(simm)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:26.991831Z","iopub.execute_input":"2026-01-07T10:28:26.992247Z","iopub.status.idle":"2026-01-07T10:28:27.025441Z","shell.execute_reply.started":"2026-01-07T10:28:26.992201Z","shell.execute_reply":"2026-01-07T10:28:27.024418Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def find_string_similarities(df, threshold=80):\n    results = []\n\n    # Select only string (object) columns\n    string_cols = df.select_dtypes(include=\"object\").columns\n    string_cols = [i for i in string_cols if i != 'class_label']\n\n    values = []\n    for col in string_cols:\n        for idx, val in df[col].items():\n            if pd.notna(val):  # ignore NaN\n                values.append((idx, col, str(val)))\n\n    # Compare all values\n    for i in range(len(values)):\n        for j in range(i + 1, len(values)):\n            idx1, col1, val1 = values[i]\n            idx2, col2, val2 = values[j]\n\n            score = fuzz.ratio(val1, val2)\n            if score >= threshold:\n                results.append({\n                    \"row1\": idx1,\n                    \"column1\": col1,\n                    \"value1\": val1,\n                    \"row2\": idx2,\n                    \"column2\": col2,\n                    \"value2\": val2,\n                    \"similarity_%\": score\n                })\n\n    return results\n    \nsimm = find_string_similarities(df_test, threshold=80)\nprint(simm)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:27.026693Z","iopub.execute_input":"2026-01-07T10:28:27.027436Z","iopub.status.idle":"2026-01-07T10:28:27.037776Z","shell.execute_reply.started":"2026-01-07T10:28:27.027392Z","shell.execute_reply":"2026-01-07T10:28:27.036563Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_train['class_label'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:27.038941Z","iopub.execute_input":"2026-01-07T10:28:27.039255Z","iopub.status.idle":"2026-01-07T10:28:27.065945Z","shell.execute_reply.started":"2026-01-07T10:28:27.039225Z","shell.execute_reply":"2026-01-07T10:28:27.064810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# change HH and LH into H, change LL and HL into L\ndf_train[\"class_label\"] = df_train[\"class_label\"].where(\n    df_train[\"class_label\"].isna(),\n    df_train[\"class_label\"].str[-1]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:27.067207Z","iopub.execute_input":"2026-01-07T10:28:27.067576Z","iopub.status.idle":"2026-01-07T10:28:27.087317Z","shell.execute_reply.started":"2026-01-07T10:28:27.067549Z","shell.execute_reply":"2026-01-07T10:28:27.086199Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"counts = df_train['class_label'].value_counts(dropna=False)\n\nlabels = [\n    f\"{idx if idx == idx else 'NaN'} \"\n    f\"(Count: {count}, {count / counts.sum() * 100:.1f}%)\"\n    for idx, count in counts.items()\n]\n\nplt.figure()\nwedges, texts, autotexts = plt.pie(\n    counts.values,\n    autopct='%1.1f%%',\n    startangle=50,\n    pctdistance=0.75\n)\n\nplt.legend(\n    wedges,\n    labels,\n    title=\"Class Label\",\n    loc=\"center left\",\n    bbox_to_anchor=(1, 0.5)\n)\n\nplt.title(\"Distribution of class_label\")\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:27.088811Z","iopub.execute_input":"2026-01-07T10:28:27.089458Z","iopub.status.idle":"2026-01-07T10:28:27.337095Z","shell.execute_reply.started":"2026-01-07T10:28:27.089413Z","shell.execute_reply":"2026-01-07T10:28:27.335740Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ticker_ids = df_train['ticker_id'].unique().tolist()\n\nn = len(ticker_ids)\ncols = 2                         # jumlah kolom subplot\nrows = math.ceil(n / cols)       # hitung baris otomatis\n\nfig, axes = plt.subplots(rows, cols, figsize=(11, 5 * rows))\naxes = axes.flatten()\n\nfor idx, ticker in enumerate(ticker_ids):\n    ax = axes[idx]\n\n    df_ticker = df_train[df_train['ticker_id'] == ticker]\n    counts = df_ticker['class_label'].value_counts(dropna=False)\n\n    labels = [\n        f\"{cls if cls == cls else 'NaN'} \"\n        f\"(Count: {count}, {count / counts.sum() * 100:.1f}%)\"\n        for cls, count in counts.items()\n    ]\n\n    wedges, texts, autotexts = ax.pie(\n        counts.values,\n        autopct='%1.1f%%',\n        startangle=50,\n        pctdistance=0.75\n    )\n\n    ax.legend(\n        wedges,\n        labels,\n        title=\"Class Label\",\n        loc=\"center left\",\n        bbox_to_anchor=(1, 0.5)\n    )\n\n    ax.set_title(f\"Ticker ID: {ticker}\")\n\n# Hapus subplot kosong jika ada\nfor j in range(idx + 1, len(axes)):\n    fig.delaxes(axes[j])\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:27.338044Z","iopub.execute_input":"2026-01-07T10:28:27.338463Z","iopub.status.idle":"2026-01-07T10:28:28.217114Z","shell.execute_reply.started":"2026-01-07T10:28:27.338433Z","shell.execute_reply":"2026-01-07T10:28:28.216053Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# since every ticker_id has same pattern, it should used same model. But let us check time pattern first.\nline_df = (\n    df_train\n    .groupby(['t', 'class_label'], dropna=False)\n    .size()\n    .unstack(fill_value=0)\n    .sort_index()\n)\n\n# Line plot\nplt.figure(figsize=(14, 6))\nfor col in line_df.columns:\n    plt.plot(line_df.index, line_df[col], label=str(col))\n\nplt.xlabel(\"Date (yyyy-mm-dd)\")\nplt.ylabel(\"Count\")\nplt.title(\"Daily Distribution by Class Label\")\nplt.legend(title=\"class_label\")\nplt.xticks(rotation=45)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:28.218811Z","iopub.execute_input":"2026-01-07T10:28:28.219391Z","iopub.status.idle":"2026-01-07T10:28:28.601091Z","shell.execute_reply.started":"2026-01-07T10:28:28.219358Z","shell.execute_reply":"2026-01-07T10:28:28.600091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# since every ticker_id has same pattern, it should used same model. But let us check time pattern first.\nticker_ids = df_train['ticker_id'].unique().tolist()\nn = len(ticker_ids)\n\nfig, axes = plt.subplots(n, 1, figsize=(14, 4 * n), sharex=True)\n\nif n == 1:\n    axes = [axes]\n\nfor ax, ticker in zip(axes, ticker_ids):\n    df_ticker = df_train[df_train['ticker_id'] == ticker]\n\n    line_df = (\n        df_ticker\n        .groupby(['t', 'class_label'], dropna=False)\n        .size()\n        .unstack(fill_value=0)\n        .sort_index()\n    )\n\n    for col in line_df.columns:\n        ax.plot(line_df.index, line_df[col], label=str(col))\n\n    ax.set_title(f\"Ticker ID: {ticker}\")\n    ax.set_ylabel(\"Count\")\n    ax.legend(title=\"class_label\")\n\naxes[-1].set_xlabel(\"Date (yyyy-mm-dd)\")\nplt.xticks(rotation=45)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:28.603092Z","iopub.execute_input":"2026-01-07T10:28:28.603987Z","iopub.status.idle":"2026-01-07T10:28:30.322394Z","shell.execute_reply.started":"2026-01-07T10:28:28.603948Z","shell.execute_reply":"2026-01-07T10:28:30.321288Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"> ****The time series has different pattern, so we will use looping for the same model by each ticker id****","metadata":{}},{"cell_type":"markdown","source":"# ALL TICKER ID","metadata":{}},{"cell_type":"markdown","source":"## Prepare Training and Testing Dataset for Modelling by TimeSeries","metadata":{}},{"cell_type":"code","source":"# sort the value first by datetime\ndf_train = df_train.sort_values(by=\"t\", ascending=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:30.323712Z","iopub.execute_input":"2026-01-07T10:28:30.324264Z","iopub.status.idle":"2026-01-07T10:28:30.549812Z","shell.execute_reply.started":"2026-01-07T10:28:30.324223Z","shell.execute_reply":"2026-01-07T10:28:30.548992Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create X and Y coordinate for training\nX = df_train.drop(columns = ['class_label', 't', 'train_id', 'ticker_id'])\n\ny = df_train['class_label']\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:30.551118Z","iopub.execute_input":"2026-01-07T10:28:30.551662Z","iopub.status.idle":"2026-01-07T10:28:30.655959Z","shell.execute_reply.started":"2026-01-07T10:28:30.551623Z","shell.execute_reply":"2026-01-07T10:28:30.654982Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"le = LabelEncoder()\n\ny = le.fit_transform(y)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:30.657271Z","iopub.execute_input":"2026-01-07T10:28:30.657561Z","iopub.status.idle":"2026-01-07T10:28:30.666784Z","shell.execute_reply.started":"2026-01-07T10:28:30.657529Z","shell.execute_reply":"2026-01-07T10:28:30.665682Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # calculate variance in every column\n# n_features_to_keep = 500\n# variances = X.var()\n\n# # drop NaN\n# variances = variances.dropna()\n\n# # sort & get top N\n# top_cols = variances.sort_values(ascending=False).head(n_features_to_keep).index.tolist()\n\n# print(f\"Selected top {len(top_cols)} features by variance\")\n\nmi = mutual_info_classif(\n    X,\n    y,\n    discrete_features=\"auto\",\n    random_state=0\n)\n\ntop_idx = mi.argsort()[-1000:]\nX_mi = X.iloc[:, top_idx]\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:28:30.667992Z","iopub.execute_input":"2026-01-07T10:28:30.668404Z","iopub.status.idle":"2026-01-07T10:41:06.026710Z","shell.execute_reply.started":"2026-01-07T10:28:30.668374Z","shell.execute_reply":"2026-01-07T10:41:06.024452Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"top_cols = X_mi.columns.tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:06.029762Z","iopub.execute_input":"2026-01-07T10:41:06.030386Z","iopub.status.idle":"2026-01-07T10:41:06.037862Z","shell.execute_reply.started":"2026-01-07T10:41:06.030342Z","shell.execute_reply":"2026-01-07T10:41:06.036710Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# compute split index 80% for training and 20% for testing\nsplit_idx = int(len(df_train) * 0.9)\n\n# split\ndf_train_split = df_train.iloc[:split_idx].reset_index(drop=True)\ndf_test_split = df_train.iloc[split_idx:].reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:06.039361Z","iopub.execute_input":"2026-01-07T10:41:06.040104Z","iopub.status.idle":"2026-01-07T10:41:06.296722Z","shell.execute_reply.started":"2026-01-07T10:41:06.040049Z","shell.execute_reply":"2026-01-07T10:41:06.295587Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create X and Y coordinate for training\nX_train = df_train_split.drop(columns = ['class_label', 't', 'train_id', 'ticker_id'])\ny_train = df_train_split['class_label']\n\n\nX_test = df_test_split.drop(columns = ['class_label', 't', 'train_id', 'ticker_id'])\ny_test = df_test_split['class_label']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:06.303335Z","iopub.execute_input":"2026-01-07T10:41:06.303695Z","iopub.status.idle":"2026-01-07T10:41:06.572502Z","shell.execute_reply.started":"2026-01-07T10:41:06.303667Z","shell.execute_reply":"2026-01-07T10:41:06.571500Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:06.574067Z","iopub.execute_input":"2026-01-07T10:41:06.574433Z","iopub.status.idle":"2026-01-07T10:41:06.581902Z","shell.execute_reply.started":"2026-01-07T10:41:06.574395Z","shell.execute_reply":"2026-01-07T10:41:06.580876Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"le = LabelEncoder()\n\ny_train_encoded = le.fit_transform(y_train)\ny_test_encoded = le.fit_transform(y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:06.583122Z","iopub.execute_input":"2026-01-07T10:41:06.583577Z","iopub.status.idle":"2026-01-07T10:41:06.608216Z","shell.execute_reply.started":"2026-01-07T10:41:06.583432Z","shell.execute_reply":"2026-01-07T10:41:06.606980Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Modelling the Dataset","metadata":{}},{"cell_type":"markdown","source":"### Cat Boost","metadata":{}},{"cell_type":"code","source":"classes = np.unique(y_train_encoded)\nclass_weights = compute_class_weight(\n    class_weight=\"balanced\",\n    classes=classes,\n    y=y_train_encoded\n)\n\nmodel = CatBoostClassifier(\n    loss_function=\"MultiClass\",\n    iterations=80,\n    learning_rate=0.05,\n    depth=2,\n    class_weights=class_weights,\n    l2_leaf_reg=10,\n    random_seed=0,\n    verbose=False\n)\n\n\nmodel.fit(\n    X_train[top_cols],\n    y_train_encoded\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:06.609835Z","iopub.execute_input":"2026-01-07T10:41:06.610163Z","iopub.status.idle":"2026-01-07T10:41:07.070126Z","shell.execute_reply.started":"2026-01-07T10:41:06.610126Z","shell.execute_reply":"2026-01-07T10:41:07.069213Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# predict\n\nstart = time.time()\ny_pred = model.predict(X_test[top_cols])\ninference_runtime = time.time() - start","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:07.071554Z","iopub.execute_input":"2026-01-07T10:41:07.071854Z","iopub.status.idle":"2026-01-07T10:41:07.133544Z","shell.execute_reply.started":"2026-01-07T10:41:07.071827Z","shell.execute_reply":"2026-01-07T10:41:07.132439Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# metrics\nmacro_f1 = f1_score(y_test_encoded, y_pred, average=\"macro\")\nmacro_bal_acc = balanced_accuracy_score(y_test_encoded, y_pred)\nmcc = matthews_corrcoef(y_test_encoded, y_pred)\naccuracy = accuracy_score(y_test_encoded, y_pred)\n\nresults = {\n    \"Accuracy\": accuracy,\n    \"Macro F1-score\": macro_f1,\n    \"Macro Balanced Accuracy\": macro_bal_acc,\n    \"Matthews CC\": mcc,\n    \"Inference Runtime (s)\": inference_runtime\n}\n\nfor k, v in results.items():\n    print(f\"{k}: {v:.6f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:07.134790Z","iopub.execute_input":"2026-01-07T10:41:07.135078Z","iopub.status.idle":"2026-01-07T10:41:07.154160Z","shell.execute_reply.started":"2026-01-07T10:41:07.135052Z","shell.execute_reply":"2026-01-07T10:41:07.153206Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Create Submission Dataset","metadata":{}},{"cell_type":"code","source":"test_pred_enc = model.predict(df_test[top_cols])\n# decode\ntest_pred = le.inverse_transform(test_pred_enc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:07.155848Z","iopub.execute_input":"2026-01-07T10:41:07.156305Z","iopub.status.idle":"2026-01-07T10:41:07.249566Z","shell.execute_reply.started":"2026-01-07T10:41:07.156274Z","shell.execute_reply":"2026-01-07T10:41:07.248246Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test_pred = df_test.copy()\ndf_test_pred['class_label'] = test_pred\n# get submission csv for id, class_label\ndf_test_pred = df_test_pred[['id', 'class_label']]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:07.251091Z","iopub.execute_input":"2026-01-07T10:41:07.251520Z","iopub.status.idle":"2026-01-07T10:41:07.483573Z","shell.execute_reply.started":"2026-01-07T10:41:07.251478Z","shell.execute_reply":"2026-01-07T10:41:07.482441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test_pred.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:07.485020Z","iopub.execute_input":"2026-01-07T10:41:07.485578Z","iopub.status.idle":"2026-01-07T10:41:07.502132Z","shell.execute_reply.started":"2026-01-07T10:41:07.485533Z","shell.execute_reply":"2026-01-07T10:41:07.501228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test_pred.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:07.503648Z","iopub.execute_input":"2026-01-07T10:41:07.504067Z","iopub.status.idle":"2026-01-07T10:41:07.515225Z","shell.execute_reply.started":"2026-01-07T10:41:07.504027Z","shell.execute_reply":"2026-01-07T10:41:07.514027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_test.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-07T10:41:07.516576Z","iopub.execute_input":"2026-01-07T10:41:07.516903Z","iopub.status.idle":"2026-01-07T10:41:07.535743Z","shell.execute_reply.started":"2026-01-07T10:41:07.516876Z","shell.execute_reply":"2026-01-07T10:41:07.534695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}