{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":525454,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":412203,"modelId":429989},{"sourceId":537890,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":417288,"modelId":434978},{"sourceId":537900,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":417294,"modelId":434985},{"sourceId":547834,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":418833,"modelId":436489},{"sourceId":555337,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":422725,"modelId":440285}],"dockerImageVersionId":31089,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import kaggle_evaluation.cmi_inference_server\nimport polars as pl\nimport pandas as pd\nimport re\nimport pickle\nimport os\nimport numpy as np\n\ndef remove_thm_outliers(df, threshold=20):\n\n    # Thermophile outlier fix\n    for column in df.columns:\n        if \"thm\" in column:\n            mask = df[column] < threshold\n            if mask.any():\n                mean_val = df.loc[~mask, column].mean()\n                df.loc[mask, column] = mean_val\n    return df\n\ndef impute_tof_values(df):\n    for column in df.columns:\n        if \"tof\" in column:\n            # Exclude -1 values when calculating the mean\n            valid_values = df[column][df[column] != -1]\n            mean_val = valid_values.mean(skipna=True)\n            max_val = df[column].max(skipna=True)\n\n            # Replace NaN with the mean\n            df[column] = df[column].fillna(mean_val)\n            # Replace -1 with the maximum value\n            df[column] = df[column].replace(-1, max_val)\n    return df\n\ndef drop_columns(columns, df):\n\n    df = df.drop(columns=columns)\n    return df\n\ndef average_tof_values(df):\n\n    tof_pattern = re.compile(r\"^(tof_\\w+)_v(\\d+)$\")\n    tof_groups = {}\n    for col in df.columns:\n        m = tof_pattern.match(col)\n        if m:\n            base = m.group(1)\n            tof_groups.setdefault(base, []).append(col)\n\n    df_tof_avg = []\n    df_tof_avg_name = []\n    for base, cols in tof_groups.items():\n        \n        tof_sensor_average = df[cols].mean(axis=1)\n        df_tof_avg.append(tof_sensor_average)\n        df_tof_avg_name.append(f\"{base}_avg\")\n\n    # Drop all columns containing 'tof'\n    tof_cols = [col for col in df.columns if \"tof\" in col]\n    df = df.drop(columns=tof_cols)\n\n    for name, avg in zip(df_tof_avg_name, df_tof_avg):\n        df[name] = avg\n    return df\n\n### Hard-coded invertet gesture\ninv_gesture_numerical_dict = {1: 'Cheek - pinch skin',\n 2: 'Forehead - pull hairline',\n 0: 'Glasses on/off',\n 3: 'Neck - scratch',\n 4: 'Neck - pinch skin',\n 5: 'Eyelash - pull hair',\n 6: 'Eyebrow - pull hair',\n 7: 'Forehead - scratch',\n 8: 'Above ear - pull hair'}\n\n\ndef add_mean_and_std_tof_thm(df):\n\n\n    df[\"flight_mean\"] = df.filter(like=\"tof_\").mean(axis=1)\n    df[\"flight_std\"] = df.filter(like=\"tof_\").std(axis=1)\n\n    df[\"heat_mean\"] = df.filter(like=\"thm_\").mean(axis=1)\n    df[\"heat_std\"] = df.filter(like=\"thm_\").std(axis=1)\n\n    return df\n\n\ndef average_intervals(df):\n\n    n = len(df)\n    interval_size = n // 4\n    intervals = [df.iloc[i*interval_size:(i+1)*interval_size] for i in range(4)]\n    \n    # Handle remainder by adding leftover rows to the last interval\n    if n % 4 != 0:\n        intervals[-1] = pd.concat([intervals[-1], df.iloc[4*interval_size:]])\n    \n    summary_rows = []\n    for idx, interval in enumerate(intervals):\n        avg_row = interval.mean(numeric_only=True)\n        avg_row[\"interval\"] = idx + 1\n    \n        # For categorical columns, take the first value in the interval\n        categorical_cols = df.select_dtypes(include=[\"object\", \"category\"]).columns\n        for cat_col in categorical_cols:\n            avg_row[cat_col] = interval.iloc[0][cat_col]\n    \n        summary_rows.append(avg_row)\n    \n    # Convert list of Series into one DataFrame, stacked vertically\n    summary_df = pd.DataFrame(summary_rows).reset_index(drop=True)\n    print(summary_df.shape)\n    print(summary_df.columns)\n    return summary_df\n\ndef flatten(df):\n    # Identify categorical columns\n    categorical_cols = df.select_dtypes(include=[\"object\", \"category\"]).columns.tolist()\n    # Identify numerical columns\n    numerical_cols = df.select_dtypes(include=[np.number]).columns.tolist()\n    # Remove 'sequence_id' and 'interval' from numerical columns if present\n    numerical_cols = [col for col in numerical_cols if col not in [\"sequence_id\", \"interval\"]]\n\n    num_values = df[numerical_cols].values.flatten()\n    # Take the first value for each categorical column\n    cat_values = df[categorical_cols].iloc[0].values\n    # Concatenate numerical and categorical values\n    concat = np.concatenate([num_values, cat_values])\n\n    # Build column names: numerical columns for each interval, then categorical columns\n    num_col_names = []\n    for i in range(len(df)):\n        num_col_names.extend([f\"{col}_interval{i+1}\" for col in numerical_cols])\n    col_names = num_col_names + categorical_cols\n\n    return pd.DataFrame([concat], columns=col_names)\n\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n\n    with open(\"/kaggle/input/tof_and_thm_removed/other/default/1/xgb_model.pkl\", \"rb\") as f:\n        model = pickle.load(f)\n\n    # Convert Polars -> Pandas\n    sequence_pd = sequence.to_pandas()\n\n    sequence_pd = remove_thm_outliers(sequence_pd)\n    sequence_pd = impute_tof_values(sequence_pd)\n    sequenc_pd = add_mean_and_std_tof_thm(sequence_pd)\n    #sequence_pd = average_tof_values(sequence_pd)\n    demographics_pd = demographics.to_pandas()\n    \n    columns_to_drop = [\"row_id\", \"sequence_counter\"]\n    sequence_pd = drop_columns(columns_to_drop, sequence_pd)\n\n    # Merge demographics\n    sequence_pd = average_intervals(sequence_pd)\n    sequence_pd = flatten(sequence_pd)\n    \n    #sequence_pd = sequence_pd.merge(demographics_pd, on=\"subject\", how=\"left\")\n    sequence_pd = sequence_pd.drop([\"subject\", \"sequence_id\"], axis=1)\n\n    sequence_pd = sequence_pd.drop(\n    columns=[\n        col for col in sequence_pd.columns \n        if col == \"gesture\" or \"tof\" in col.lower() or \"thm\" in col.lower() or \"heat\" in col.lower() or \"flight\" in col.lower()\n    ])\n    \n    # Predict gesture\n    y_pred = model.predict(sequence_pd)\n    \n    gesture = inv_gesture_numerical_dict[int(y_pred[0])]\n    print(y_pred)\n    \n    return gesture\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-01T19:52:24.883718Z","iopub.execute_input":"2025-09-01T19:52:24.884003Z","iopub.status.idle":"2025-09-01T19:52:24.903854Z","shell.execute_reply.started":"2025-09-01T19:52:24.883981Z","shell.execute_reply":"2025-09-01T19:52:24.903159Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Serve the inference function\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-09-01T19:52:31.906153Z","iopub.execute_input":"2025-09-01T19:52:31.906978Z","iopub.status.idle":"2025-09-01T19:52:33.962991Z","shell.execute_reply.started":"2025-09-01T19:52:31.906950Z","shell.execute_reply":"2025-09-01T19:52:33.962227Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}