{"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":"import glob\nimport numpy as np\nimport pathlib\nimport pandas as pd\nimport polars as pl\nfrom typing import Tuple","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.982986Z","iopub.status.idle":"2023-05-20T11:30:03.983412Z","shell.execute_reply.started":"2023-05-20T11:30:03.983208Z","shell.execute_reply":"2023-05-20T11:30:03.983227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_ROOT = '/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/'\nSEED = 42","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.984451Z","iopub.status.idle":"2023-05-20T11:30:03.985657Z","shell.execute_reply.started":"2023-05-20T11:30:03.985391Z","shell.execute_reply":"2023-05-20T11:30:03.985415Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Loading","metadata":{}},{"cell_type":"code","source":"def load_defog_and_tdcsfog_df(path: str = \"train\") -> Tuple[pl.DataFrame, pl.DataFrame]:\n    defog_df = pl.DataFrame()\n    tdcsfog_df = pl.DataFrame()\n    \n    paths = glob.glob(DATA_ROOT + path + '/**/**')\n\n    for i, path in enumerate(paths):\n    \n        if \"notype\" in path: \n            continue\n    \n        id_ = pathlib.Path(path).parts[-1].split('.')[0]\n        type_ = pathlib.Path(path).parts[-2]\n        \n        tmp = pl.read_csv(path)\n        # idのカラム追加\n        tmp = tmp.with_columns(pl.Series(\"Id\", [id_] * len(tmp)))\n        # typeのカラム追加\n        tmp = tmp.with_columns(pl.Series(\"type\", [type_] * len(tmp)))\n    \n        if type_ == \"defog\":\n            if path == \"train\":\n                tmp = tmp.filter((pl.col(\"Valid\") > 0) & (pl.col(\"Task\") > 0))\n            defog_df = pl.concat([defog_df, tmp])\n        elif type_ == \"tdcsfog\":\n            tdcsfog_df = pl.concat([tdcsfog_df, tmp])\n        \n    return (defog_df, tdcsfog_df)\n\ntrain_defog_df, train_tdcsfog_df = load_defog_and_tdcsfog_df(\"train\")\ntest_defog_df, test_tdcsfog_df = load_defog_and_tdcsfog_df(\"test\")","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.988994Z","iopub.status.idle":"2023-05-20T11:30:03.989845Z","shell.execute_reply.started":"2023-05-20T11:30:03.989641Z","shell.execute_reply":"2023-05-20T11:30:03.989661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Subjects\nfrom sklearn.cluster import KMeans\n\nN_S_CLUSTER = 8\n\n# Subjectsのデータ読み込み\nsubjects_metadata = pl.read_csv(DATA_ROOT + \"/subjects.csv\")\n\n# Subjectを加工\nsubjects_metadata = (subjects_metadata\n                     .with_columns(pl.col(\"Sex\").rank(\"dense\")-1) # 性別をカテゴリカル特徴\n                     .fill_null(0) # NULLを0埋め\n                )\n\n# クラスタリング結果を追加\nX = subjects_metadata.drop(\"Subject\").to_pandas()\nkmeans_label = KMeans(n_clusters=N_S_CLUSTER, random_state=SEED).fit_predict(X)\n\nsubjects_metadata = (subjects_metadata\n                     .with_columns(pl.Series(kmeans_label).alias(\"s_kmeans\"))\n)\nsubjects_metadata","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.990973Z","iopub.status.idle":"2023-05-20T11:30:03.991778Z","shell.execute_reply.started":"2023-05-20T11:30:03.991569Z","shell.execute_reply":"2023-05-20T11:30:03.991593Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"N_T_CLUSTER = 8\n\n# Task(defogのみ)\ntask_metadata = pl.read_csv(DATA_ROOT + \"/tasks.csv\")\ntask_metadata = (task_metadata\n                 .with_columns(Duration=(pl.col(\"End\") - pl.col(\"Begin\"))) # タスクの経過時間を追加\n                 .with_columns(pl.col(\"Task\").rank(\"dense\")-1)\n                 .with_columns(Time=pl.arange(pl.col(\"Begin\"),pl.col(\"End\")))\n                )\ntask_metadata = task_metadata.explode(\"Time\")\n\n# クラスタリング結果を追加\nX = task_metadata.drop(\"Id\", \"Time\").to_pandas()\nkmeans_label = KMeans(n_clusters=N_T_CLUSTER, random_state=SEED).fit_predict(X)\n\ntask_metadata = (task_metadata\n                     .with_columns(pl.Series(kmeans_label).alias(\"t_kmeans\"))\n)\ntask_metadata","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.993632Z","iopub.status.idle":"2023-05-20T11:30:03.994108Z","shell.execute_reply.started":"2023-05-20T11:30:03.993898Z","shell.execute_reply":"2023-05-20T11:30:03.993919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# メタデータの読み込み\ndefog_metadata = pl.read_csv(DATA_ROOT + \"/defog_metadata.csv\")\ndefog_metadata = (defog_metadata.with_columns(pl.col(\"Medication\").rank(\"dense\")-1)) # 投薬の投与をカテゴリカル特徴\ndefog_metadata = (defog_metadata\n                  .join(subjects_metadata, on=[\"Subject\", \"Visit\"], how=\"left\")\n                  .drop(\"Subject\"))\n\ntdcsfog_metadata = pl.read_csv(DATA_ROOT + \"/tdcsfog_metadata.csv\")\ntdcsfog_metadata = (tdcsfog_metadata.with_columns(pl.col(\"Medication\").rank(\"dense\")-1))\ntdcsfog_metadata = (tdcsfog_metadata\n                    .join(subjects_metadata, on=[\"Subject\"], how=\"left\")\n                    .drop(\"Subject\"))","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.996232Z","iopub.status.idle":"2023-05-20T11:30:03.996692Z","shell.execute_reply.started":"2023-05-20T11:30:03.996482Z","shell.execute_reply":"2023-05-20T11:30:03.996501Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# メタデータの情報をdfに追加\ntrain_defog_df = (train_defog_df\n                  .join(defog_metadata, on=[\"Id\"], how=\"left\")\n                  .join(task_metadata, on=[\"Id\", \"Time\"], how=\"left\")\n                 )\ntrain_tdcsfog_df = (train_tdcsfog_df.join(tdcsfog_metadata, on=\"Id\", how=\"left\"))\n\ntest_defog_df = (test_defog_df\n                 .join(defog_metadata, on=[\"Id\"], how=\"left\")\n                 .join(task_metadata, on=[\"Id\", \"Time\"], how=\"left\")\n                )\ntest_tdcsfog_df = (test_tdcsfog_df.join(tdcsfog_metadata, on=\"Id\", how=\"left\"))","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.997887Z","iopub.status.idle":"2023-05-20T11:30:03.998274Z","shell.execute_reply.started":"2023-05-20T11:30:03.998075Z","shell.execute_reply":"2023-05-20T11:30:03.998092Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_defog_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:03.999997Z","iopub.status.idle":"2023-05-20T11:30:04.000393Z","shell.execute_reply.started":"2023-05-20T11:30:04.000205Z","shell.execute_reply":"2023-05-20T11:30:04.000221Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_defog_df.describe()","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.001614Z","iopub.status.idle":"2023-05-20T11:30:04.001974Z","shell.execute_reply.started":"2023-05-20T11:30:04.001794Z","shell.execute_reply":"2023-05-20T11:30:04.001810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_tdcsfog_df.describe()","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.003629Z","iopub.status.idle":"2023-05-20T11:30:04.003987Z","shell.execute_reply.started":"2023-05-20T11:30:04.003809Z","shell.execute_reply":"2023-05-20T11:30:04.003826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data Preprocessing","metadata":{}},{"cell_type":"code","source":"# defog/tdcsfogの共通する前処理\ndef preprocess_df(df: pl.DataFrame) -> pl.DataFrame:\n    tmp = df\n    # Accの統計情報を取得\n    acc_cols = ['AccV', 'AccML', 'AccAP']\n    for acc_col in acc_cols:\n        tmp_g = (\n                tmp.groupby(\"Id\").agg([\n                    pl.col(acc_col).mean().alias(f\"{acc_col}_mean\"), \n                    pl.col(acc_col).max().alias(f\"{acc_col}_max\"),\n                    pl.col(acc_col).min().alias(f\"{acc_col}_min\"),\n                    pl.col(acc_col).std().alias(f\"{acc_col}_std\"),\n                    pl.col(acc_col).first().alias(f\"{acc_col}_first\"),\n                    pl.col(acc_col).last().alias(f\"{acc_col}_last\"),\n                    (pl.col(acc_col).max() - pl.col(acc_col).min()).alias(f\"{acc_col}_delta\")\n                ])\n            )\n        tmp = (tmp.join(tmp_g, on=[\"Id\"], how=\"left\"))\n    tmp = (tmp\n           .with_columns((pl.col(\"AccV\") + pl.col(\"AccML\") + pl.col(\"AccAP\")).alias(\"Stride\"))\n           .with_columns(pl.col(\"Stride\").abs().sqrt().alias(\"Step\"))\n    )\n    return tmp\n\ndef split_x_and_y(df: pl.DataFrame) -> Tuple[pl.DataFrame, pl.DataFrame]:\n    df_x = df.drop('StartHesitation', 'Turn', 'Walking')\n    df_y = df.select([\n        'StartHesitation', 'Turn', 'Walking'\n    ])\n    return df_x, df_y","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.005277Z","iopub.status.idle":"2023-05-20T11:30:04.005942Z","shell.execute_reply.started":"2023-05-20T11:30:04.005744Z","shell.execute_reply":"2023-05-20T11:30:04.005764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_defog_df = preprocess_df(train_defog_df)","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.007094Z","iopub.status.idle":"2023-05-20T11:30:04.007631Z","shell.execute_reply.started":"2023-05-20T11:30:04.007437Z","shell.execute_reply":"2023-05-20T11:30:04.007457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_tdcsfog_df = preprocess_df(train_tdcsfog_df)","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.008553Z","iopub.status.idle":"2023-05-20T11:30:04.008907Z","shell.execute_reply.started":"2023-05-20T11:30:04.008731Z","shell.execute_reply":"2023-05-20T11:30:04.008748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_defog_df, y_defog_df = split_x_and_y(train_defog_df)\nX_tdcsfog_df, y_tdcsfog_df = split_x_and_y(train_tdcsfog_df)","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.009939Z","iopub.status.idle":"2023-05-20T11:30:04.010314Z","shell.execute_reply.started":"2023-05-20T11:30:04.010119Z","shell.execute_reply":"2023-05-20T11:30:04.010135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Training (LightGBMRegressor)\n- LightGBMMultiRegressior was adopted based on https://www.kaggle.com/code/nickcgray/gait-prediction (Thank you for sharing!)","metadata":{}},{"cell_type":"code","source":"import lightgbm as lgb\nfrom sklearn.multioutput import MultiOutputRegressor\nfrom sklearn.base import clone\nfrom sklearn.metrics import average_precision_score, make_scorer\n\nbest_params_ = {'colsample_bytree': 0.5282057895135501,\n 'learning_rate': 0.22659963168004743,\n 'max_depth': 8,\n 'min_child_weight': 3.1233911067827616,\n 'n_estimators': 291,\n 'subsample': 0.9961057796456088,\n 'num_boost_round': 10\n}\n\ndef custom_average_precision(y_true, y_pred):\n    score = average_precision_score(y_true, y_pred)\n    return 'average_precision', score, True\n\nclass LGBMMultiOutputRegressor(MultiOutputRegressor):\n    def fit(self, X, y, eval_set=None, **fit_params):\n        self.estimators_ = [clone(self.estimator) for _ in range(y.shape[1])]\n        \n        for i, estimator in enumerate(self.estimators_):\n            if eval_set:\n                fit_params['eval_set'] = [(eval_set[0], eval_set[1][:, i])]\n            estimator.fit(X, y[:, i], **fit_params)\n        \n        return self","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.011534Z","iopub.status.idle":"2023-05-20T11:30:04.011904Z","shell.execute_reply.started":"2023-05-20T11:30:04.011718Z","shell.execute_reply":"2023-05-20T11:30:04.011735Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import lightgbm as lgb\nfrom sklearn.model_selection import GroupKFold\n\nregs_defog = []\ncvs_defog = []\ngkf = GroupKFold(n_splits=5)\n\n\nX = X_defog_df.drop(\"Id\", \"type\", \"Valid\", \"Task\").to_numpy()\ny = y_defog_df.to_numpy()\n\nsplits = gkf.split(X, y, groups=X_defog_df['Id'].to_numpy())\n\nfor train_idx, val_idx in splits:\n    # train/valデータを作成する\n    X_train = X[train_idx,:]\n    y_train = y[train_idx,:]\n    X_val = X[val_idx,:]\n    y_val = y[val_idx,:]\n    \n    multioutput_regressor = LGBMMultiOutputRegressor(lgb.LGBMRegressor(**best_params_))\n    \n    multioutput_regressor.fit(\n        X_train, y_train,\n        eval_set=(X_val, y_val),\n        eval_metric=custom_average_precision,\n        early_stopping_rounds=10,\n        verbose = 0,\n    )\n    regs_defog.append(multioutput_regressor)\n    \n    cv = average_precision_score(y_val, multioutput_regressor.predict(X_val).clip(0.0,1.0))\n    \n    cvs_defog.append(cv)\n    \nprint(cvs_defog)\nprint(np.mean(cvs_defog))","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.013022Z","iopub.status.idle":"2023-05-20T11:30:04.013446Z","shell.execute_reply.started":"2023-05-20T11:30:04.013240Z","shell.execute_reply":"2023-05-20T11:30:04.013260Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"regs_tdcsfog = []\ncvs_tdcsfog = []\ngkf = GroupKFold(n_splits=5)\n\n\nX = X_tdcsfog_df.drop(\"Id\", \"type\").to_numpy()\ny = y_tdcsfog_df.to_numpy()\n\nsplits = gkf.split(X, y, groups=X_tdcsfog_df['Id'].to_numpy())\n\nfor train_idx, val_idx in splits:\n    # train/valデータを作成する\n    X_train = X[train_idx,:]\n    y_train = y[train_idx,:]\n    X_val = X[val_idx,:]\n    y_val = y[val_idx,:]\n    \n    multioutput_regressor = LGBMMultiOutputRegressor(lgb.LGBMRegressor(**best_params_))\n    \n    multioutput_regressor.fit(\n        X_train, y_train,\n        eval_set=(X_val, y_val),\n        eval_metric=custom_average_precision,\n        early_stopping_rounds=10,\n        verbose = 0,\n    )\n    regs_tdcsfog.append(multioutput_regressor)\n    \n    cv = average_precision_score(y_val, multioutput_regressor.predict(X_val).clip(0.0,1.0))\n    \n    cvs_tdcsfog.append(cv)\n    \nprint(cvs_tdcsfog)\nprint(np.mean(cvs_tdcsfog))","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.015083Z","iopub.status.idle":"2023-05-20T11:30:04.015609Z","shell.execute_reply.started":"2023-05-20T11:30:04.015365Z","shell.execute_reply":"2023-05-20T11:30:04.015389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predict ","metadata":{}},{"cell_type":"code","source":"test_defog_df = preprocess_df(test_defog_df)\ntest_tdcsfog_df = preprocess_df(test_tdcsfog_df)","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.017276Z","iopub.status.idle":"2023-05-20T11:30:04.017702Z","shell.execute_reply.started":"2023-05-20T11:30:04.017496Z","shell.execute_reply":"2023-05-20T11:30:04.017513Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_defog_test = test_defog_df.drop(\"Id\", \"type\").to_numpy()\nprobs_defog = [reg.predict(X_defog_test).clip(0.0,1.0) for reg in regs_defog]\navg_probs_defog = np.average(np.array(probs_defog), axis=0)\nprint(avg_probs_defog.shape)","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.022118Z","iopub.status.idle":"2023-05-20T11:30:04.022597Z","shell.execute_reply.started":"2023-05-20T11:30:04.022379Z","shell.execute_reply":"2023-05-20T11:30:04.022397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result_defog_test = (test_defog_df.with_columns(\n    pl.concat_str([\n        pl.col(\"Id\"),\n        pl.col(\"Time\")\n    ],\n    separator=\"_\",\n    ).alias(\"Id\")).select(\"Id\"))\nresult_defog_test = (result_defog_test\n                     .with_columns(pl.Series(avg_probs_defog[:,0]).alias(\"StartHesitation\"))\n                     .with_columns(pl.Series(avg_probs_defog[:,1]).alias(\"Turn\"))\n                     .with_columns(pl.Series(avg_probs_defog[:,2]).alias(\"Walking\")))","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.023993Z","iopub.status.idle":"2023-05-20T11:30:04.024414Z","shell.execute_reply.started":"2023-05-20T11:30:04.024217Z","shell.execute_reply":"2023-05-20T11:30:04.024235Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_tdcsfog_test = test_tdcsfog_df.drop(\"Id\", \"type\").to_numpy()\nprobs_tdcsfog = [reg.predict(X_tdcsfog_test).clip(0.0,1.0) for reg in regs_tdcsfog]\navg_probs_tdcsfog = np.average(np.array(probs_tdcsfog), axis=0)\nprint(avg_probs_tdcsfog.shape)","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.026281Z","iopub.status.idle":"2023-05-20T11:30:04.026709Z","shell.execute_reply.started":"2023-05-20T11:30:04.026499Z","shell.execute_reply":"2023-05-20T11:30:04.026517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result_tdcsfog_test = (test_tdcsfog_df.with_columns(\n    pl.concat_str([\n        pl.col(\"Id\"),\n        pl.col(\"Time\")\n    ],\n    separator=\"_\",\n    ).alias(\"Id\")).select(\"Id\"))\nresult_tdcsfog_test = (result_tdcsfog_test\n                     .with_columns(pl.Series(avg_probs_tdcsfog[:,0]).alias(\"StartHesitation\"))\n                     .with_columns(pl.Series(avg_probs_tdcsfog[:,1]).alias(\"Turn\"))\n                     .with_columns(pl.Series(avg_probs_tdcsfog[:,2]).alias(\"Walking\")))","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.027953Z","iopub.status.idle":"2023-05-20T11:30:04.028378Z","shell.execute_reply.started":"2023-05-20T11:30:04.028184Z","shell.execute_reply":"2023-05-20T11:30:04.028202Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result_test = pl.concat([result_defog_test, result_tdcsfog_test])","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.030722Z","iopub.status.idle":"2023-05-20T11:30:04.031170Z","shell.execute_reply.started":"2023-05-20T11:30:04.030953Z","shell.execute_reply":"2023-05-20T11:30:04.030972Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result_test","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.033334Z","iopub.status.idle":"2023-05-20T11:30:04.033789Z","shell.execute_reply.started":"2023-05-20T11:30:04.033588Z","shell.execute_reply":"2023-05-20T11:30:04.033607Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"result_test.write_csv(\"submission.csv\", separator=\",\")","metadata":{"execution":{"iopub.status.busy":"2023-05-20T11:30:04.035689Z","iopub.status.idle":"2023-05-20T11:30:04.036145Z","shell.execute_reply.started":"2023-05-20T11:30:04.035923Z","shell.execute_reply":"2023-05-20T11:30:04.035943Z"},"trusted":true},"execution_count":null,"outputs":[]}]}