{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.13"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":12182570,"sourceType":"datasetVersion","datasetId":7673047},{"sourceId":251006994,"sourceType":"kernelVersion"},{"sourceId":251030378,"sourceType":"kernelVersion"},{"sourceId":251777588,"sourceType":"kernelVersion"},{"sourceId":252033650,"sourceType":"kernelVersion"},{"sourceId":252191622,"sourceType":"kernelVersion"},{"sourceId":252729913,"sourceType":"kernelVersion"},{"sourceId":253640529,"sourceType":"kernelVersion"},{"sourceId":255370501,"sourceType":"kernelVersion"},{"sourceId":256141783,"sourceType":"kernelVersion"},{"sourceId":256775634,"sourceType":"kernelVersion"},{"sourceId":258208617,"sourceType":"kernelVersion"},{"sourceId":258268609,"sourceType":"kernelVersion"},{"sourceId":258384440,"sourceType":"kernelVersion"}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":131.374004,"end_time":"2025-08-19T10:09:17.783991","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-08-19T10:07:06.409987","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# the referenced notebook¶\n- https://www.kaggle.com/code/salmanahmedtamu/pytorch-training-infer-pipeline-0-72 @salmanahmedtamu\n- https://www.kaggle.com/code/rktqwe/lb-0-77-linear-accel-tf-bilstm-gru-attention @rktqwe\n- https://www.kaggle.com/code/nksusth/lb-0-78-quaternions-tf-bilstm-gru-attention @nksusth","metadata":{"papermill":{"duration":0.004296,"end_time":"2025-08-19T10:07:10.717293","exception":false,"start_time":"2025-08-19T10:07:10.712997","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import os\nimport random\nimport pickle\nimport polars as pl\nimport pandas as pd\nimport numpy as np\n\nfrom scipy.spatial.transform import Rotation as R\nfrom scipy.stats import skew, kurtosis\n\n\nfrom sklearn.metrics import f1_score\nfrom sklearn.preprocessing import LabelEncoder, StandardScaler\nfrom sklearn.model_selection import StratifiedGroupKFold\nfrom sklearn.utils.class_weight import compute_class_weight\n\nimport lightgbm as lgb\nfrom catboost import CatBoostClassifier\n\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.amp import autocast, GradScaler\nfrom torch.optim import lr_scheduler, Adam, AdamW\nfrom torch.optim.lr_scheduler import CosineAnnealingWarmRestarts\n\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# save\ndef save(model=None, model_name=None):\n    with open(model_name, 'wb') as f:\n        pickle.dump(model, f)\n        \n# load\ndef load(model_path=None):\n    with open(model_path, mode='rb') as f:\n            model = pickle.load(f)\n    return model\n\ndef seed_everything(seed: int):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n\nexp_paths = {\n    # imu\n    'exp068': '/kaggle/input/cmi-detect-behavior-exp068-train/',\n    'exp070': '/kaggle/input/cmi-detect-behavior-exp070-train/',\n    'exp092': '/kaggle/input/cmi-detect-behavior-exp092-train/',\n    'exp093': '/kaggle/input/cmi-detect-behavior-exp093-train/',\n    'exp104': '/kaggle/input/cmi-detect-behavior-exp104-train/',\n    'exp117': '/kaggle/input/cmi-detect-behavior-exp117-train/',\n    'exp326': '/kaggle/input/cmi-detect-behavior-exp326-train/',\n    'exp329': '/kaggle/input/cmi-detect-behavior-exp329-train/',\n\n    # imu,tof,thm\n    'exp086': '/kaggle/input/cmi-detect-behavior-exp086-train/',\n    'exp137': '/kaggle/input/cmi-detect-behavior-exp137-train/',\n    'exp179': '/kaggle/input/cmi-detect-behavior-exp179-train/',\n    'exp226': '/kaggle/input/cmi-detect-behavior-exp226-train/'\n}\n\nconfigs = {name: load(f'{path}config.pkl') for name, path in exp_paths.items()}\n\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\ndevice","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:10.725692Z","iopub.status.busy":"2025-08-19T10:07:10.725140Z","iopub.status.idle":"2025-08-19T10:07:26.112680Z","shell.execute_reply":"2025-08-19T10:07:26.111926Z"},"papermill":{"duration":15.392941,"end_time":"2025-08-19T10:07:26.114048","exception":false,"start_time":"2025-08-19T10:07:10.721107","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_angular_speed(angular_velocity):\n    return np.linalg.norm(angular_velocity, axis=1)\n\ndef calculate_angular_acceleration(angular_velocity):\n    angular_acc = np.diff(angular_velocity, axis=0)\n    return angular_acc\n\ndef calculate_euler_angle_diff(quat_data):\n    valid_mask = ~np.all(np.isclose(quat_data, 0), axis=1)\n\n    cleaned_quat = quat_data.copy()\n    for i in range(len(cleaned_quat)):\n        if not valid_mask[i]:\n            cleaned_quat[i] = cleaned_quat[i-1] if i > 0 else [0, 0, 0, 1]\n\n    euler_angles = R.from_quat(cleaned_quat).as_euler('xyz', degrees=False)\n\n    euler_diff = np.diff(euler_angles, axis=0)\n\n    return euler_diff\n    \ndef remove_gravity_from_acc(acc_data, rot_data):\n    \"\"\"\n    Удаляет компоненту гравитации из данных акселерометра, используя данные кватерниона.\n    acc_data: pd.DataFrame или np.array с колонками/столбцами ['acc_x', 'acc_y', 'acc_z']\n    rot_data: pd.DataFrame или np.array с колонками/столбцами ['rot_x', 'rot_y', 'rot_z', 'rot_w'] (порядок важен для Scipy: x, y, z, w)\n    Возвращает: np.array с линейным ускорением [linear_acc_x, linear_acc_y, linear_acc_z]\n    \"\"\"\n    if isinstance(acc_data, pd.DataFrame):\n        acc_values = acc_data[['acc_x', 'acc_y', 'acc_z']].values\n    else:\n        acc_values = acc_data # предполагаем, что это уже numpy array [N, 3]\n\n    if isinstance(rot_data, pd.DataFrame):\n        # Scipy ожидает кватернионы в порядке [x, y, z, w]\n        quat_values = rot_data[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    else:\n        quat_values = rot_data # предполагаем, что это уже numpy array [N, 4] в нужном порядке\n\n    num_samples = acc_values.shape[0]\n    linear_accel = np.zeros_like(acc_values)\n    \n    # Вектор гравитации в глобальной системе координат (приблизительно)\n    # Предполагаем, что Z - это \"вверх\", поэтому гравитация действует вдоль -Z\n    # или +Z в зависимости от системы координат IMU.\n    # Если IMU дает ускорение свободного падения ~9.81 по Z в состоянии покоя, \n    # то гравитация в мировой системе [0,0,-9.81] будет преобразована в [0,0,9.81] в системе датчика.\n    # Будем считать, что гравитация в системе датчика, когда он лежит плашмя, это [0,0,g]\n    # и мы хотим ее вычесть.\n    gravity_world = np.array([0, 0, 9.81]) # Стандартное значение g\n\n    for i in range(num_samples):\n        if np.all(np.isnan(quat_values[i])) or np.all(np.isclose(quat_values[i], 0)): # Проверка на NaN или нулевой кватернион\n            # Если кватернион невалидный, просто используем сырое ускорение\n            # или можно присвоить NaN/0, но это может потребовать доп. обработки\n            linear_accel[i, :] = acc_values[i, :] \n            continue\n\n        try:\n            # Создаем объект Rotation из кватерниона\n            rotation = R.from_quat(quat_values[i])\n            # Вращаем вектор гравитации из мировой системы в систему координат сенсора\n            gravity_sensor_frame = rotation.apply(gravity_world, inverse=True) # или inverse=False, зависит от конвенции кватерниона\n            # Вычитаем компоненту гравитации\n            linear_accel[i, :] = acc_values[i, :] - gravity_sensor_frame\n        except ValueError: # Например, если кватернион не нормирован\n             linear_accel[i, :] = acc_values[i, :] # Фоллбэк\n             \n    return linear_accel\n\ndef calculate_angular_velocity_from_quat(rot_data, time_delta=1/200): # Assuming 200Hz sampling rate\n    if isinstance(rot_data, pd.DataFrame):\n        quat_values = rot_data[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    else:\n        quat_values = rot_data\n\n    num_samples = quat_values.shape[0]\n    angular_vel = np.zeros((num_samples, 3))\n\n    for i in range(num_samples - 1):\n        q_t = quat_values[i]\n        q_t_plus_dt = quat_values[i+1]\n\n        if np.all(np.isnan(q_t)) or np.all(np.isclose(q_t, 0)) or \\\n           np.all(np.isnan(q_t_plus_dt)) or np.all(np.isclose(q_t_plus_dt, 0)):\n            continue\n\n        try:\n            rot_t = R.from_quat(q_t)\n            rot_t_plus_dt = R.from_quat(q_t_plus_dt)\n\n            # Calculate the relative rotation\n            delta_rot = rot_t.inv() * rot_t_plus_dt\n            \n            # Convert delta rotation to angular velocity vector\n            # The rotation vector (Euler axis * angle) scaled by 1/dt\n            # is a good approximation for small delta_rot\n            angular_vel[i, :] = delta_rot.as_rotvec() / time_delta\n        except ValueError:\n            # If quaternion is invalid, angular velocity remains zero\n            pass\n            \n    return angular_vel\n\ndef calculate_angular_distance(rot_data):\n    if isinstance(rot_data, pd.DataFrame):\n        quat_values = rot_data[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    else:\n        quat_values = rot_data\n\n    num_samples = quat_values.shape[0]\n    angular_dist = np.zeros(num_samples)\n\n    for i in range(num_samples - 1):\n        q1 = quat_values[i]\n        q2 = quat_values[i+1]\n\n        if np.all(np.isnan(q1)) or np.all(np.isclose(q1, 0)) or \\\n           np.all(np.isnan(q2)) or np.all(np.isclose(q2, 0)):\n            angular_dist[i] = 0 # Или np.nan, в зависимости от желаемого поведения\n            continue\n        try:\n            # Преобразование кватернионов в объекты Rotation\n            r1 = R.from_quat(q1)\n            r2 = R.from_quat(q2)\n\n            # Вычисление углового расстояния: 2 * arccos(|real(p * q*)|)\n            # где p* - сопряженный кватернион q\n            # В scipy.spatial.transform.Rotation, r1.inv() * r2 дает относительное вращение.\n            # Угол этого относительного вращения - это и есть угловое расстояние.\n            relative_rotation = r1.inv() * r2\n            \n            # Угол rotation vector соответствует угловому расстоянию\n            # Норма rotation vector - это угол в радианах\n            angle = np.linalg.norm(relative_rotation.as_rotvec())\n            angular_dist[i] = angle\n        except ValueError:\n            angular_dist[i] = 0 # В случае недействительных кватернионов\n            pass\n            \n    return angular_dist\n    \ndef pad_sequences_torch(sequences, maxlen, padding='post', truncating='post', value=0.0):\n    \"\"\"PyTorch equivalent of Keras pad_sequences\"\"\"\n    result = []\n    for seq in sequences:\n        seq = np.array(seq)\n        T, D = seq.shape\n        if T == maxlen:\n            result.append(seq)\n            \n        elif T < maxlen:\n            pad_len = maxlen - T\n            pad = np.full((pad_len, D), value)\n            seq_padded = np.concatenate([seq, pad], axis=0)\n            result.append(seq_padded)\n        else:  # T > maxlen → downsample by average\n            indices = np.linspace(0, T, num=maxlen + 1, dtype=int)\n            compressed = []\n            for i in range(maxlen):\n                segment = seq[indices[i]:indices[i+1]]\n                compressed.append(np.mean(segment, axis=0))\n            result.append(np.stack(compressed))\n            \n    return np.array(result, dtype=np.float32)\n\ndef preprocess_sequence(df_seq: pd.DataFrame, feature_cols: list, scaler: StandardScaler):\n    \"\"\"Normalizes and cleans the time series sequence\"\"\"\n    mat = df_seq[feature_cols].ffill().bfill().fillna(0).values\n    return scaler.transform(mat).astype('float32')\n\nclass MixupDataset(Dataset):\n    \"\"\"Dataset with MixUp augmentation\"\"\"\n    def __init__(self, X, y, alpha=0.2):\n        self.X = torch.FloatTensor(X)\n        self.y = torch.FloatTensor(y)\n        self.alpha = alpha\n        \n    def __len__(self):\n        return len(self.X)\n    \n    def __getitem__(self, idx):\n        x, y = self.X[idx], self.y[idx]\n        \n        if self.alpha > 0:\n            lam = np.random.beta(self.alpha, self.alpha)\n            rand_idx = np.random.randint(0, len(self.X))\n            x_rand, y_rand = self.X[rand_idx], self.y[rand_idx]\n            \n            x = lam * x + (1 - lam) * x_rand\n            y = lam * y + (1 - lam) * y_rand\n            \n        return x, y\n","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:26.122650Z","iopub.status.busy":"2025-08-19T10:07:26.122176Z","iopub.status.idle":"2025-08-19T10:07:26.141251Z","shell.execute_reply":"2025-08-19T10:07:26.140725Z"},"papermill":{"duration":0.024715,"end_time":"2025-08-19T10:07:26.142316","exception":false,"start_time":"2025-08-19T10:07:26.117601","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def Imu_FE(df):\n    linear_accel_list = []\n    for _, group in df.groupby('sequence_id'):\n        acc_data_group = group[['acc_x', 'acc_y', 'acc_z']]\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']] # Порядок x,y,z,w для Scipy\n        linear_accel_group = remove_gravity_from_acc(acc_data_group, rot_data_group)\n        linear_accel_list.append(pd.DataFrame(linear_accel_group, columns=['linear_acc_x', 'linear_acc_y', 'linear_acc_z'], index=group.index))\n    \n    df_linear_accel = pd.concat(linear_accel_list)\n    df = pd.concat([df, df_linear_accel], axis=1)\n\n    df['acc_mag'] = np.sqrt(df['acc_x']**2 + df['acc_y']**2 + df['acc_z']**2)\n    df['rot_angle'] = 2 * np.arccos(df['rot_w'].clip(-1, 1))\n\n    df['acc_mag_jerk'] = df.groupby('sequence_id')['acc_mag'].diff().fillna(0)\n    df['rot_angle_vel'] = df.groupby('sequence_id')['rot_angle'].diff().fillna(0)  \n\n    angular_vel_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']]\n        angular_vel_group = calculate_angular_velocity_from_quat(rot_data_group)\n        angular_vel_list.append(pd.DataFrame(angular_vel_group, columns=['angular_vel_x', 'angular_vel_y', 'angular_vel_z'], index=group.index))\n\n\n    df_angular_vel = pd.concat(angular_vel_list)\n    df = pd.concat([df, df_angular_vel], axis=1)\n\n\n    angular_distance_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']]\n        angular_dist_group = calculate_angular_distance(rot_data_group)\n        angular_distance_list.append(pd.DataFrame(angular_dist_group, columns=['angular_distance'], index=group.index))\n    \n    df_angular_distance = pd.concat(angular_distance_list)\n    df = pd.concat([df, df_angular_distance], axis=1)\n\n    angular_speed_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['angular_vel_x', 'angular_vel_y', 'angular_vel_z']]\n        angular_dist_group = calculate_angular_speed(rot_data_group)\n        angular_speed_list.append(pd.DataFrame(angular_dist_group, columns=['angular_speed'], index=group.index))\n    \n    df_angular_speed = pd.concat(angular_speed_list)\n    df = pd.concat([df, df_angular_speed], axis=1)\n\n    angular_acc_list = []\n\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['angular_vel_x', 'angular_vel_y', 'angular_vel_z']]\n        angular_dist_group = calculate_angular_acceleration(rot_data_group)\n\n        new_index = group.index[1:]\n\n        acc_df = pd.DataFrame(\n            angular_dist_group,\n            columns=['ang_acc_x', 'ang_acc_y', 'ang_acc_z'],\n            index=new_index\n        )\n\n        angular_acc_list.append(acc_df)\n\n    df_angular_acc = pd.concat(angular_acc_list)\n    df_angular_acc = df_angular_acc.reindex(df.index)\n    df = pd.concat([df, df_angular_acc], axis=1)\n\n    df['linear_acc_mag'] = np.sqrt(df['linear_acc_x']**2 + df['linear_acc_y']**2 + df['linear_acc_z']**2)\n    df['linear_acc_mag_jerk'] = df.groupby('sequence_id')['linear_acc_mag'].diff().fillna(0)\n\n    df['linear_accx_jerk'] = df.groupby('sequence_id')['linear_acc_x'].diff().fillna(0)\n    df['linear_accy_jerk'] = df.groupby('sequence_id')['linear_acc_y'].diff().fillna(0)\n    df['linear_accz_jerk'] = df.groupby('sequence_id')['linear_acc_z'].diff().fillna(0)\n\n    df['linear_accx_rolling_mean5'] = df.groupby('sequence_id')['linear_acc_x'].rolling(5).mean().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_mean5'] = df.groupby('sequence_id')['linear_acc_y'].rolling(5).mean().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_mean5'] = df.groupby('sequence_id')['linear_acc_z'].rolling(5).mean().reset_index(level=0, drop=True).fillna(0)\n\n    \n    df['linear_accx_rolling_std3'] = df.groupby('sequence_id')['linear_acc_x'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_std3'] = df.groupby('sequence_id')['linear_acc_y'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_std3'] = df.groupby('sequence_id')['linear_acc_z'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n\n    df['linear_accx_rolling_min3'] = df.groupby('sequence_id')['linear_acc_x'].rolling(3).min().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_min3'] = df.groupby('sequence_id')['linear_acc_y'].rolling(3).min().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_min3'] = df.groupby('sequence_id')['linear_acc_z'].rolling(3).min().reset_index(level=0, drop=True).fillna(0)\n    \n    df['linear_accx_rolling_max3'] = df.groupby('sequence_id')['linear_acc_x'].rolling(3).max().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_max3'] = df.groupby('sequence_id')['linear_acc_y'].rolling(3).max().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_max3'] = df.groupby('sequence_id')['linear_acc_z'].rolling(3).max().reset_index(level=0, drop=True).fillna(0)\n    \n    df['angular_vel_x_rolling_mean3'] = df.groupby('sequence_id')['angular_vel_x'].rolling(3).mean().reset_index(level=0, drop=True).fillna(0)\n    df['angular_vel_y_rolling_mean3'] = df.groupby('sequence_id')['angular_vel_y'].rolling(3).mean().reset_index(level=0, drop=True).fillna(0)\n    df['angular_vel_z_rolling_mean3'] = df.groupby('sequence_id')['angular_vel_z'].rolling(3).mean().reset_index(level=0, drop=True).fillna(0)\n    df['angular_distance_rolling_mean3'] = df.groupby('sequence_id')['angular_distance'].rolling(3).mean().reset_index(level=0, drop=True).fillna(0)\n    \n    df['angular_vel_x_rolling_std3'] = df.groupby('sequence_id')['angular_vel_x'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n    df['angular_vel_y_rolling_std3'] = df.groupby('sequence_id')['angular_vel_y'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n    df['angular_vel_z_rolling_std3'] = df.groupby('sequence_id')['angular_vel_z'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n    df['angular_distance_rolling_std3'] = df.groupby('sequence_id')['angular_distance'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n\n    df['angular_vel_x_dominance'] = df['angular_vel_x'] / (df['angular_vel_x'] + df['angular_vel_y'] + df['angular_vel_z'] + 1e-8)\n    df['angular_vel_y_dominance'] = df['angular_vel_y'] / (df['angular_vel_x'] + df['angular_vel_y'] + df['angular_vel_z'] + 1e-8)\n    df['angular_vel_z_dominance'] = df['angular_vel_z'] / (df['angular_vel_x'] + df['angular_vel_y'] + df['angular_vel_z'] + 1e-8)\n\n    df['angular_vel_x_rolling_min3'] = df.groupby('sequence_id')['angular_vel_x'].rolling(3).min().reset_index(level=0, drop=True)\n    df['angular_vel_y_rolling_min3'] = df.groupby('sequence_id')['angular_vel_y'].rolling(3).min().reset_index(level=0, drop=True)\n    df['angular_vel_z_rolling_min3'] = df.groupby('sequence_id')['angular_vel_z'].rolling(3).min().reset_index(level=0, drop=True)\n    df['angular_distance_rolling_min3'] = df.groupby('sequence_id')['angular_distance'].rolling(3).min().reset_index(level=0, drop=True)\n\n    df['angular_vel_x_rolling_max3'] = df.groupby('sequence_id')['angular_vel_x'].rolling(3).max().reset_index(level=0, drop=True)\n    df['angular_vel_y_rolling_max3'] = df.groupby('sequence_id')['angular_vel_y'].rolling(3).max().reset_index(level=0, drop=True)\n    df['angular_vel_z_rolling_max3'] = df.groupby('sequence_id')['angular_vel_z'].rolling(3).max().reset_index(level=0, drop=True)\n    df['angular_distance_rolling_max3'] = df.groupby('sequence_id')['angular_distance'].rolling(3).max().reset_index(level=0, drop=True)\n\n    df['angular_vel_x_diff'] = df.groupby('sequence_id')['angular_vel_x'].diff().fillna(0)\n    df['angular_vel_y_diff'] = df.groupby('sequence_id')['angular_vel_y'].diff().fillna(0)\n    df['angular_vel_z_diff'] = df.groupby('sequence_id')['angular_vel_z'].diff().fillna(0)\n    \n    df['linear_accxy_mag'] = np.sqrt(df['linear_acc_x']**2+df['linear_acc_y']**2)\n    df['linear_accyz_mag'] = np.sqrt(df['linear_acc_y']**2+df['linear_acc_z']**2)\n    df['linear_acczx_mag'] = np.sqrt(df['linear_acc_x']**2+df['linear_acc_z']**2)\n\n    df['linear_accxy_jerk_mag'] = np.sqrt(df['linear_accx_jerk']**2+df['linear_accy_jerk']**2)\n    df['linear_accyz_jerk_mag'] = np.sqrt(df['linear_accy_jerk']**2+df['linear_accz_jerk']**2)\n    df['linear_acczx_jerk_mag'] = np.sqrt(df['linear_accx_jerk']**2+df['linear_accz_jerk']**2)\n\n    df['angular_vel_xy_mag'] = np.sqrt(df['angular_vel_x']**2+df['angular_vel_y']**2)\n    df['angular_vel_yz_mag'] = np.sqrt(df['angular_vel_y']**2+df['angular_vel_z']**2)\n    df['angular_vel_zx_mag'] = np.sqrt(df['angular_vel_x']**2+df['angular_vel_z']**2)\n\n    df['ang_acc_xy_ang'] = np.sqrt(df['ang_acc_x']**2+df['ang_acc_y']**2)\n    df['ang_acc_yz_ang'] = np.sqrt(df['ang_acc_y']**2+df['ang_acc_z']**2)\n    df['ang_acc_zx_ang'] = np.sqrt(df['ang_acc_x']**2+df['ang_acc_z']**2)\n\n    df['linear_accxy_rolling_mean5_mag'] = np.sqrt(df['linear_accx_rolling_mean5']**2+df['linear_accy_rolling_mean5']**2)\n    df['linear_accyz_rolling_mean5_mag'] = np.sqrt(df['linear_accy_rolling_mean5']**2+df['linear_accz_rolling_mean5']**2)\n    df['linear_acczx_rolling_mean5_mag'] = np.sqrt(df['linear_accx_rolling_mean5']**2+df['linear_accz_rolling_mean5']**2)\n    df['linear_acc_rolling_mean5_mag'] = np.sqrt(df['linear_accx_rolling_mean5']**2+df['linear_accy_rolling_mean5']**2+df['linear_accz_rolling_mean5']**2)\n\n    df['linear_accxy_rolling_std3_mag'] = np.sqrt(df['linear_accx_rolling_std3']**2+df['linear_accy_rolling_std3']**2)\n    df['linear_accyz_rolling_std3_mag'] = np.sqrt(df['linear_accy_rolling_std3']**2+df['linear_accz_rolling_std3']**2)\n    df['linear_acczx_rolling_std3_mag'] = np.sqrt(df['linear_accz_rolling_std3']**2+df['linear_accx_rolling_std3']**2)\n    df['linear_acc_rolling_std3_mag'] = np.sqrt(df['linear_accx_rolling_std3']**2+df['linear_accy_rolling_std3']**2+df['linear_accz_rolling_std3']**2)\n\n    df['linear_accxy_rolling_min3_mag'] = np.sqrt(df['linear_accx_rolling_min3']**2+df['linear_accy_rolling_min3']**2)  \n    df['linear_accyz_rolling_min3_mag'] = np.sqrt(df['linear_accy_rolling_min3']**2+df['linear_accz_rolling_min3']**2)  \n    df['linear_acczx_rolling_min3_mag'] = np.sqrt(df['linear_accz_rolling_min3']**2+df['linear_accx_rolling_min3']**2)  \n    df['linear_acc_rolling_min3_mag'] = np.sqrt(df['linear_accx_rolling_min3']**2+df['linear_accy_rolling_min3']**2+df['linear_accz_rolling_min3']**2)  \n\n    total = df['linear_acc_x']+df['linear_acc_y']+df['linear_acc_z']+1e-8\n    df['linear_acc_x_div'] = df['linear_acc_x'] / total\n    df['linear_acc_y_div'] = df['linear_acc_y'] / total\n    df['linear_acc_z_div'] = df['linear_acc_z'] / total\n\n    total = df['ang_acc_x']+df['ang_acc_y']+df['ang_acc_z']+1e-8\n    df['ang_acc_x_div'] = df['ang_acc_x'] / total\n    df['ang_acc_y_div'] = df['ang_acc_y'] / total\n    df['ang_acc_z_div'] = df['ang_acc_z'] / total\n\n    total = df['angular_vel_x_diff']+df['angular_vel_y_diff']+df['angular_vel_z_diff']+1e-8\n    df['angular_vel_x_diff_div'] = df['angular_vel_x_diff'] / total\n    df['angular_vel_y_diff_div'] = df['angular_vel_y_diff'] / total\n    df['angular_vel_z_diff_div'] = df['angular_vel_z_diff'] / total\n    return df\n\ndef ImuTofThm_FE(df):\n\n    linear_accel_list = []\n    for _, group in df.groupby('sequence_id'):\n        acc_data_group = group[['acc_x', 'acc_y', 'acc_z']]\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']] # Порядок x,y,z,w для Scipy\n        linear_accel_group = remove_gravity_from_acc(acc_data_group, rot_data_group)\n        linear_accel_list.append(pd.DataFrame(linear_accel_group, columns=['linear_acc_x', 'linear_acc_y', 'linear_acc_z'], index=group.index))\n    \n    df_linear_accel = pd.concat(linear_accel_list)\n    df = pd.concat([df, df_linear_accel], axis=1)\n\n    df['acc_mag'] = np.sqrt(df['acc_x']**2 + df['acc_y']**2 + df['acc_z']**2)\n    df['rot_angle'] = 2 * np.arccos(df['rot_w'].clip(-1, 1))\n\n    df['acc_mag_jerk'] = df.groupby('sequence_id')['acc_mag'].diff().fillna(0)\n    df['rot_angle_vel'] = df.groupby('sequence_id')['rot_angle'].diff().fillna(0)  \n\n    angular_vel_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']]\n        angular_vel_group = calculate_angular_velocity_from_quat(rot_data_group)\n        angular_vel_list.append(pd.DataFrame(angular_vel_group, columns=['angular_vel_x', 'angular_vel_y', 'angular_vel_z'], index=group.index))\n\n\n    df_angular_vel = pd.concat(angular_vel_list)\n    df = pd.concat([df, df_angular_vel], axis=1)\n\n    angular_distance_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']]\n        angular_dist_group = calculate_angular_distance(rot_data_group)\n        angular_distance_list.append(pd.DataFrame(angular_dist_group, columns=['angular_distance'], index=group.index))\n    \n    df_angular_distance = pd.concat(angular_distance_list)\n    df = pd.concat([df, df_angular_distance], axis=1)\n\n    angular_speed_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['angular_vel_x', 'angular_vel_y', 'angular_vel_z']]\n        angular_dist_group = calculate_angular_speed(rot_data_group)\n        angular_speed_list.append(pd.DataFrame(angular_dist_group, columns=['angular_speed'], index=group.index))\n    \n    df_angular_speed = pd.concat(angular_speed_list)\n    df = pd.concat([df, df_angular_speed], axis=1)\n\n    angular_acc_list = []\n\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['angular_vel_x', 'angular_vel_y', 'angular_vel_z']]\n        angular_dist_group = calculate_angular_acceleration(rot_data_group)\n\n        new_index = group.index[1:]\n\n        acc_df = pd.DataFrame(\n            angular_dist_group,\n            columns=['ang_acc_x', 'ang_acc_y', 'ang_acc_z'],\n            index=new_index\n        )\n\n        angular_acc_list.append(acc_df)\n\n    df_angular_acc = pd.concat(angular_acc_list)\n    df_angular_acc = df_angular_acc.reindex(df.index)\n    df = pd.concat([df, df_angular_acc], axis=1)\n\n    df['linear_acc_mag'] = np.sqrt(df['linear_acc_x']**2 + df['linear_acc_y']**2 + df['linear_acc_z']**2)\n    df['linear_acc_mag_jerk'] = df.groupby('sequence_id')['linear_acc_mag'].diff().fillna(0)\n\n    df['linear_accx_jerk'] = df.groupby('sequence_id')['linear_acc_x'].diff().fillna(0)\n    df['linear_accy_jerk'] = df.groupby('sequence_id')['linear_acc_y'].diff().fillna(0)\n    df['linear_accz_jerk'] = df.groupby('sequence_id')['linear_acc_z'].diff().fillna(0)\n\n    df['linear_accx_rolling_mean5'] = df.groupby('sequence_id')['linear_acc_x'].rolling(5).mean().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_mean5'] = df.groupby('sequence_id')['linear_acc_y'].rolling(5).mean().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_mean5'] = df.groupby('sequence_id')['linear_acc_z'].rolling(5).mean().reset_index(level=0, drop=True).fillna(0)\n\n    df['linear_accx_rolling_std3'] = df.groupby('sequence_id')['linear_acc_x'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_std3'] = df.groupby('sequence_id')['linear_acc_y'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_std3'] = df.groupby('sequence_id')['linear_acc_z'].rolling(3).std().reset_index(level=0, drop=True).fillna(0)\n\n    df['linear_accx_rolling_min3'] = df.groupby('sequence_id')['linear_acc_x'].rolling(3).min().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_min3'] = df.groupby('sequence_id')['linear_acc_y'].rolling(3).min().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_min3'] = df.groupby('sequence_id')['linear_acc_z'].rolling(3).min().reset_index(level=0, drop=True).fillna(0)\n\n    df['linear_accx_rolling_max3'] = df.groupby('sequence_id')['linear_acc_x'].rolling(3).max().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accy_rolling_max3'] = df.groupby('sequence_id')['linear_acc_y'].rolling(3).max().reset_index(level=0, drop=True).fillna(0)\n    df['linear_accz_rolling_max3'] = df.groupby('sequence_id')['linear_acc_z'].rolling(3).max().reset_index(level=0, drop=True).fillna(0)\n\n    for i in range(1, 6):\n        pixel_cols = [f\"tof_{i}_v{p}\" for p in range(64)]\n        tof_data = df[pixel_cols].replace(-1, np.nan)\n    \n        df[f'tof_{i}_mean'] = tof_data.mean(axis=1)\n        df[f'tof_{i}_std'] = tof_data.std(axis=1)\n        df[f'tof_{i}_min'] = tof_data.min(axis=1)\n        df[f'tof_{i}_max'] = tof_data.max(axis=1)\n\n        tof_mode = 16\n        region_size = 64 // tof_mode\n        for r in range(tof_mode):\n            region_data = tof_data.iloc[:, r*region_size : (r+1)*region_size]\n            df[f'tof{tof_mode}_{i}_region_{r}_mean'] = region_data.mean(axis=1)\n            df[f'tof{tof_mode}_{i}_region_{r}_std'] = region_data.std(axis=1)\n            df[f'tof{tof_mode}_{i}_region_{r}_min'] = region_data.min(axis=1)\n            df[f'tof{tof_mode}_{i}_region_{r}_max'] = region_data.max(axis=1)\n    return df\n\ndef gbdt_feature_engineering(df):\n    linear_accel_list = []\n    for _, group in df.groupby('sequence_id'):\n        acc_data_group = group[['acc_x', 'acc_y', 'acc_z']]\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']] # Порядок x,y,z,w для Scipy\n        linear_accel_group = remove_gravity_from_acc(acc_data_group, rot_data_group)\n        linear_accel_list.append(pd.DataFrame(linear_accel_group, columns=['linear_acc_x', 'linear_acc_y', 'linear_acc_z'], index=group.index))\n    \n    df_linear_accel = pd.concat(linear_accel_list)\n    df = pd.concat([df, df_linear_accel], axis=1)\n\n    df['acc_mag'] = np.sqrt(df['acc_x']**2 + df['acc_y']**2 + df['acc_z']**2)\n    df['rot_angle'] = 2 * np.arccos(df['rot_w'].clip(-1, 1))\n\n    df['acc_mag_jerk'] = df.groupby('sequence_id')['acc_mag'].diff().fillna(0)\n    df['rot_angle_vel'] = df.groupby('sequence_id')['rot_angle'].diff().fillna(0)  \n\n    angular_vel_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']]\n        angular_vel_group = calculate_angular_velocity_from_quat(rot_data_group)\n        angular_vel_list.append(pd.DataFrame(angular_vel_group, columns=['angular_vel_x', 'angular_vel_y', 'angular_vel_z'], index=group.index))\n\n\n    df_angular_vel = pd.concat(angular_vel_list)\n    df = pd.concat([df, df_angular_vel], axis=1)\n\n\n    angular_distance_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['rot_x', 'rot_y', 'rot_z', 'rot_w']]\n        angular_dist_group = calculate_angular_distance(rot_data_group)\n        angular_distance_list.append(pd.DataFrame(angular_dist_group, columns=['angular_distance'], index=group.index))\n    \n    df_angular_distance = pd.concat(angular_distance_list)\n    df = pd.concat([df, df_angular_distance], axis=1)\n\n    angular_speed_list = []\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['angular_vel_x', 'angular_vel_y', 'angular_vel_z']]\n        angular_dist_group = calculate_angular_speed(rot_data_group)\n        angular_speed_list.append(pd.DataFrame(angular_dist_group, columns=['angular_speed'], index=group.index))\n    \n    df_angular_speed = pd.concat(angular_speed_list)\n    df = pd.concat([df, df_angular_speed], axis=1)\n\n    angular_acc_list = []\n\n    for _, group in df.groupby('sequence_id'):\n        rot_data_group = group[['angular_vel_x', 'angular_vel_y', 'angular_vel_z']]\n        angular_dist_group = calculate_angular_acceleration(rot_data_group)\n\n        new_index = group.index[1:]\n\n        acc_df = pd.DataFrame(\n            angular_dist_group,\n            columns=['ang_acc_x', 'ang_acc_y', 'ang_acc_z'],\n            index=new_index\n        )\n\n        angular_acc_list.append(acc_df)\n\n    df_angular_acc = pd.concat(angular_acc_list)\n    df_angular_acc = df_angular_acc.reindex(df.index)\n    df = pd.concat([df, df_angular_acc], axis=1)\n    return df\n\ndef GBDT_IMU_FE(df, train=True):\n\n    df = pl.from_pandas(df)\n\n    agg_exprs = []\n    for c in ['acc_x', 'acc_y','acc_z', 'rot_w', 'rot_x', 'rot_y', \n              'rot_z', 'linear_acc_x', 'linear_acc_y', 'linear_acc_z', 'acc_mag', 'rot_angle',\n              'acc_mag_jerk', 'rot_angle_vel', 'angular_vel_x', 'angular_vel_y',\n              'angular_vel_z', 'angular_distance', 'angular_speed', 'ang_acc_x',\n              'ang_acc_y', 'ang_acc_z']:\n        agg_exprs += [\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            pl.col(c).first().alias(f\"{c}_first\"),\n            pl.col(c).last().alias(f\"{c}_last\"),\n            pl.col(c).quantile(0.25, \"nearest\").alias(f\"{c}_t25\"),\n            pl.col(c).quantile(0.75, \"nearest\").alias(f\"{c}_t75\"),\n            (pl.col(c).last() - pl.col(c).first()).alias(f\"{c}_delta\"),\n            pl.corr(\"sequence_counter\", c).alias(f\"{c}_corr_time\"),\n            pl.col(c).diff().mean().alias(f\"{c}_diff_mean\"),\n            pl.col(c).diff().std().alias(f\"{c}_diff_std\"),\n            pl.col(c).skew().alias(f\"{c}_skew\"),\n            pl.col(c).kurtosis().alias(f\"{c}_kurt\"),\n            pl.col(c).diff().abs().gt(0).sum().alias(f\"{c}_n_changes\")\n        ]\n        agg_exprs += [\n            pl.when(pl.col(\"sequence_counter\") < 0.1 * pl.max(\"sequence_counter\"))\n              .then(pl.col(c)).otherwise(None).mean().alias(f\"{c}_seg1_mean\"),\n            pl.when(pl.col(\"sequence_counter\") > 0.9 * pl.max(\"sequence_counter\"))\n              .then(pl.col(c)).otherwise(None).mean().alias(f\"{c}_seg3_mean\"),\n        ]\n\n        for part_name, part_expr in [\n            ('first_30pct', pl.col('sequence_counter') < pl.max('sequence_counter') * 0.3),\n            ('middle_40pct', (pl.col('sequence_counter') >= pl.max('sequence_counter') * 0.3) & (pl.col('sequence_counter') < pl.max('sequence_counter') * 0.7)),\n            ('last_30pct', pl.col('sequence_counter') >= pl.max('sequence_counter') * 0.7),\n        ]:\n            agg_exprs.extend([\n                (pl.when(part_expr).then(pl.col(c))).mean().alias(f'{c}_mean_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).std().alias(f'{c}_std_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).diff().mean().alias(f'{c}_diff_mean_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).diff().std().alias(f'{c}_diff_std_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).quantile(0.25, \"nearest\").alias(f'{c}_t25_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).quantile(0.75, \"nearest\").alias(f'{c}_t75_{part_name}'),\n            ])\n\n    if train:\n        agg_exprs += [pl.col(\"gesture\").first().alias(\"gesture\")]\n    \n    agg_exprs += [pl.col(\"subject\").first().alias(\"subject\")]\n    agg_exprs += [pl.col(\"sequence_counter\").max().alias('sequence_counter')]\n\n    if train:\n        agg_exprs += [pl.col('row_id').first().alias('row_id')]\n        agg_exprs += [pl.col('orientation').first().alias('orientation')]\n        agg_exprs += [pl.col(\"sequence_type\").first().alias(\"sequence_type\")]\n\n    df = (\n        df\n        .group_by(\"sequence_id\", maintain_order=True)\n        .agg(agg_exprs)\n        .to_pandas()\n    )\n\n    return df\n\ndef GBDT_IMU_TOF_THM_FE(df, train=True):\n\n    df = pl.from_pandas(df)\n\n    agg_exprs = []\n    for c in ['acc_x', 'acc_y','acc_z', 'rot_w', 'rot_x', 'rot_y', \n              'rot_z', 'linear_acc_x', 'linear_acc_y', 'linear_acc_z', 'acc_mag', 'rot_angle',\n              'acc_mag_jerk', 'rot_angle_vel', 'angular_vel_x', 'angular_vel_y',\n              'angular_vel_z', 'angular_distance', 'angular_speed', 'ang_acc_x',\n              'ang_acc_y', 'ang_acc_z'] + ['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5'] + [f'tof_{i+1}_v{j}' for i in range(5) for j in range(64)]:\n        agg_exprs += [\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            pl.col(c).first().alias(f\"{c}_first\"),\n            pl.col(c).last().alias(f\"{c}_last\"),\n            pl.col(c).quantile(0.25, \"nearest\").alias(f\"{c}_t25\"),\n            pl.col(c).quantile(0.75, \"nearest\").alias(f\"{c}_t75\"),\n            (pl.col(c).last() - pl.col(c).first()).alias(f\"{c}_delta\"),\n            pl.corr(\"sequence_counter\", c).alias(f\"{c}_corr_time\"),\n            pl.col(c).diff().mean().alias(f\"{c}_diff_mean\"),\n            pl.col(c).diff().std().alias(f\"{c}_diff_std\"),\n            pl.col(c).skew().alias(f\"{c}_skew\"),\n            pl.col(c).kurtosis().alias(f\"{c}_kurt\"),\n            pl.col(c).diff().abs().gt(0).sum().alias(f\"{c}_n_changes\")\n        ]\n        agg_exprs += [\n            pl.when(pl.col(\"sequence_counter\") < 0.1 * pl.max(\"sequence_counter\"))\n              .then(pl.col(c)).otherwise(None).mean().alias(f\"{c}_seg1_mean\"),\n            pl.when(pl.col(\"sequence_counter\") > 0.9 * pl.max(\"sequence_counter\"))\n              .then(pl.col(c)).otherwise(None).mean().alias(f\"{c}_seg3_mean\"),\n        ]\n\n        for part_name, part_expr in [\n            ('first_30pct', pl.col('sequence_counter') < pl.max('sequence_counter') * 0.3),\n            ('middle_40pct', (pl.col('sequence_counter') >= pl.max('sequence_counter') * 0.3) & (pl.col('sequence_counter') < pl.max('sequence_counter') * 0.7)),\n            ('last_30pct', pl.col('sequence_counter') >= pl.max('sequence_counter') * 0.7),\n        ]:\n            agg_exprs.extend([\n                (pl.when(part_expr).then(pl.col(c))).mean().alias(f'{c}_mean_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).std().alias(f'{c}_std_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).diff().mean().alias(f'{c}_diff_mean_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).diff().std().alias(f'{c}_diff_std_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).quantile(0.25, \"nearest\").alias(f'{c}_t25_{part_name}'),\n                (pl.when(part_expr).then(pl.col(c))).quantile(0.75, \"nearest\").alias(f'{c}_t75_{part_name}'),\n            ])\n\n    \n    agg_exprs += [pl.col(\"subject\").first().alias(\"subject\")]\n    agg_exprs += [pl.col(\"sequence_counter\").max().alias('sequence_counter')]\n\n\n    df = (\n        df\n        .group_by(\"sequence_id\", maintain_order=True)\n        .agg(agg_exprs)\n        .to_pandas()\n    )\n    return df","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:26.150118Z","iopub.status.busy":"2025-08-19T10:07:26.149904Z","iopub.status.idle":"2025-08-19T10:07:26.215849Z","shell.execute_reply":"2025-08-19T10:07:26.215333Z"},"papermill":{"duration":0.071454,"end_time":"2025-08-19T10:07:26.216951","exception":false,"start_time":"2025-08-19T10:07:26.145497","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pad_len=127\n# imu\nexp068_scaler = load(f'{exp_paths[\"exp068\"]}scaler.pkl')   \nexp070_scaler = load(f'{exp_paths[\"exp070\"]}scaler.pkl')\nexp092_scaler = load(f'{exp_paths[\"exp092\"]}scaler.pkl')\nexp093_scaler = load(f'{exp_paths[\"exp093\"]}scaler.pkl')\nexp104_scaler = load(f'{exp_paths[\"exp104\"]}scaler.pkl')\nexp326_scaler = load(f'{exp_paths[\"exp326\"]}scaler.pkl')\nexp329_scaler = load(f'{exp_paths[\"exp329\"]}scaler.pkl')\n\n# imu, tof, thm\nexp086_scaler = load(f'{exp_paths[\"exp086\"]}scaler.pkl')\nexp137_scaler = load(f'{exp_paths[\"exp137\"]}scaler.pkl')\n\nle = load('/kaggle/input/cmi-detect-behavior-gesture-encoder/Encoder_model.pickle')","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:26.224319Z","iopub.status.busy":"2025-08-19T10:07:26.223825Z","iopub.status.idle":"2025-08-19T10:07:26.326780Z","shell.execute_reply":"2025-08-19T10:07:26.326249Z"},"papermill":{"duration":0.107818,"end_time":"2025-08-19T10:07:26.327931","exception":false,"start_time":"2025-08-19T10:07:26.220113","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sys\nsys.path.append(\"/kaggle/input/cmi-detect-behavior-model-structure\")\n\n# imu\nfrom exp068 import exp068_model\nfrom exp070 import exp070_model\nfrom exp092 import exp092_model\nfrom exp093 import exp093_model\nfrom exp104 import exp104_model\nfrom exp326 import exp326_model\nfrom exp329 import exp329_model\n\n# imu, tof, thm\nfrom exp086 import exp086_model\nfrom exp137 import exp137_model","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:26.335254Z","iopub.status.busy":"2025-08-19T10:07:26.335046Z","iopub.status.idle":"2025-08-19T10:07:26.431526Z","shell.execute_reply":"2025-08-19T10:07:26.431031Z"},"papermill":{"duration":0.101352,"end_time":"2025-08-19T10:07:26.432541","exception":false,"start_time":"2025-08-19T10:07:26.331189","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_models(config, model_class, model_path, pad_len, device, extra_args=None):\n    \"\"\"\n    共通のモデル読み込み関数\n\n    config: モデル設定辞書\n    model_class: モデルクラス\n    model_path: 学習済みモデルファイルのパス（fold番号は後で追加）\n    pad_len: シーケンス長\n    device: 推論デバイス\n    extra_args: 追加のキーワード引数（辞書）\n    \"\"\"\n    models = []\n    extra_args = extra_args or {}\n\n    for i in range(config['n_splits']):\n        # モデルインスタンス生成\n        model = model_class(\n            pad_len=pad_len,\n            acc_dim=len(config['acc_cols']),\n            rot_dim=len(config['rot_cols']),\n            n_classes=len(config['target_le'].classes_),\n            weight_decay=config['WD'],\n            **extra_args\n        )\n\n        # 特殊な入力次元（imu_dim / tof_dim / thm_tof_cols）に対応\n        if 'features' in config:\n            model.imu_dim = len(config['features'])\n        if 'thm_tof_cols' in config:\n            model.tof_dim = len(config['thm_tof_cols'])\n\n        # DataParallel & 重み読み込み\n        model = nn.DataParallel(model)\n        model.load_state_dict(torch.load(f\"{model_path}{config['model_name']}_fold{i}.bin\"))\n        model.to(device)\n        model.eval()\n\n        models.append(model)\n\n    return models\n\n\n# === 実際の読み込み ===\n# imu\nexp068_models = load_models(\n    config=configs['exp068'],\n    model_class=exp068_model,\n    model_path=exp_paths['exp068'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'imu_dim': len(configs['exp068']['features']),\n        'model_args': configs['exp068']['model_args']\n    }\n)\n\nexp070_models = load_models(\n    config=configs['exp070'],\n    model_class=exp070_model,\n    model_path=exp_paths['exp070'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'imu_dim': len(configs['exp070']['features']),\n        'model_args': configs['exp070']['model_args']\n    }\n)\n\nexp092_models = load_models(\n    config=configs['exp092'],\n    model_class=exp092_model,\n    model_path=exp_paths['exp092'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'imu_dim': len(configs['exp092']['features']),\n        'model_args': configs['exp092']['model_args']\n    }\n)\n\nexp093_models = load_models(\n    config=configs['exp093'],\n    model_class=exp093_model,\n    model_path=exp_paths['exp093'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'imu_dim': len(configs['exp093']['features']),\n        'model_args': configs['exp093']['model_args']\n    }\n)\n\n\n\nexp104_models = []\nfor i in range(configs['exp104']['n_splits']):\n    model = exp104_model(\n        in_channels=len(configs['exp104']['features']),\n        num_classes=len(configs['exp104']['target_le'].classes_),\n        args=configs['exp104']['model_args']\n    )\n    \n    model = nn.DataParallel(model)\n    model.load_state_dict(\n        torch.load(f'{exp_paths[\"exp104\"]}{configs[\"exp104\"][\"model_name\"]}_fold{i}.bin')\n    )\n    model.to(configs['exp104']['device'])\n    model.eval()\n    exp104_models.append(model)\n\nexp117_features = np.load(f'{exp_paths[\"exp117\"]}columns.npy', allow_pickle=True)\nexp117_models = [load(f'{exp_paths[\"exp117\"]}{configs[\"exp117\"][\"model_name\"]}_fold{i}.pkl') for i in range(configs[\"exp117\"][\"n_splits\"])]\n\nexp326_models = load_models(\n    config=configs['exp326'],\n    model_class=exp326_model,\n    model_path=exp_paths['exp326'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'imu_dim': len(configs['exp326']['features'])\n    } \n)\n\nexp329_models = load_models(\n    config=configs['exp329'],\n    model_class=exp329_model,\n    model_path=exp_paths['exp329'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'imu_dim': len(configs['exp329']['features']),\n        'model_args': configs['exp329']['model_args']\n    }\n)\n\n# imu, tof, thm\nexp086_models = load_models(\n    config=configs['exp086'],\n    model_class=exp086_model,\n    model_path=exp_paths['exp086'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'tof_dim': len(configs['exp086']['thm_tof_cols']),\n        'args': configs['exp086']['model_args']\n    }\n)\n\nexp137_models = load_models(\n    config=configs['exp137'],\n    model_class=exp137_model,\n    model_path=exp_paths['exp137'],\n    pad_len=pad_len,\n    device=device,\n    extra_args={\n        'tof_dim': len(configs['exp137']['thm_tof_cols']),\n        'args': configs['exp137']['model_args']\n    }\n)\n\n\nexp179_features = np.load(f'{exp_paths[\"exp179\"]}columns.npy', allow_pickle=True)\nexp179_models = [load(f'{exp_paths[\"exp179\"]}{configs[\"exp179\"][\"model_name\"]}_fold{i}.pkl') for i in range(configs[\"exp179\"][\"n_splits\"])]\n\nexp226_features = np.load(f'{exp_paths[\"exp226\"]}columns.npy', allow_pickle=True)\nexp226_models = [load(f'{exp_paths[\"exp226\"]}{configs[\"exp226\"][\"model_name\"]}_fold{i}.pkl') for i in range(configs[\"exp226\"][\"n_splits\"])]","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:26.439895Z","iopub.status.busy":"2025-08-19T10:07:26.439662Z","iopub.status.idle":"2025-08-19T10:07:39.062346Z","shell.execute_reply":"2025-08-19T10:07:39.061509Z"},"papermill":{"duration":12.628074,"end_time":"2025-08-19T10:07:39.063796","exception":false,"start_time":"2025-08-19T10:07:26.435722","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tof_cols = ['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5'] + \\\n           [f'tof_{i+1}_v{j}' for i in range(5) for j in range(64)]\n\ndef run_inference(df_seq, fe_func, config, scaler, models):\n    df_seq = fe_func(df_seq)\n    mat = preprocess_sequence(df_seq, config['features'], scaler)\n\n    pad = pad_sequences_torch([mat], maxlen=pad_len, padding='post', truncating='post')\n\n    # inference\n    preds = []\n    for model in models:\n        model.eval()\n        with torch.no_grad():\n            x = torch.FloatTensor(pad).to(device)\n            logits = model(x)\n            logits = nn.Softmax(dim=1)(logits)\n            preds.append(logits.detach().cpu().numpy())\n\n    preds = np.array(preds)\n    pred = preds.mean(axis=0)\n    return pred\n\ndef gbdt_test_data(df_seq, df_demo, fe_fuc0, fe_fuc1):\n    df_seq = fe_fuc0(df_seq)\n    df_seq = fe_fuc1(df_seq, train=False)\n    test = pd.merge(df_seq, df_demo, on='subject', how='left')\n    return test\n    \n   \ndef gbdt_inferenc(test, features, models):\n    data = test[features]\n\n    for c in data.columns:\n        if data[c].dtype == \"float64\":\n            data[c] = data[c].astype(\"float32\")\n        if data[c].dtype == \"int64\":\n            data[c] = data[c].astype(\"int32\")\n\n    pred = []\n    for model in models:\n        pred.append(model.predict_proba(data))\n\n        \n    pred = np.mean(pred, axis=0)\n    return pred\n    \n    \ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \"\"\"Prediction function for Kaggle competition\"\"\"\n\n    df_seq = sequence.to_pandas()\n    df_demo = demographics.to_pandas()\n    is_imu_only = df_seq[tof_cols].isnull().all(axis=1).all()\n    \n    if is_imu_only:\n        exp068_pred = run_inference(\n                df_seq,\n                fe_func=Imu_FE,\n                config=configs['exp068'],\n                scaler=exp068_scaler,\n                models=exp068_models\n        )\n\n        exp070_pred = run_inference(\n            df_seq,\n            fe_func=Imu_FE,\n            config=configs['exp070'],\n            scaler=exp070_scaler,\n            models=exp070_models\n        )\n\n        exp092_pred = run_inference(\n            df_seq,\n            fe_func=Imu_FE,\n            config=configs['exp092'],\n            scaler=exp092_scaler,\n            models=exp092_models\n        )\n\n        exp093_pred = run_inference(\n            df_seq,\n            fe_func=Imu_FE,\n            config=configs['exp093'],\n            scaler=exp093_scaler,\n            models=exp093_models\n        )\n\n        exp104_pred = run_inference(\n            df_seq,\n            fe_func=Imu_FE,\n            config=configs['exp104'],\n            scaler=exp104_scaler,\n            models=exp104_models\n        )\n\n        exp326_pred = run_inference(\n            df_seq,\n            fe_func=Imu_FE,\n            config=configs['exp326'],\n            scaler=exp326_scaler,\n            models=exp326_models\n        )\n\n        exp329_pred = run_inference(\n            df_seq,\n            fe_func=Imu_FE,\n            config=configs['exp329'],\n            scaler=exp329_scaler,\n            models=exp329_models\n        )\n\n\n\n        gbdt_test = gbdt_test_data(\n            df_seq,\n            df_demo,\n            fe_fuc0=gbdt_feature_engineering,\n            fe_fuc1=GBDT_IMU_FE\n        )\n\n        exp117_pred = gbdt_inferenc(\n            test=gbdt_test,\n            features=exp117_features,\n            models=exp117_models\n        )    \n\n        pred = (\n            exp068_pred\n            + exp070_pred\n            + exp092_pred\n            + exp093_pred\n            + exp104_pred\n            + exp117_pred\n            + exp326_pred\n            + exp329_pred\n        ) / 8\n\n    else:\n        exp068_pred = run_inference(\n                df_seq,\n                fe_func=Imu_FE,\n                config=configs['exp068'],\n                scaler=exp068_scaler,\n                models=exp068_models\n        )\n        \n        exp086_pred = run_inference(\n            df_seq,\n            fe_func=ImuTofThm_FE,\n            config=configs['exp086'],\n            scaler=exp086_scaler,\n            models=exp086_models\n        )\n\n        exp137_pred = run_inference(\n            df_seq,\n            fe_func=ImuTofThm_FE,\n            config=configs['exp137'],\n            scaler=exp137_scaler,\n            models=exp137_models\n        )\n\n        exp326_pred = run_inference(\n            df_seq,\n            fe_func=Imu_FE,\n            config=configs['exp326'],\n            scaler=exp326_scaler,\n            models=exp326_models\n        )\n\n        gbdt_test = gbdt_test_data(\n            df_seq,\n            df_demo,\n            fe_fuc0=gbdt_feature_engineering,\n            fe_fuc1=GBDT_IMU_TOF_THM_FE,\n        )\n\n\n        exp179_pred = gbdt_inferenc(\n            test=gbdt_test,\n            features=exp179_features,\n            models=exp179_models\n        )\n\n        exp226_pred = gbdt_inferenc(\n            test=gbdt_test,\n            features=exp226_features,\n            models=exp226_models\n        )\n\n\n        pred = (\n            exp086_pred\n            + exp137_pred\n            + exp068_pred\n            + exp179_pred\n            + exp226_pred\n            + exp326_pred\n        ) / 6\n\n    \n    \n    pred = pred.argmax(axis=1)\n    pred = le.inverse_transform(pred)\n    \n    \n    return pred[0]\n    \n# Kaggle competition interface\nimport kaggle_evaluation.cmi_inference_server\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":{"execution":{"iopub.execute_input":"2025-08-19T10:07:39.072375Z","iopub.status.busy":"2025-08-19T10:07:39.072128Z","iopub.status.idle":"2025-08-19T10:07:49.888161Z","shell.execute_reply":"2025-08-19T10:07:49.887371Z"},"papermill":{"duration":10.821564,"end_time":"2025-08-19T10:07:49.889533","exception":false,"start_time":"2025-08-19T10:07:39.067969","status":"completed"},"tags":[],"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pd.read_parquet('submission.parquet')","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:49.898026Z","iopub.status.busy":"2025-08-19T10:07:49.897531Z","iopub.status.idle":"2025-08-19T10:07:49.944428Z","shell.execute_reply":"2025-08-19T10:07:49.943849Z"},"papermill":{"duration":0.051997,"end_time":"2025-08-19T10:07:49.945470","exception":false,"start_time":"2025-08-19T10:07:49.893473","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def custom_metrics(y_true, y_pred):\n    target_gestures = [\n        'Above ear - pull hair',\n        'Cheek - pinch skin',\n        'Eyebrow - pull hair',\n        'Eyelash - pull hair',\n        'Forehead - pull hairline',\n        'Forehead - scratch',\n        'Neck - pinch skin',\n        'Neck - scratch',\n    ]\n\n    non_target_gestures = [\n        'Write name on leg',\n        'Wave hello',\n        'Glasses on/off',\n        'Text on phone',\n        'Write name in air',\n        'Feel around in tray and pull out an object',\n        'Scratch knee/leg skin',\n        'Pull air toward your face',\n        'Drink from bottle/cup',\n        'Pinch knee/leg skin'\n    ]\n\n    all_classes = target_gestures + non_target_gestures\n\n    invalid_types = {i for i in y_pred.unique() if i not in all_classes}\n    if invalid_types:\n        raise ParticipantVisibleError(\n                f\"Invalid gesture values in submission: {invalid_types}\"\n        )\n\n    y_true_bin = y_true.isin(target_gestures).values\n    y_pred_bin = y_pred.isin(target_gestures).values\n    f1_binary = f1_score(\n            y_true_bin,\n            y_pred_bin,\n            pos_label=True,\n            zero_division=0,\n            average='binary'\n    )\n\n    y_true_mc = y_true.apply(lambda x: x if x in target_gestures else 'non_target')\n    y_pred_mc = y_pred.apply(lambda x: x if x in target_gestures else 'non_target')\n\n    f1_macro = f1_score(\n            y_true_mc,\n            y_pred_mc,\n            average='macro',\n            zero_division=0\n    )\n    return f1_binary, f1_macro, 0.5 * f1_binary + 0.5 * f1_macro\n\n\nexp068 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp068-train/lstm_imu_imu_only_train.csv')\nexp070 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp070-train/lstm_imu_imu_only_train.csv')\nexp092 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp092-train/lstm_imu_imu_only_train.csv')\nexp093 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp093-train/lstm_imu_imu_only_train.csv')\nexp104 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp104-train/gru_imu_imu_only_train.csv')\nexp117 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp117-train/oof_cat_imu.csv')\nexp326 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp326-train/gru_imu_imu_only_train.csv')\nexp329 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp329-train/lstm_imu_imu_only_train.csv')\n\nle = load('/kaggle/input/cmi-detect-behavior-gesture-encoder/Encoder_model.pickle')\npred = (exp068.iloc[:, 3:].values\n         + exp070.iloc[:, 3:].values\n         + exp104.iloc[:, 3:].values\n         + exp092.iloc[:, 3:].values\n         + exp093.iloc[:, 3:].values\n         + exp117.iloc[:, -18:].values\n         + exp326.iloc[:, 3:].values\n         + exp329.iloc[:, 3:].values\n        ) / 8\npred = np.argmax(pred, axis=1)\npred = le.inverse_transform(pred)\npred = pd.Series(pred)\ncustom_metrics(exp326['gesture'], pred)","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:49.953621Z","iopub.status.busy":"2025-08-19T10:07:49.953409Z","iopub.status.idle":"2025-08-19T10:07:54.058987Z","shell.execute_reply":"2025-08-19T10:07:54.058312Z"},"papermill":{"duration":4.111025,"end_time":"2025-08-19T10:07:54.060239","exception":false,"start_time":"2025-08-19T10:07:49.949214","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"code","source":"exp086 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp086-train/gru_imu_tof_thm_imu_only_train.csv')\nexp137 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp137-train/lstm_imu_tof_thm_imu_only_train.csv')\nexp179 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp179-train/oof_cat_imu_tof_thm.csv')\nexp226 = pd.read_csv('/kaggle/input/cmi-detect-behavior-exp226-train/oof_xgb_imu_tof_thm.csv')\n\nindex = exp326['sequence_id'].isin(exp086['sequence_id'])\npred = (exp086.iloc[:, 3:].values\n        +exp137.iloc[:, 3:].values\n        + exp326.loc[index].iloc[:, 3:].values\n        + exp068.loc[index].iloc[:, 3:].values\n        + exp179.iloc[:, -18:].values\n        + exp226.iloc[:, -18:].values\n        ) / 6\npred = np.argmax(pred, axis=1)\npred = le.inverse_transform(pred)\npred = pd.Series(pred)\ncustom_metrics(exp086['gesture'], pred)","metadata":{"execution":{"iopub.execute_input":"2025-08-19T10:07:54.068985Z","iopub.status.busy":"2025-08-19T10:07:54.068687Z","iopub.status.idle":"2025-08-19T10:09:15.254773Z","shell.execute_reply":"2025-08-19T10:09:15.253996Z"},"papermill":{"duration":81.195338,"end_time":"2025-08-19T10:09:15.259535","exception":false,"start_time":"2025-08-19T10:07:54.064197","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}