{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\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# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:09:30.209138Z","iopub.execute_input":"2025-06-26T17:09:30.209631Z","iopub.status.idle":"2025-06-26T17:09:30.707584Z","shell.execute_reply.started":"2025-06-26T17:09:30.209584Z","shell.execute_reply":"2025-06-26T17:09:30.706297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport polars as pl\nimport matplotlib.pyplot as plt\nimport seaborn as sns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:09:30.709451Z","iopub.execute_input":"2025-06-26T17:09:30.710044Z","iopub.status.idle":"2025-06-26T17:09:31.597942Z","shell.execute_reply.started":"2025-06-26T17:09:30.710014Z","shell.execute_reply":"2025-06-26T17:09:31.597110Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/cmi-detect-behavior-with-sensor-data/\"\nTRAIN_PATH = DATA_DIR + \"train.csv\"\nTRAIN_DEMO_PATH = DATA_DIR + \"train_demographics.csv\"\n\ntrain_df = pd.read_csv(TRAIN_PATH)\ndem_df = pd.read_csv(TRAIN_DEMO_PATH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:09:31.598970Z","iopub.execute_input":"2025-06-26T17:09:31.599369Z","iopub.status.idle":"2025-06-26T17:10:05.635298Z","shell.execute_reply.started":"2025-06-26T17:09:31.599345Z","shell.execute_reply":"2025-06-26T17:10:05.634090Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Train shape:\", train_df.shape)\ntrain_df.head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:05.637114Z","iopub.execute_input":"2025-06-26T17:10:05.637435Z","iopub.status.idle":"2025-06-26T17:10:05.666602Z","shell.execute_reply.started":"2025-06-26T17:10:05.637400Z","shell.execute_reply":"2025-06-26T17:10:05.665588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Demographics shape:\", dem_df.shape)\ndem_df.head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:05.667417Z","iopub.execute_input":"2025-06-26T17:10:05.667673Z","iopub.status.idle":"2025-06-26T17:10:05.680522Z","shell.execute_reply.started":"2025-06-26T17:10:05.667651Z","shell.execute_reply":"2025-06-26T17:10:05.679516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# シーケンスごとに一意のgestureを取得して、その分布を集計\ngesture_counts = train_df[['sequence_id', 'gesture']].drop_duplicates().gesture.value_counts()\nprint(\"ユニークジェスチャー数:\", gesture_counts.shape[0])\nprint(gesture_counts)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:05.681667Z","iopub.execute_input":"2025-06-26T17:10:05.682109Z","iopub.status.idle":"2025-06-26T17:10:05.788063Z","shell.execute_reply.started":"2025-06-26T17:10:05.682074Z","shell.execute_reply":"2025-06-26T17:10:05.786866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 振る舞いごとに一意のgestureを取得して、その分布を集計\nbehavior_counts = train_df[['sequence_id','behavior']].drop_duplicates().behavior.value_counts()\nprint(\"\\nユニークな振る舞いの数:\", behavior_counts.shape[0])\nprint(behavior_counts)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:05.788932Z","iopub.execute_input":"2025-06-26T17:10:05.789275Z","iopub.status.idle":"2025-06-26T17:10:05.895163Z","shell.execute_reply.started":"2025-06-26T17:10:05.789241Z","shell.execute_reply":"2025-06-26T17:10:05.893906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ターゲット(BFRB)と非ターゲットの比率\nseq_types = train_df[['sequence_id', 'sequence_type']].drop_duplicates().sequence_type.value_counts()\nprint(\"\\nTarget/Non-Targetシーケンス数:\\n\", seq_types)\nprint(\"ターゲット(%) = {:.1f}%\".format(100 * seq_types[\"Target\"] / seq_types.sum()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:05.897015Z","iopub.execute_input":"2025-06-26T17:10:05.897367Z","iopub.status.idle":"2025-06-26T17:10:05.988761Z","shell.execute_reply.started":"2025-06-26T17:10:05.897342Z","shell.execute_reply":"2025-06-26T17:10:05.987531Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 棒グラフで可視化（ターゲット vs 非ターゲット）\nplt.figure(figsize=(4,4))\nsns.barplot(x=seq_types.index, y=seq_types.values)\nplt.title(\"sequence: Target vs Non-Target\")\nplt.ylabel(\"sequence count\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:05.989757Z","iopub.execute_input":"2025-06-26T17:10:05.990000Z","iopub.status.idle":"2025-06-26T17:10:06.142578Z","shell.execute_reply.started":"2025-06-26T17:10:05.989980Z","shell.execute_reply":"2025-06-26T17:10:06.141278Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# シーケンスの長さ（各sequence_idに対応する行数）を集計\nseq_lengths = train_df.groupby('sequence_id')['sequence_counter'].max() + 1  # 0始まりなので+1\nprint(seq_lengths.describe())\nplt.figure(figsize=(6,4))\nsns.histplot(seq_lengths, bins=20, kde=False)\nplt.title(\"sequence length\")\nplt.xlabel(\"time step of sequence\")\nplt.ylabel(\"sequence count\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:06.146061Z","iopub.execute_input":"2025-06-26T17:10:06.146453Z","iopub.status.idle":"2025-06-26T17:10:06.419010Z","shell.execute_reply.started":"2025-06-26T17:10:06.146417Z","shell.execute_reply":"2025-06-26T17:10:06.417927Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 例として特定のBFRBジェスチャーと非BFRBジェスチャーのシーケンスを各1つ抽出\nexample_bfrb = train_df[train_df['gesture'] == 'Above ear - pull hair']  # 髪を引っぱるの最初の200サンプル\nexample_non = train_df[train_df['gesture'] == 'Glasses on/off']  # 眼鏡を直すの最初の200サンプル\n\n# 加速度の時系列をプロット\nplt.figure(figsize=(10,4))\nplt.plot(example_bfrb['sequence_counter'], example_bfrb['acc_x'], label='Hair pulling - acc_x')\nplt.plot(example_bfrb['sequence_counter'], example_bfrb['acc_y'], label='Hair pulling - acc_y')\nplt.plot(example_bfrb['sequence_counter'], example_bfrb['acc_z'], label='Hair pulling - acc_z')\nplt.title(\"example: acc in behavior of pulling hair\")\nplt.xlabel(\"time step\")\nplt.ylabel(\"acc (m/s^2)\")\nplt.legend()\nplt.show()\n\nplt.figure(figsize=(10,4))\nplt.plot(example_non['sequence_counter'], example_non['acc_x'], label='Adjust glasses - acc_x')\nplt.plot(example_non['sequence_counter'], example_non['acc_y'], label='Adjust glasses - acc_y')\nplt.plot(example_non['sequence_counter'], example_non['acc_z'], label='Adjust glasses - acc_z')\nplt.title(\"example: acc in behavior of glass on/off\")\nplt.xlabel(\"time step\")\nplt.ylabel(\"acc (m/s^2)\")\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:06.420392Z","iopub.execute_input":"2025-06-26T17:10:06.420815Z","iopub.status.idle":"2025-06-26T17:10:07.681177Z","shell.execute_reply.started":"2025-06-26T17:10:06.420779Z","shell.execute_reply":"2025-06-26T17:10:07.680107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 欠損率の計算\nnan_ratio = train_df.isna().mean()  # 各列のNaN比率\nnan_cols = nan_ratio[nan_ratio > 0]\nprint(\"欠損値の存在する列数:\", nan_cols.shape[0])\nnan_cols.sort_values(ascending=False).head(20)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:07.682561Z","iopub.execute_input":"2025-06-26T17:10:07.682869Z","iopub.status.idle":"2025-06-26T17:10:08.591887Z","shell.execute_reply.started":"2025-06-26T17:10:07.682840Z","shell.execute_reply":"2025-06-26T17:10:08.590962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 成人/小児の人数と年齢分布\nprint(dem_df['adult_child'].value_counts())\nprint(\"年齢の最小,中央値,最大:\", dem_df['age'].min(), dem_df['age'].median(), dem_df['age'].max())\n\n# 身長や腕の長さの基本統計量\nprint(dem_df[['height_cm','shoulder_to_wrist_cm','elbow_to_wrist_cm']].describe())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:08.592997Z","iopub.execute_input":"2025-06-26T17:10:08.593361Z","iopub.status.idle":"2025-06-26T17:10:08.612833Z","shell.execute_reply.started":"2025-06-26T17:10:08.593327Z","shell.execute_reply":"2025-06-26T17:10:08.611939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 前処理：NaNを補間する関数を定義\ndef fill_na_values(df: pd.DataFrame) -> pd.DataFrame:\n    df = df.copy()\n    # サーモパイルNaNを0で補完\n    thm_cols = [c for c in df.columns if c.startswith('thm_')]\n    df[thm_cols] = df[thm_cols].fillna(0.0)\n    # ToF NaNを-1で補完\n    tof_cols = [c for c in df.columns if c.startswith('tof_')]\n    df[tof_cols] = df[tof_cols].fillna(-1)\n    return df\n\n# 1. Gestureフェーズに絞る\ngesture_df = train_df[train_df['behavior'] == 'Performs gesture'].copy()\ngesture_df = fill_na_values(gesture_df)\n\n# 2. シーケンスごとに集計 -> 特徴量データフレーム作成\nfeatures_list = []  # ここに各シーケンスの特徴量辞書を追加\nfor seq_id, seq_data in gesture_df.groupby('sequence_id'):\n    seq_feat = {\"sequence_id\": seq_id}\n    # IMU特徴量: accとrotの平均と標準偏差\n    for axis in ['acc_x','acc_y','acc_z','rot_w','rot_x','rot_y','rot_z']:\n        seq_feat[f\"{axis}_mean\"] = seq_data[axis].mean()\n        seq_feat[f\"{axis}_std\"]  = seq_data[axis].std()\n    # サーモパイル特徴量: 平均と最大\n    for i in range(1,6):\n        col = f\"thm_{i}\"\n        seq_feat[f\"{col}_mean\"] = seq_data[col].mean()\n        seq_feat[f\"{col}_max\"]  = seq_data[col].max()\n    # ToF特徴量: 最小距離と検出率\n    for i in range(1,6):\n        # 該当センサーの64列を抽出\n        tof_cols = [f\"tof_{i}_v{j}\" for j in range(64)]\n        tof_vals = seq_data[tof_cols].values  # (時間長さ x 64)のnumpy配列\n        # 検出ありの値のみ考慮するため、-1をnp.nanに置換\n        tof_vals_flat = tof_vals.flatten()\n        tof_vals_flat = np.where(tof_vals_flat == -1, np.nan, tof_vals_flat)\n        if np.all(np.isnan(tof_vals_flat)):\n            # 一度も検出がない場合\n            min_dist = 255  # 検出なしを255とする（255は通常範囲外の大きな値として仮定）\n            detect_rate = 0.0\n        else:\n            min_dist = np.nanmin(tof_vals_flat)\n            # フレームごとに少なくとも1つ検出があったかを判定\n            frame_detect = np.any(np.isfinite(tof_vals), axis=1)  # True/False per time\n            detect_rate = frame_detect.mean()\n        seq_feat[f\"tof_{i}_min_dist\"] = min_dist\n        seq_feat[f\"tof_{i}_detect_rate\"] = detect_rate\n    # デモグラ情報付加\n    subj = seq_data['subject'].iloc[0]\n    dem_row = dem_df[dem_df['subject'] == subj].iloc[0]\n    seq_feat[\"age\"] = dem_row[\"age\"]\n    seq_feat[\"sex\"] = dem_row[\"sex\"]\n    seq_feat[\"handedness\"] = dem_row[\"handedness\"]\n    seq_feat[\"height_cm\"] = dem_row[\"height_cm\"]\n    seq_feat[\"shoulder_to_wrist_cm\"] = dem_row[\"shoulder_to_wrist_cm\"]\n    seq_feat[\"elbow_to_wrist_cm\"] = dem_row[\"elbow_to_wrist_cm\"]\n    features_list.append(seq_feat)\n\ntrain_features_df = pd.DataFrame(features_list)\nprint(\"特徴量データフレームのサイズ:\", train_features_df.shape)\ntrain_features_df.head(5)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:10:08.613870Z","iopub.execute_input":"2025-06-26T17:10:08.614143Z","iopub.status.idle":"2025-06-26T17:12:04.499283Z","shell.execute_reply.started":"2025-06-26T17:10:08.614119Z","shell.execute_reply":"2025-06-26T17:12:04.498052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ラベル列を付加（gesture名とsequence_type）\nseq_labels = train_df[['sequence_id','gesture','sequence_type']].drop_duplicates(subset='sequence_id')\ntrain_features_df = train_features_df.merge(seq_labels, on='sequence_id', how='left')\nprint(\"特徴量データにラベルを結合したサイズ:\", train_features_df.shape)\ntrain_features_df[['sequence_id','gesture','sequence_type']].head(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:04.500438Z","iopub.execute_input":"2025-06-26T17:12:04.500813Z","iopub.status.idle":"2025-06-26T17:12:04.617057Z","shell.execute_reply.started":"2025-06-26T17:12:04.500777Z","shell.execute_reply":"2025-06-26T17:12:04.616058Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import f1_score, classification_report\n\n# 便利のため、Target/Non-Target判定用の辞書を作成\ngesture_to_type = {row['gesture']: row['sequence_type'] for _, row in seq_labels.iterrows()}\ntarget_gestures = [g for g,t in gesture_to_type.items() if t==\"Target\"]\n\n# 特徴量データから学習用データ構築\nX = train_features_df.drop(columns=['sequence_id','gesture','sequence_type'])\ny = train_features_df['gesture']  # 多クラスラベル（18クラス）\n\n# 被験者が混ざらないよう、subjectでグループ分割\nsubjects = train_df[['sequence_id','subject']].drop_duplicates()['subject']\nprint(f\"X: {X.shape}, y: {y.shape}, subjects: {subjects.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:04.618205Z","iopub.execute_input":"2025-06-26T17:12:04.618504Z","iopub.status.idle":"2025-06-26T17:12:05.336353Z","shell.execute_reply.started":"2025-06-26T17:12:04.618473Z","shell.execute_reply":"2025-06-26T17:12:05.335273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = X.fillna(0.0)\nX.head(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:05.337649Z","iopub.execute_input":"2025-06-26T17:12:05.338023Z","iopub.status.idle":"2025-06-26T17:12:05.363443Z","shell.execute_reply.started":"2025-06-26T17:12:05.337986Z","shell.execute_reply":"2025-06-26T17:12:05.362131Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X.columns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:05.364617Z","iopub.execute_input":"2025-06-26T17:12:05.365018Z","iopub.status.idle":"2025-06-26T17:12:05.373111Z","shell.execute_reply.started":"2025-06-26T17:12:05.364978Z","shell.execute_reply":"2025-06-26T17:12:05.372148Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y.head(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:05.374140Z","iopub.execute_input":"2025-06-26T17:12:05.374445Z","iopub.status.idle":"2025-06-26T17:12:05.394925Z","shell.execute_reply.started":"2025-06-26T17:12:05.374421Z","shell.execute_reply":"2025-06-26T17:12:05.393866Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 最後の１行だけドロップする\nsubjects = subjects.iloc[:-1]\nsubjects.head(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:05.395981Z","iopub.execute_input":"2025-06-26T17:12:05.396416Z","iopub.status.idle":"2025-06-26T17:12:05.413104Z","shell.execute_reply.started":"2025-06-26T17:12:05.396379Z","shell.execute_reply":"2025-06-26T17:12:05.411986Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train, X_val, y_train, y_val, sub_train, sub_val = train_test_split(\n    X, y, subjects, test_size=0.2, stratify=y, random_state=42\n)\nprint(\"学習データサイズ:\", X_train.shape, \"検証データサイズ:\", X_val.shape)\n\n# ベースモデル：IMU特徴量のみを使用\nimu_features = [c for c in X.columns if c.startswith('acc_') or c.startswith('rot_')]\nclf_baseline = RandomForestClassifier(n_estimators=100, random_state=0)\nclf_baseline.fit(X_train[imu_features], y_train)\n\n# 検証データで予測\ny_pred_base = clf_baseline.predict(X_val[imu_features])\nprint(\"Baselineモデルの分類レポート:\")\nprint(classification_report(y_val, y_pred_base, zero_division=0))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:05.414348Z","iopub.execute_input":"2025-06-26T17:12:05.414802Z","iopub.status.idle":"2025-06-26T17:12:08.513629Z","shell.execute_reply.started":"2025-06-26T17:12:05.414769Z","shell.execute_reply":"2025-06-26T17:12:08.512509Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Binary F1とMacro F1の計算\n# 実際のsequence_type（Target/Non-Target）に基づき、予測もTarget/Non-Targetにマップする\ny_val_type = [gesture_to_type[g] for g in y_val] \ny_pred_type = [gesture_to_type[g] for g in y_pred_base]\nbinary_f1 = f1_score(y_val_type, y_pred_type, pos_label=\"Target\")\n# Macro F1: 8個のターゲットクラス＋Non-Target1クラス の計9クラスで計算\ny_val_macro = [g if gesture_to_type[g]==\"Target\" else \"NonTarget\" for g in y_val]\ny_pred_macro = [g if gesture_to_type[g]==\"Target\" else \"NonTarget\" for g in y_pred_base]\nlabels = [g for g,t in gesture_to_type.items() if t==\"Target\"] + [\"NonTarget\"]\nmacro_f1 = f1_score(y_val_macro, y_pred_macro, labels=labels, average='macro')\nprint(f\"Baselineモデル -> Binary F1 = {binary_f1:.3f}, Macro F1 = {macro_f1:.3f}, 平均 = {((binary_f1+macro_f1)/2):.3f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:08.514839Z","iopub.execute_input":"2025-06-26T17:12:08.515158Z","iopub.status.idle":"2025-06-26T17:12:08.551224Z","shell.execute_reply.started":"2025-06-26T17:12:08.515131Z","shell.execute_reply":"2025-06-26T17:12:08.549930Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 改善モデル：全特徴量を使用（IMU+サーモ+ToF+デモグラ）\nclf_improved = RandomForestClassifier(n_estimators=100, random_state=0)\nclf_improved.fit(X_train, y_train)\ny_pred_imp = clf_improved.predict(X_val)\n\n# 性能評価\nprint(\"Improvedモデルの分類レポート:\")\nprint(classification_report(y_val, y_pred_imp, zero_division=0))\ny_val_type = [gesture_to_type[g] for g in y_val]\ny_pred_type = [gesture_to_type[g] for g in y_pred_imp]\nbinary_f1_imp = f1_score(y_val_type, y_pred_type, pos_label=\"Target\")\ny_val_macro = [g if gesture_to_type[g]==\"Target\" else \"NonTarget\" for g in y_val]\ny_pred_macro = [g if gesture_to_type[g]==\"Target\" else \"NonTarget\" for g in y_pred_imp]\nmacro_f1_imp = f1_score(y_val_macro, y_pred_macro, labels=labels, average='macro')\nprint(f\"Improvedモデル -> Binary F1 = {binary_f1_imp:.3f}, Macro F1 = {macro_f1_imp:.3f}, 平均 = {((binary_f1_imp+macro_f1_imp)/2):.3f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:08.552573Z","iopub.execute_input":"2025-06-26T17:12:08.552998Z","iopub.status.idle":"2025-06-26T17:12:12.702326Z","shell.execute_reply.started":"2025-06-26T17:12:08.552960Z","shell.execute_reply":"2025-06-26T17:12:12.701151Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 特徴量重要度の取得と上位表示\nimportances = clf_improved.feature_importances_\nfeature_names = X.columns\nimp_df = pd.DataFrame({'feature': feature_names, 'importance': importances})\nimp_df = imp_df.sort_values('importance', ascending=False)\nprint(\"上位10特徴量:\\n\", imp_df.head(10))\n\n# 棒グラフで可視化\nplt.figure(figsize=(8,5))\nsns.barplot(x=imp_df.head(10)['importance'], y=imp_df.head(10)['feature'])\nplt.title(\"importance of feature top 10\")\nplt.xlabel(\"importance\")\nplt.ylabel(\"feature amount\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:12.703574Z","iopub.execute_input":"2025-06-26T17:12:12.703865Z","iopub.status.idle":"2025-06-26T17:12:12.950010Z","shell.execute_reply.started":"2025-06-26T17:12:12.703841Z","shell.execute_reply":"2025-06-26T17:12:12.948459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 推論用にグローバルでモデルとデモグラを準備\nmodel = clf_improved  # 学習済みモデルを使用\ndem_df_indexed = dem_df.set_index('subject')  # 被験者IDで検索しやすく\n\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    seq_pd = sequence.to_pandas()\n\n    # フェーズ情報があればGesture部分を抽出、なければ全行を使用\n    if 'behavior' in seq_pd.columns:\n        seq_pd = seq_pd[seq_pd['behavior'] == 'Performs gesture'].copy()\n    elif 'phase' in seq_pd.columns:\n        seq_pd = seq_pd[seq_pd['phase'] == 'Gesture'].copy()\n    else:\n        seq_pd = seq_pd.copy()\n\n    if seq_pd.empty:\n        raise ValueError(\"No usable rows found in the input sequence.\")\n\n    # NaN補間\n    seq_pd = fill_na_values(seq_pd)\n\n    feat = {}\n    # IMU特徴量\n    for axis in ['acc_x','acc_y','acc_z','rot_w','rot_x','rot_y','rot_z']:\n        feat[f\"{axis}_mean\"] = seq_pd[axis].mean()\n        feat[f\"{axis}_std\"]  = seq_pd[axis].std()\n\n    # サーモパイル特徴量\n    for i in range(1, 6):\n        col = f\"thm_{i}\"\n        if seq_pd[col].isna().all():\n            feat[f\"{col}_mean\"] = 26.0\n            feat[f\"{col}_max\"] = 26.0\n            continue\n        feat[f\"{col}_mean\"] = seq_pd[col].mean()\n        feat[f\"{col}_max\"] = seq_pd[col].max()\n\n    # ToF特徴量（flattenせず、画素ごとに処理）\n    for i in range(1, 6):\n        tof_cols = [\n            f\"tof_{i}_v{j}\" for j in range(64) \n            if f\"tof_{i}_v{j}\" in seq_pd.columns\n        ]\n        if not tof_cols:\n            feat[f\"tof_{i}_mean_dist\"] = 255.0\n            feat[f\"tof_{i}_min_dist\"] = 255.0\n            continue\n        # tof_colsのカラムがNaNの場合\n        if seq_pd[tof_cols].isna().all().all():\n            feat[f\"tof_{i}_mean_dist\"] = 255.0\n            feat[f\"tof_{i}_min_dist\"] = 255.0\n            continue\n        # -1を NaN に置換\n        vals = seq_pd[tof_cols].replace(-1, np.nan)\n        feat[f\"tof_{i}_mean_dist\"] = vals.mean(skipna=True).mean()\n        feat[f\"tof_{i}_min_dist\"] = vals.min(skipna=True).min()\n\n    # デモグラ特徴\n    subj = seq_pd['subject'].iloc[0]\n    if subj in dem_df_indexed.index:\n        dem_row = dem_df_indexed.loc[subj]\n        feat[\"age\"] = dem_row[\"age\"]\n        feat[\"sex\"] = dem_row[\"sex\"]\n        feat[\"handedness\"] = dem_row[\"handedness\"]\n        feat[\"height_cm\"] = dem_row[\"height_cm\"]\n        feat[\"shoulder_to_wrist_cm\"] = dem_row[\"shoulder_to_wrist_cm\"]\n        feat[\"elbow_to_wrist_cm\"] = dem_row[\"elbow_to_wrist_cm\"]\n    else:\n        feat[\"age\"] = dem_df[\"age\"].mean()\n        feat[\"sex\"] = 1\n        feat[\"handedness\"] = 1\n        feat[\"height_cm\"] = dem_df[\"height_cm\"].mean()\n        feat[\"shoulder_to_wrist_cm\"] = dem_df[\"shoulder_to_wrist_cm\"].mean()\n        feat[\"elbow_to_wrist_cm\"] = dem_df[\"elbow_to_wrist_cm\"].mean()\n\n    # 特徴量をモデルに合わせて並び替え\n    X_seq = pd.DataFrame([feat])\n    # 特徴量を学習時の順序に揃え、不足している列は0.0で埋める\n    X_seq = X_seq.reindex(model.feature_names_in_, axis=1, fill_value=0.0)\n    X_seq = X_seq.fillna(0.0)\n\n    pred_gesture = model.predict(X_seq)[0]\n    return pred_gesture\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:12.951389Z","iopub.execute_input":"2025-06-26T17:12:12.952173Z","iopub.status.idle":"2025-06-26T17:12:12.969828Z","shell.execute_reply.started":"2025-06-26T17:12:12.952122Z","shell.execute_reply":"2025-06-26T17:12:12.968410Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import kaggle_evaluation.cmi_inference_server\n\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    inference_server.serve()\nelse:\n    inference_server.run_local_gateway(\n        data_paths=(\n            '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv',\n            '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv',\n        )\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:12.971018Z","iopub.execute_input":"2025-06-26T17:12:12.971446Z","iopub.status.idle":"2025-06-26T17:12:14.978132Z","shell.execute_reply.started":"2025-06-26T17:12:12.971411Z","shell.execute_reply":"2025-06-26T17:12:14.976889Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# test_df = pd.read_csv(DATA_DIR + \"test.csv\")\n# test_demo_df = pd.read_csv(DATA_DIR + \"test_demographics.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:14.979530Z","iopub.execute_input":"2025-06-26T17:12:14.980496Z","iopub.status.idle":"2025-06-26T17:12:14.985032Z","shell.execute_reply.started":"2025-06-26T17:12:14.980453Z","shell.execute_reply":"2025-06-26T17:12:14.983801Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# print(test_df.shape)\n# display(test_df.head(10))\n# display(test_demo_df.head(5))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:14.989605Z","iopub.execute_input":"2025-06-26T17:12:14.989942Z","iopub.status.idle":"2025-06-26T17:12:15.001001Z","shell.execute_reply.started":"2025-06-26T17:12:14.989919Z","shell.execute_reply":"2025-06-26T17:12:14.999960Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import polars as pl\n# from tqdm import tqdm\n\n# # 推論対象のユニークな sequence_id を取得\n# sequence_ids = test_df['sequence_id'].unique()\n\n# # 結果保存用\n# results = []\n\n# demo_pl = pl.from_pandas(test_demo_df)\n\n# # 1シーケンスずつ推論\n# for seq_id in tqdm(sequence_ids):\n#     # 対応するシーケンスのデータを Polars に変換\n#     seq_df = test_df[test_df[\"sequence_id\"] == seq_id]\n#     seq_pl = pl.from_pandas(seq_df)\n#\n#     try:\n#         gesture = predict(seq_pl, demo_pl)\n#         print(gesture)\n#         results.append((seq_id, gesture))\n#     except Exception as e:\n#         print(f\"Error for {seq_id}: {e}\")\n#         results.append((seq_id, \"ERROR\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:15.002308Z","iopub.execute_input":"2025-06-26T17:12:15.002925Z","iopub.status.idle":"2025-06-26T17:12:15.019776Z","shell.execute_reply.started":"2025-06-26T17:12:15.002885Z","shell.execute_reply":"2025-06-26T17:12:15.018723Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# pred_df = pd.DataFrame(results, columns=[\"sequence_id\", \"gesture\"])\n# pred_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:15.021069Z","iopub.execute_input":"2025-06-26T17:12:15.021358Z","iopub.status.idle":"2025-06-26T17:12:15.036009Z","shell.execute_reply.started":"2025-06-26T17:12:15.021335Z","shell.execute_reply":"2025-06-26T17:12:15.034842Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# res = pd.read_parquet(\"/kaggle/working/submission.parquet\")\n# res.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:15.037210Z","iopub.execute_input":"2025-06-26T17:12:15.037528Z","iopub.status.idle":"2025-06-26T17:12:15.053212Z","shell.execute_reply.started":"2025-06-26T17:12:15.037501Z","shell.execute_reply":"2025-06-26T17:12:15.052135Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# test_seq_id = test_df[\"sequence_id\"].iloc[0]\n\n# # 2. そのシーケンスを抽出してコピー\n# seq_masked = test_df[test_df[\"sequence_id\"] == test_seq_id].copy()\n\n# # 3. Thermopile / ToF 列を NaN マスク\n# non_imu_cols = [c for c in seq_masked.columns\n#                 if c.startswith(\"thm_\") or c.startswith(\"tof_\")]\n# seq_masked[non_imu_cols] = np.nan\n\n# # 4. Polars へ変換（predict は pl.DataFrame 受け取り）\n# seq_pl  = pl.from_pandas(seq_masked)\n# demo_pl = pl.from_pandas(test_demo_df)\n\n# display(seq_pl.head(3))\n# display(demo_pl.head(3))\n\n# # 5. 推論呼び出し\n# pred_gesture = predict(seq_pl, demo_pl)\n# print(f\"sequence_id: {test_seq_id}  →  predicted gesture = {pred_gesture}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-26T17:12:15.054405Z","iopub.execute_input":"2025-06-26T17:12:15.054728Z","iopub.status.idle":"2025-06-26T17:12:15.071165Z","shell.execute_reply.started":"2025-06-26T17:12:15.054702Z","shell.execute_reply":"2025-06-26T17:12:15.070095Z"}},"outputs":[],"execution_count":null}]}