{"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":"gpu","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"},{"sourceId":245501000,"sourceType":"kernelVersion"}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport polars as pl\nimport joblib\n\nmodels=[]\nfor fold in range(5):\n    model = joblib.load(f'/kaggle/input/exp01-training/model_lgb{fold}.joblib')\n    models.append(model)\nle = joblib.load('/kaggle/input/exp01-training/le.joblib')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-15T01:28:23.213804Z","iopub.execute_input":"2025-06-15T01:28:23.214678Z","iopub.status.idle":"2025-06-15T01:28:23.510834Z","shell.execute_reply.started":"2025-06-15T01:28:23.214649Z","shell.execute_reply":"2025-06-15T01:28:23.510237Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def extract_acc_features(data: pl.DataFrame) -> pl.DataFrame:\n    \"\"\"\n    閾値ベースの加速度特徴量を抽出（Polars使用、agg_exprsスタイル）。\n    \n    Parameters\n    ----------\n    data : pl.DataFrame\n        入力データ。以下のカラムが必要：\n        - sequence_id\n        - acc_x, acc_y, acc_z\n\n    Returns\n    -------\n    pl.DataFrame\n        sequence_idごとの特徴量を格納したDataFrame\n    \"\"\"\n    # ---- 前処理（特徴量計算） ----\n    data = data.with_columns([\n        # delta_acc = √(Δx² + Δy² + Δz²)\n        ((pl.col(\"acc_x\").diff().pow(2) +\n          pl.col(\"acc_y\").diff().pow(2) +\n          pl.col(\"acc_z\").diff().pow(2)).sqrt()).alias(\"delta_acc\"),\n    ])\n    data = data.with_columns([\n        # AVM = √(x² + y² + z²)\n        ((pl.col(\"acc_x\").pow(2) +\n          pl.col(\"acc_y\").pow(2) +\n          pl.col(\"acc_z\").pow(2)).sqrt()).alias(\"avm\")\n    ])\n    data = data.with_columns([\n        # 閾値フラグ\n        (pl.col(\"delta_acc\") > 0.2).cast(pl.Int8).alias(\"motion_gt_0.2g\"),\n        (pl.col(\"avm\") > 1.5).cast(pl.Int8).alias(\"high_intensity_flag\")\n    ])\n\n    # ---- 姿勢変化 GM ----\n    gm_df = (\n        data.group_by(\"sequence_id\")\n        .agg([\n            pl.mean(\"acc_x\").alias(\"mean_x\"),\n            pl.mean(\"acc_y\").alias(\"mean_y\"),\n            pl.mean(\"acc_z\").alias(\"mean_z\"),\n        ])\n        .with_columns([\n            ((pl.col(\"mean_x\").pow(2) + pl.col(\"mean_y\").pow(2) + pl.col(\"mean_z\").pow(2)).sqrt()).alias(\"gm\")\n        ])\n        .select([\"sequence_id\", \"gm\"])\n    )\n\n    # ---- 閾値ベース特徴量集約 ----\n    agg_exprs = [\n        pl.sum(\"motion_gt_0.2g\").alias(\"motion_count_gt_0.2g\"),\n        pl.sum(\"high_intensity_flag\").alias(\"high_intensity_count\"),\n        pl.mean(\"high_intensity_flag\").alias(\"high_intensity_ratio\"),\n    ]\n\n    thresh_features = data.group_by(\"sequence_id\").agg(agg_exprs)\n\n    # ---- 結合 ----\n    result = thresh_features.join(gm_df, on=\"sequence_id\", how=\"left\")\n    result = data.join(result, on= \"sequence_id\", how=\"left\")\n\n    return result","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-15T01:28:23.605018Z","iopub.execute_input":"2025-06-15T01:28:23.605576Z","iopub.status.idle":"2025-06-15T01:28:23.613935Z","shell.execute_reply.started":"2025-06-15T01:28:23.605553Z","shell.execute_reply":"2025-06-15T01:28:23.613122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def feature_engineering_inference(data: pl.DataFrame) -> pl.DataFrame:\n    demographic_cols = [\n        \"adult_child\", \"age\", \"sex\", \"handedness\",\n        \"height_cm\", \"shoulder_to_wrist_cm\", \"elbow_to_wrist_cm\"\n    ]\n\n    # 特徴量の増加\n    data = extract_acc_features(data=data)\n\n    # 数値センサーカラムの抽出\n    stat_cols = [\n        c for c in data.columns\n        if c not in demographic_cols + [\"sequence_id\", \"row_id\", \"sequence_counter\", \"subject\"]\n    ]\n\n    # ---- 通常の sequence_id 単位の集約 ----\n    agg_exprs = []\n    for c in stat_cols:\n        agg_exprs.extend([\n            pl.col(c).mean().alias(f\"{c}_mean\"),\n            pl.col(c).std().alias(f\"{c}_std\"),\n            pl.col(c).var().alias(f\"{c}_var\"),\n            pl.col(c).quantile(0.25).alias(f\"{c}_q25\"),\n            pl.col(c).median().alias(f\"{c}_q50\"),\n            pl.col(c).quantile(0.75).alias(f\"{c}_q75\"),\n            pl.col(c).max().alias(f\"{c}_max\"),\n            pl.col(c).min().alias(f\"{c}_min\"),\n        ])\n\n    base_features = data.group_by(\"sequence_id\").agg(agg_exprs)\n\n    # ---- カテゴリカル変数 × 数値カラムのクロス統計量 ----\n    cat_cols = [\"sex\", \"handedness\", \"adult_child\"]\n    cat_stats = []\n\n    for cat_col in cat_cols:\n        if cat_col in data.columns:\n            for c in stat_cols:\n                group = (\n                    data.group_by([\"sequence_id\", cat_col])\n                    .agg([\n                        pl.col(c).mean().alias(f\"{c}_mean\"),\n                        pl.col(c).std().alias(f\"{c}_std\"),\n                    ])\n                )\n\n                # 各カテゴリごとにフィルタしてリネームしてマージ\n                for category in group.select(cat_col).unique().to_series(0):\n                    filtered = group.filter(pl.col(cat_col) == category).select([\n                        pl.col(\"sequence_id\"),\n                        pl.col(f\"{c}_mean\").alias(f\"{c}_{cat_col}_{category}_mean\"),\n                        pl.col(f\"{c}_std\").alias(f\"{c}_{cat_col}_{category}_std\"),\n                    ])\n                    cat_stats.append(filtered)\n    \n    # ✅ sequence_id を残して安全に順次 join\n    cat_features = cat_stats[0]\n    for df in cat_stats[1:]:\n        cat_features = cat_features.join(df, on=\"sequence_id\", how=\"left\")\n    cat_features\n\n    # ---- 最終マージ ----\n    if cat_features is not None:\n        final = base_features.join(cat_features, on=\"sequence_id\", how=\"left\")\n        # final = base_features.join(data[[\"sequence_id\", target_col]].unique(), on=\"sequence_id\", how=\"left\")\n    else:\n        final = base_features\n\n    return final","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-15T01:28:24.258483Z","iopub.execute_input":"2025-06-15T01:28:24.258766Z","iopub.status.idle":"2025-06-15T01:28:24.267992Z","shell.execute_reply.started":"2025-06-15T01:28:24.258745Z","shell.execute_reply":"2025-06-15T01:28:24.267287Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> list[str]:\n    predictions = []\n\n    for seq_id in sequence[\"sequence_id\"].unique():\n        # 個別にシーケンスを抽出\n        seq_df = sequence.filter(pl.col(\"sequence_id\") == seq_id)\n        demo_df = demographics.filter(pl.col(\"sequence_id\") == seq_id)\n\n        # 特徴量生成\n        cleaned_data = feature_engineering_inference(seq_df)\n        pdf = cleaned_data.to_pandas().drop(columns=[\"sequence_id\"])\n\n        # アンサンブル推論\n        pred_list = []\n        for model in models:\n            pred = model.predict(pdf)\n            if isinstance(pred, np.ndarray):\n                pred = pred[0]\n            pred_list.append(int(pred))\n\n        # 投票で最終予測\n        predicted_label_id = max(set(pred_list), key=pred_list.count)\n        predicted_gesture_str = le.inverse_transform([predicted_label_id])[0]\n\n        predictions.append(predicted_gesture_str)\n\n    return predictions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-15T01:30:25.562102Z","iopub.execute_input":"2025-06-15T01:30:25.562648Z","iopub.status.idle":"2025-06-15T01:30:25.567998Z","shell.execute_reply.started":"2025-06-15T01:30:25.562616Z","shell.execute_reply":"2025-06-15T01:30:25.567236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n#     cleaned_data = feature_engineering_inference(sequence)\n#     pdf = cleaned_data.to_pandas().drop(columns=[\"sequence_id\"])\n#     # Predict using ensemble of CV models\n#     predictions = []\n#     for model in models:\n#         pred = model.predict(pdf)\n#         # Ensure we get a scalar value\n#         if isinstance(pred, np.ndarray):\n#             pred = pred[0]\n#         predictions.append(int(pred))\n    \n#     # Use majority vote or most confident prediction\n#     predicted_label_id = max(set(predictions), key=predictions.count)\n    \n#     # Convert back to gesture string\n#     predicted_gesture_str = le.inverse_transform([predicted_label_id])[0]\n    \n#     return predicted_gesture_str","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-15T01:28:27.845264Z","iopub.execute_input":"2025-06-15T01:28:27.845849Z","iopub.status.idle":"2025-06-15T01:28:27.850398Z","shell.execute_reply.started":"2025-06-15T01:28:27.845825Z","shell.execute_reply":"2025-06-15T01:28:27.849680Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import kaggle_evaluation.cmi_inference_server\nimport os\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-15T01:30:28.816957Z","iopub.execute_input":"2025-06-15T01:30:28.817533Z","iopub.status.idle":"2025-06-15T01:30:29.292171Z","shell.execute_reply.started":"2025-06-15T01:30:28.817510Z","shell.execute_reply":"2025-06-15T01:30:29.291214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}