{"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":"none","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 1. Tổng quan","metadata":{}},{"cell_type":"markdown","source":"Mục tiêu là khám phá các thuật toán học máy giám sát để phát hiện hành vi dựa trên dữ liệu cảm biến. Chúng ta sẽ xử lý các vấn đề như giá trị thiếu, xây dựng đường ống (pipelines), tạo ra sơ đồ phụ thuộc, và áp dụng mã hóa một lần.\n\nChúng ta sẽ sử dụng dữ liệu từ các cảm biến để dự đoán hành vi của các đối tượng trong môi trường. Các thuật toán học máy sẽ giúp phân loại hành vi dựa trên các đặc điểm đo lường được từ dữ liệu cảm biến. Mục tiêu cuối cùng là xây dựng một mô hình có khả năng dự đoán chính xác hành vi dựa trên các tính năng cảm biến được cung cấp.","metadata":{}},{"cell_type":"markdown","source":"# Outline","metadata":{}},{"cell_type":"markdown","source":"## 1.1 Tải và Khám Phá Dữ Liệu","metadata":{}},{"cell_type":"markdown","source":"Tải dữ liệu và tiến hành kiểm tra sơ bộ (kích thước, loại dữ liệu, và các thuộc tính).\n\nPhân tích thống kê cho các tính năng trong dataset, bao gồm:\n* Giá trị thiếu và cách xử lý.\n* Các tính năng đặc biệt như giới tính, tuổi, hành vi.\n* Kiểm tra sự phân phối mục tiêu và đặc tính dữ liệu qua biểu đồ phân phối và biểu đồ hộp.","metadata":{}},{"cell_type":"markdown","source":"## 1.2 Tiền Xử Lý Dữ Liệu","metadata":{}},{"cell_type":"markdown","source":"* Xử lý các giá trị thiếu bằng các phương pháp phù hợp.\n* Tách biệt dữ liệu thành các nhóm: cảm biến IMU, ToF, và mục tiêu.\n* Chuẩn hóa và chuẩn bị dữ liệu cho mô hình.\n* Feature Engineering để tối ưu hóa đặc trưng dữ liệu.","metadata":{}},{"cell_type":"markdown","source":"## 1.3 Khám Phá Dữ Liệu và Trực Quan Hóa","metadata":{}},{"cell_type":"markdown","source":"Trực quan hóa mối quan hệ giữa các tính năng:\n* Tương quan Pearson giữa các tính năng.\n* Biểu đồ phân tán giữa các tính năng chính (ví dụ: chiều cao, trọng lượng, v.v.).\n* Biểu đồ hình tròn cho các tính năng phân loại như giới tính.\n* Tính toán mối tương quan giữa các dữ liệu cảm biến (IMU, ToF, v.v.).","metadata":{}},{"cell_type":"markdown","source":"## 1.4 Xây Dựng Mô Hình Học Máy","metadata":{}},{"cell_type":"markdown","source":"Chọn mô hình học máy phù hợp: Linear Regression, Decision Tree, Random Forest, XGBoost, MLP, và các mô hình hồi quy khác.\n\nTối ưu hóa mô hình qua Grid Search và Cross-Validation (K-fold).\n\nTối ưu hóa siêu tham số để cải thiện độ chính xác.","metadata":{}},{"cell_type":"markdown","source":"## 1.5 Đánh Giá Mô Hình và Tối Ưu Hóa","metadata":{}},{"cell_type":"markdown","source":"Đánh giá mô hình dựa trên các chỉ số như F1-score, accuracy, precision, recall.\n\nSử dụng ensemble learning như bagging và boosting để kết hợp các mô hình và cải thiện hiệu suất.\n\nTối ưu hóa mô hình cuối cùng để đảm bảo tính ổn định và khả năng dự đoán chính xác trong các bộ dữ liệu thực tế.","metadata":{}},{"cell_type":"markdown","source":"## 1.6 Kết Quả và Thảo Luận\n","metadata":{}},{"cell_type":"markdown","source":"Trình bày kết quả mô hình với các chỉ số đánh giá chi tiết.\n\nPhân tích các yếu tố ảnh hưởng đến độ chính xác của mô hình.\n\nKết luận về mô hình tối ưu và khả năng áp dụng vào thực tế.","metadata":{}},{"cell_type":"markdown","source":"# 2. Tổng quan dữ liệu","metadata":{}},{"cell_type":"markdown","source":"## 2.1 Tải dữ liệu và thư viện","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport warnings\nfrom matplotlib.ticker import MaxNLocator\nfrom mpl_toolkits.mplot3d import Axes3D\nimport os\nimport polars as pl\nimport kaggle_evaluation.cmi_inference_server\nfrom sklearn.metrics import accuracy_score, f1_score\nimport joblib\nfrom scipy.spatial.transform import Rotation as R","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:05:36.680289Z","iopub.execute_input":"2025-08-07T08:05:36.680997Z","iopub.status.idle":"2025-08-07T08:05:40.593117Z","shell.execute_reply.started":"2025-08-07T08:05:36.680958Z","shell.execute_reply":"2025-08-07T08:05:40.591978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tải tập dữ liệu\n#Dữ liệu cảm biến cho tập huấn luyện (Train Sensor Data): \ntrain_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")\n\n#Thông tin nhân khẩu học của đối tượng trong tập huấn luyện (giới tính, tuổi, ...): \ntrain_dem_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\")\n\n#Dữ liệu cảm biến cho tập kiểm tra:\ntest_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv\")\n\n# Thông tin nhân khẩu học của đối tượng trong tập kiểm tra:\ntest_dem_df = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:05:40.595001Z","iopub.execute_input":"2025-08-07T08:05:40.595587Z","iopub.status.idle":"2025-08-07T08:06:18.580759Z","shell.execute_reply.started":"2025-08-07T08:05:40.595546Z","shell.execute_reply":"2025-08-07T08:06:18.579949Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2.1 Khám phá dữ liệu","metadata":{}},{"cell_type":"markdown","source":"### 2.1.1 Mẫu dữ liệu \n","metadata":{}},{"cell_type":"code","source":"train_df.sample(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:18.581605Z","iopub.execute_input":"2025-08-07T08:06:18.581912Z","iopub.status.idle":"2025-08-07T08:06:18.646292Z","shell.execute_reply.started":"2025-08-07T08:06:18.581877Z","shell.execute_reply":"2025-08-07T08:06:18.645226Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dem_df.sample(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:18.647258Z","iopub.execute_input":"2025-08-07T08:06:18.647593Z","iopub.status.idle":"2025-08-07T08:06:18.660987Z","shell.execute_reply.started":"2025-08-07T08:06:18.647560Z","shell.execute_reply":"2025-08-07T08:06:18.660029Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:18.664406Z","iopub.execute_input":"2025-08-07T08:06:18.664758Z","iopub.status.idle":"2025-08-07T08:06:18.703504Z","shell.execute_reply.started":"2025-08-07T08:06:18.664731Z","shell.execute_reply":"2025-08-07T08:06:18.702532Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dem_df.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:18.704581Z","iopub.execute_input":"2025-08-07T08:06:18.704947Z","iopub.status.idle":"2025-08-07T08:06:18.728292Z","shell.execute_reply.started":"2025-08-07T08:06:18.704919Z","shell.execute_reply":"2025-08-07T08:06:18.727290Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.describe().T","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:18.729500Z","iopub.execute_input":"2025-08-07T08:06:18.730411Z","iopub.status.idle":"2025-08-07T08:06:27.188397Z","shell.execute_reply.started":"2025-08-07T08:06:18.730383Z","shell.execute_reply":"2025-08-07T08:06:27.187350Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.1.2 Kích thước ","metadata":{}},{"cell_type":"code","source":"print(\"No. of rows : \", train_df.shape[0])\nprint(\"No. of columns : \", train_df.shape[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:27.189447Z","iopub.execute_input":"2025-08-07T08:06:27.189971Z","iopub.status.idle":"2025-08-07T08:06:27.196180Z","shell.execute_reply.started":"2025-08-07T08:06:27.189933Z","shell.execute_reply":"2025-08-07T08:06:27.195180Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"No. of rows : \", test_df.shape[0])\nprint(\"No. of columns : \", test_df.shape[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:27.197265Z","iopub.execute_input":"2025-08-07T08:06:27.197633Z","iopub.status.idle":"2025-08-07T08:06:27.216648Z","shell.execute_reply.started":"2025-08-07T08:06:27.197600Z","shell.execute_reply":"2025-08-07T08:06:27.215601Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"No. of rows : \", train_dem_df.shape[0])\nprint(\"No. of columns : \", train_dem_df.shape[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:27.217718Z","iopub.execute_input":"2025-08-07T08:06:27.218102Z","iopub.status.idle":"2025-08-07T08:06:27.238970Z","shell.execute_reply.started":"2025-08-07T08:06:27.218061Z","shell.execute_reply":"2025-08-07T08:06:27.237973Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"No. of rows : \", test_dem_df.shape[0])\nprint(\"No. of columns : \", test_dem_df.shape[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:27.240052Z","iopub.execute_input":"2025-08-07T08:06:27.240348Z","iopub.status.idle":"2025-08-07T08:06:27.264403Z","shell.execute_reply.started":"2025-08-07T08:06:27.240323Z","shell.execute_reply":"2025-08-07T08:06:27.263498Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.1.3 Tóm tắt thống kê các tính năng ","metadata":{}},{"cell_type":"code","source":"train_df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:27.265475Z","iopub.execute_input":"2025-08-07T08:06:27.265815Z","iopub.status.idle":"2025-08-07T08:06:35.787550Z","shell.execute_reply.started":"2025-08-07T08:06:27.265768Z","shell.execute_reply":"2025-08-07T08:06:35.786691Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dem_df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:35.788382Z","iopub.execute_input":"2025-08-07T08:06:35.788660Z","iopub.status.idle":"2025-08-07T08:06:35.816926Z","shell.execute_reply.started":"2025-08-07T08:06:35.788639Z","shell.execute_reply":"2025-08-07T08:06:35.815939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:35.821249Z","iopub.execute_input":"2025-08-07T08:06:35.821541Z","iopub.status.idle":"2025-08-07T08:06:36.351689Z","shell.execute_reply.started":"2025-08-07T08:06:35.821516Z","shell.execute_reply":"2025-08-07T08:06:36.350645Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dem_df.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:36.353081Z","iopub.execute_input":"2025-08-07T08:06:36.353412Z","iopub.status.idle":"2025-08-07T08:06:36.383471Z","shell.execute_reply.started":"2025-08-07T08:06:36.353377Z","shell.execute_reply":"2025-08-07T08:06:36.382435Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.1.4 Giá trị bị thiếu ","metadata":{}},{"cell_type":"code","source":"datasets = {\n    \"Train Data\": train_df,\n    \"Train Demographics\": train_dem_df,\n    \"Test Data\": test_df,\n    \"Test Demographics\": test_dem_df,\n}\n\nfor name, df in datasets.items():\n    print(f\"Missing values in {name}:\")\n    missing = df.isnull().sum()\n    missing = missing[missing > 0]\n    if not missing.empty:\n        print(missing)\n    else:\n        print(\"  ✅ No missing values.\")\n    print()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:36.385063Z","iopub.execute_input":"2025-08-07T08:06:36.385432Z","iopub.status.idle":"2025-08-07T08:06:37.280778Z","shell.execute_reply.started":"2025-08-07T08:06:36.385399Z","shell.execute_reply":"2025-08-07T08:06:37.280059Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.1.5 kiểm tra giá trị trùng lặp","metadata":{}},{"cell_type":"code","source":"#Kiểm tra trùng lặp (Duplicate Rows): .duplicated(): Trả về Series boolean, dòng nào trùng lặp sẽ là True.\n#                                     .sum(): Đếm tổng số dòng trùng lặp.\n#Kiểm tra xem dữ liệu có bản ghi nào bị trùng không — quan trọng cho việc làm sạch dữ liệu.\n\n# Đếm các hàng trùng lặp trong train_df\ntrain_duplicates = train_df.duplicated().sum()\n\n# Đếm các hàng trùng lặp trong  test_df\ntest_duplicates = test_df.duplicated().sum()\n\n# Đếm các hàng trùng lặp trong train_dem_df (optional)\ntrain_dem_duplicates = train_dem_df.duplicated().sum()\n# Đếm các hàng trùng lặp trong test_dem_df (optional)\ntest_dem_duplicates = test_dem_df.duplicated().sum()\n\n# In số lượng dòng trùng:\nprint(f\"Number of duplicate rows in train_df: {train_duplicates}\")\nprint(f\"Number of duplicate rows in test_df: {test_duplicates}\")\nprint(f\"Number of duplicate rows in train_dem_df: {train_dem_duplicates}\")\nprint(f\"Number of duplicate rows in test_dem_df: {test_dem_duplicates}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:37.281603Z","iopub.execute_input":"2025-08-07T08:06:37.281817Z","iopub.status.idle":"2025-08-07T08:06:42.623764Z","shell.execute_reply.started":"2025-08-07T08:06:37.281798Z","shell.execute_reply":"2025-08-07T08:06:42.622736Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.1.6 giá trị duy nhất","metadata":{}},{"cell_type":"code","source":"train_df.nunique().to_frame(\"# of unique values\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:42.624800Z","iopub.execute_input":"2025-08-07T08:06:42.625131Z","iopub.status.idle":"2025-08-07T08:06:45.547408Z","shell.execute_reply.started":"2025-08-07T08:06:42.625107Z","shell.execute_reply":"2025-08-07T08:06:45.546610Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dem_df.nunique().to_frame(\"# of unique values\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:45.548423Z","iopub.execute_input":"2025-08-07T08:06:45.548772Z","iopub.status.idle":"2025-08-07T08:06:45.559171Z","shell.execute_reply.started":"2025-08-07T08:06:45.548733Z","shell.execute_reply":"2025-08-07T08:06:45.558301Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Tiền xử lý dữ liệu","metadata":{}},{"cell_type":"markdown","source":"### Gesture (Target Column)","metadata":{}},{"cell_type":"markdown","source":"### Phân Tích Biểu Đồ Phân Phối Cử Chỉ","metadata":{}},{"cell_type":"code","source":"gesture_counts = train_df['gesture'].value_counts()\n\n# Tính toán tỷ lệ phần trăm\npercentages = (gesture_counts / gesture_counts.sum()) * 100\n\n# Tạo nhãn bao gồm cả số lượng và phần trăm\nlabels = [f'{label} ({count}, {perc:.1f}%)' for label, count, perc in zip(gesture_counts.index, gesture_counts.values, percentages)]\n\n# Tạo biểu đồ tròn\nplt.figure(figsize=(8, 8)) # Set figure size for better readability\nplt.pie(gesture_counts, labels=labels, startangle=160, counterclock=False, wedgeprops={'edgecolor': 'black'})\nplt.title('Distribution of Gestures')\nplt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:45.560098Z","iopub.execute_input":"2025-08-07T08:06:45.560374Z","iopub.status.idle":"2025-08-07T08:06:46.020830Z","shell.execute_reply.started":"2025-08-07T08:06:45.560354Z","shell.execute_reply":"2025-08-07T08:06:46.019814Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Tổng Quan Phân Phối Cử Chỉ","metadata":{}},{"cell_type":"markdown","source":"Biểu đồ trên cho thấy sự phân bổ của các loại cử chỉ được ghi nhận trong bộ dữ liệu. Sau đây là một số điểm nổi bật:\n\n* Cử chỉ \"Text on phone\" chiếm tỷ lệ cao nhất, lên tới 10.2%. Điều này cho thấy người tham gia có xu hướng sử dụng điện thoại khá nhiều.\n\n* \"Neck - scratch\" và \"Eyebrow - pull hair\" là hai cử chỉ phổ biến khác, với tỷ lệ lần lượt là 9.8% và 7.7%.\n\n* Một số cử chỉ liên quan đến việc tương tác với cơ thể như \"Forehead - scratch\" hay \"Write name on leg\" cũng chiếm tỷ lệ đáng kể, lần lượt là 7.1% và 1.8%.","metadata":{}},{"cell_type":"markdown","source":"### Phân Tích Chi Tiết Các Cử Chỉ","metadata":{}},{"cell_type":"markdown","source":"* Cử chỉ \"Text on phone\" (10.2%): Là cử chỉ phổ biến nhất, cho thấy mối quan hệ mạnh mẽ giữa người tham gia và việc sử dụng thiết bị di động.\n\n* Cử chỉ \"Neck - scratch\" (9.8%): Phản ánh thói quen cào, gãi vùng cổ, điều này có thể gợi ý về các hành vi tần suất cao trong những người tham gia.\n\n* Cử chỉ \"Eyebrow - pull hair\" (7.7%): Cử chỉ này có thể liên quan đến những hành động liên quan đến sự căng thẳng hoặc lo lắng.","metadata":{}},{"cell_type":"markdown","source":"**Các Cử Chỉ Ít Phổ Biến**\nMột số cử chỉ như \"Pinch knee/leg skin\" (1.7%) hay \"Write name on leg\" (1.8%) có tỷ lệ thấp hơn, phản ánh những hành vi ít gặp hơn trong tập dữ liệu.","metadata":{}},{"cell_type":"markdown","source":"**Những Phát Hiện Quan Trọng**\n\nCác cử chỉ liên quan đến việc tương tác cơ thể và sử dụng thiết bị điện tử chiếm ưu thế, điều này giúp chúng ta hiểu hơn về hành vi người dùng trong bối cảnh cuộc thi.","metadata":{}},{"cell_type":"markdown","source":"### Phân tích Biểu đồ Tròn (Donut Chart) về Hướng Đối Tượng\n","metadata":{}},{"cell_type":"code","source":"# Biểu đồ tròn\nfig, ax = plt.subplots(figsize=(8, 8))\n\n# Tính phần trăm cho các nhãn (tùy chọn, cũng có thể sử dụng autopct)\norientation_counts = train_df[\"orientation\"].value_counts().head()\ntotal_count = orientation_counts.sum()\nlabels = [f'{label} ({count})' for label, count in orientation_counts.items()]\n\nwedges, texts, autotexts = ax.pie(\n    orientation_counts,\n    autopct='%1.1f%%',\n    startangle=90,\n    colors=plt.cm.Paired.colors,\n    pctdistance=0.85, # Distance of the percentage labels from the center\n    wedgeprops=dict(width=0.3, edgecolor='w'), # Create the donut hole\n    labels=labels\n)\n\n# Vẽ một vòng tròn ở trung tâm để biến nó thành biểu đồ donut\ncentre_circle = plt.Circle((0,0), 0.70, fc='white')\nfig.gca().add_artist(centre_circle)\n\nax.set_title(\"Subject's orientation (Donut Chart)\")\nax.axis('equal')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:46.022005Z","iopub.execute_input":"2025-08-07T08:06:46.022348Z","iopub.status.idle":"2025-08-07T08:06:46.232382Z","shell.execute_reply.started":"2025-08-07T08:06:46.022322Z","shell.execute_reply":"2025-08-07T08:06:46.231434Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### **Các Thành Phần Chính:**","metadata":{}},{"cell_type":"markdown","source":"Tư thế \"Seated Straight\" (Ngồi Thẳng):\n\n* Tỷ lệ: 32.7% (188,120)\n\n* Phân tích: Đây là tư thế chiếm ưu thế nhất trong dữ liệu, cho thấy rằng đa số đối tượng trong nghiên cứu đã được quan sát trong tư thế ngồi thẳng.","metadata":{}},{"cell_type":"markdown","source":"Tư thế \"Seated Lean Non Dom - FACE DOWN\" (Ngồi Nghiêng Không Phải Tay Thuận - Mặt Hướng Xuống):\n\n* Tỷ lệ: 22.9% (131,636)\n\n* Phân tích: Tư thế này đứng ở vị trí thứ hai với tỷ lệ khá cao, cho thấy đối tượng đôi khi có thể nghiêng và hướng mặt xuống.","metadata":{}},{"cell_type":"markdown","source":"Tư thế \"Lie on Back\" (Nằm Ngửa):\n\n* Tỷ lệ: 22.6% (130,043)\n\n* Phân tích: Tư thế nằm ngửa chiếm tỷ lệ gần như tương đương với \"Seated Lean Non Dom - FACE DOWN\", cho thấy một phần đáng kể đối tượng cũng nằm ngửa trong suốt quá trình thu thập dữ liệu.","metadata":{}},{"cell_type":"markdown","source":"Tư thế \"Lie on Side - Non Dominant\" (Nằm Nghiêng Bên Không Thuận):\n\n* Tỷ lệ: 21.8% (125,146)\n\n* Phân tích: Tư thế này có tỷ lệ thấp nhất nhưng vẫn khá cao so với các tư thế khác, cho thấy một phần đối tượng cũng có xu hướng nằm nghiêng về phía không thuận.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n\nTư thế ngồi thẳng chiếm ưu thế nhất, nhưng sự phân bố khá đồng đều của các tư thế khác phản ánh một sự đa dạng trong cách đối tượng tương tác trong môi trường thử nghiệm.","metadata":{}},{"cell_type":"markdown","source":"### Biểu đồ cột trên thể hiện số lượng các đối tượng khác nhau trong tập dữ liệu","metadata":{}},{"cell_type":"code","source":"# Biểu đồ cột\nfig, ax = plt.subplots(figsize=(8, 6))\norientation_counts.sort_values(ascending=False).plot( # Often good to sort descending for vertical bars\n    kind=\"bar\", color='purple', ax=ax, title=\"Target (Vertical Bar)\"\n)\nax.set_xlabel(\"Target\")\nax.set_ylabel(\"Count\")\nplt.xticks(rotation=20, ha='right') # Rotate x-axis labels for better readability if they overlap\nplt.tight_layout() # Adjust layout to prevent labels from overlapping\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:46.233231Z","iopub.execute_input":"2025-08-07T08:06:46.233472Z","iopub.status.idle":"2025-08-07T08:06:46.506759Z","shell.execute_reply.started":"2025-08-07T08:06:46.233453Z","shell.execute_reply":"2025-08-07T08:06:46.505879Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Trục hoành (X-axis):** Các nhóm đối tượng (Target), bao gồm:\n\n* Seated Straight: Được xếp hạng cao nhất trong biểu đồ.\n\n* Seated Lean Non Dom - FACE DOWN: Có số lượng thấp hơn so với nhóm Seated Straight, nhưng vẫn chiếm tỷ lệ lớn.\n\n* Lie on Back và Lie on Side - Non Dominant: Các nhóm này có số lượng ít hơn một chút, nhưng vẫn đáng kể so với tổng thể.","metadata":{}},{"cell_type":"markdown","source":"**Trục tung (Y-axis):** Thể hiện số lượng các bản ghi trong mỗi nhóm mục tiêu, với Seated Straight có số lượng lớn nhất, gần 180.000 bản ghi. Các nhóm còn lại có số lượng thấp hơn, khoảng từ 125.000 đến 150.000 bản ghi.","metadata":{}},{"cell_type":"markdown","source":"**Các điểm quan trọng:**\n\n* Mẫu dữ liệu không đồng đều: Biểu đồ cho thấy sự chênh lệch lớn trong số lượng bản ghi giữa các nhóm mục tiêu, với một nhóm (Seated Straight) có số lượng lớn hơn nhiều so với các nhóm còn lại.\n\n* Ý nghĩa của việc sử dụng biểu đồ cột: Biểu đồ này giúp dễ dàng nhận diện và so sánh sự phân bố của các nhóm mục tiêu trong tập dữ liệu, điều này có thể hỗ trợ trong việc phân tích dữ liệu và chọn lựa mô hình học máy phù hợp.","metadata":{}},{"cell_type":"markdown","source":"### Sequence Type (Target vs Non Target)","metadata":{}},{"cell_type":"markdown","source":"### Phân tích biểu đồ tròn \"Target vs Non-target\"","metadata":{}},{"cell_type":"code","source":"# Biểu đồ tròn\nfig, ax = plt.subplots(figsize=(8, 8))\n\n# Tính phần trăm cho các nhãn (không bắt buộc, cũng có thể dùng autopct)\nsequence_type_counts = train_df[\"sequence_type\"].value_counts().head()\ntotal_count = sequence_type_counts.sum()\nlabels = [f'{label} ({count})' for label, count in sequence_type_counts.items()]\n\nwedges, texts, autotexts = ax.pie(\n    sequence_type_counts,\n    autopct='%1.1f%%',\n    startangle=90,\n    colors=plt.cm.Paired.colors,\n    pctdistance=0.85, # Distance of the percentage labels from the center\n    wedgeprops=dict(width=0.3, edgecolor='w'), # Create the donut hole\n    labels=labels\n)\n\n# Vẽ hình tròn ở giữa để tạo biểu đồ vành khuyên\ncentre_circle = plt.Circle((0,0), 0.70, fc='white')\nfig.gca().add_artist(centre_circle)\n\nax.set_title(\"Target vs Non-target (Donut Chart)\")\nax.axis('equal')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:46.507963Z","iopub.execute_input":"2025-08-07T08:06:46.508233Z","iopub.status.idle":"2025-08-07T08:06:46.696462Z","shell.execute_reply.started":"2025-08-07T08:06:46.508213Z","shell.execute_reply":"2025-08-07T08:06:46.695420Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Target:**\n\n* Tỷ lệ: 59.8%\n\n* Số lượng: 344,058\n\n**Non-target:**\n\n* Tỷ lệ: 40.2%\n\n* Số lượng: 230,887","metadata":{}},{"cell_type":"markdown","source":"**Nhận xét**\n\nBiểu đồ cho thấy có sự chênh lệch khá lớn giữa hai nhóm. Nhóm Target chiếm ưu thế với 59.8%, trong khi nhóm Non-target chỉ chiếm 40.2%.","metadata":{}},{"cell_type":"markdown","source":"Sự mất cân bằng giữa hai lớp có thể gây ảnh hưởng đến độ chính xác và hiệu suất của mô hình. Có thể cần áp dụng các phương pháp xử lý mất cân bằng lớp như điều chỉnh trọng số lớp, oversampling (tăng cường lớp ít xuất hiện), hoặc undersampling (giảm bớt lớp nhiều xuất hiện).","metadata":{}},{"cell_type":"markdown","source":"### Phân tích Biểu đồ cột của hai nhóm dữ liệu: \"Target\" và \"Non-Target.\"","metadata":{}},{"cell_type":"code","source":"# Biểu đồ cột dọc\nfig, ax = plt.subplots(figsize=(8, 6))\nsequence_type_counts.sort_values(ascending=False).plot( # Often good to sort descending for vertical bars\n    kind=\"bar\", color='lightgreen', ax=ax, title=\"Target vs Non Target (Vertical Bar)\"\n)\nax.set_xlabel(\"Target\")\nax.set_ylabel(\"Count\")\nplt.xticks(rotation=20, ha='right') # Rotate x-axis labels for better readability if they overlap\nplt.tight_layout() # Adjust layout to prevent labels from overlapping\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:46.697548Z","iopub.execute_input":"2025-08-07T08:06:46.697908Z","iopub.status.idle":"2025-08-07T08:06:46.890440Z","shell.execute_reply.started":"2025-08-07T08:06:46.697878Z","shell.execute_reply":"2025-08-07T08:06:46.889553Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Trục X (Hoành Độ):**\n\nBao gồm hai nhãn: \"Target\" và \"Non-Target.\" Những nhãn này biểu thị hai nhóm dữ liệu trong bài toán.\n\n**Trục Y (Tung Độ):**\n\nĐại diện cho số lượng (Count) của từng nhóm dữ liệu. Số lượng trong nhóm \"Target\" cao hơn đáng kể so với nhóm \"Non-Target.\"","metadata":{}},{"cell_type":"markdown","source":"Sự mất cân bằng dữ liệu: Nếu \"Target\" có quá nhiều mẫu so với \"Non-Target,\" điều này có thể dẫn đến hiện tượng mô hình bị thiên lệch. Cần kiểm tra và áp dụng các phương pháp như tái cân bằng (resampling), trọng số lớp (class weighting) hoặc các kỹ thuật học máy phù hợp để cải thiện hiệu suất.","metadata":{}},{"cell_type":"markdown","source":"### Behavior","metadata":{}},{"cell_type":"markdown","source":"### Phân tích Biểu đồ Vành Khuyên: Hành Vi Của Đối Tượng Trong Quá Trình","metadata":{}},{"cell_type":"code","source":"# Biểu đồ tròn\nfig, ax = plt.subplots(figsize=(8, 8))\n\n# Vẽ hình tròn ở giữa để tạo biểu đồ vành khuyên\nbehavior_counts = train_df[\"behavior\"].value_counts().head()\ntotal_count = behavior_counts.sum()\nlabels = [f'{label} ({count})' for label, count in behavior_counts.items()]\n\nwedges, texts, autotexts = ax.pie(\n    behavior_counts,\n    autopct='%1.1f%%',\n    startangle=90,\n    colors=plt.cm.Paired.colors,\n    pctdistance=0.85, # Distance of the percentage labels from the center\n    wedgeprops=dict(width=0.3, edgecolor='w'), # Create the donut hole\n    labels=labels\n)\n\ncentre_circle = plt.Circle((0,0), 0.70, fc='white')\nfig.gca().add_artist(centre_circle)\n\nax.set_title(\"Subject's Behavior During the Sequence (Donut Chart)\")\nax.axis('equal')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:46.891308Z","iopub.execute_input":"2025-08-07T08:06:46.891620Z","iopub.status.idle":"2025-08-07T08:06:47.118230Z","shell.execute_reply.started":"2025-08-07T08:06:46.891599Z","shell.execute_reply":"2025-08-07T08:06:47.116759Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Thực hiện cử chỉ (44.5%)**\nĐây là hành vi phổ biến nhất, chiếm 44,5% tổng số hành vi. Điều này cho thấy đối tượng chủ yếu thực hiện các cử chỉ trong quá trình.\n\n**Di chuyển tay đến vị trí mục tiêu (27.2%)**\nHành vi này chiếm 27,2% và đứng thứ hai. Điều này có nghĩa là một phần lớn thời gian đối tượng dành cho việc di chuyển tay đến một vị trí mục tiêu cụ thể.\n\n**Tay ở vị trí mục tiêu (16.6%)**\nHành vi này chiếm 16,6%, tức là đối tượng giữ tay ở vị trí mục tiêu trong một khoảng thời gian đáng kể trong suốt quá trình.\n\n**Thư giãn và di chuyển tay đến vị trí mục tiêu (11.7%)**\nĐây là hành vi ít xuất hiện nhất, chiếm 11,7% tổng số hành vi. Hành vi này diễn ra khi đối tượng thư giãn và di chuyển tay đến vị trí mục tiêu.","metadata":{}},{"cell_type":"markdown","source":"Các hành vi phổ biến nhất là \"Thực hiện cử chỉ\" và \"Di chuyển tay đến vị trí mục tiêu\", trong khi \"Thư giãn và di chuyển tay đến vị trí mục tiêu\" là ít xảy ra nhất. Biểu đồ này cung cấp một cái nhìn rõ ràng về các hành vi chủ yếu của đối tượng trong suốt quá trình.","metadata":{}},{"cell_type":"markdown","source":"### Biểu đồ cột trên minh họa hành vi của các đối tượng trong một chuỗi","metadata":{}},{"cell_type":"code","source":"# Biểu đồ cột dọc\nfig, ax = plt.subplots(figsize=(8, 6))\nbehavior_counts.sort_values(ascending=False).plot( # Often good to sort descending for vertical bars\n    kind=\"bar\", color='skyblue', ax=ax, title=\"Subject's Behavior During the Sequence (Vertical Bar)\"\n)\nax.set_xlabel(\"Behavior\")\nax.set_ylabel(\"Count\")\nplt.xticks(rotation=20, ha='right') # Rotate x-axis labels for better readability if they overlap\nplt.tight_layout() # Adjust layout to prevent labels from overlapping\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:47.119283Z","iopub.execute_input":"2025-08-07T08:06:47.119895Z","iopub.status.idle":"2025-08-07T08:06:47.325963Z","shell.execute_reply.started":"2025-08-07T08:06:47.119731Z","shell.execute_reply":"2025-08-07T08:06:47.324916Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Trục hoành (Loại hành vi):**\nBiểu đồ hiển thị bốn loại hành vi khác nhau được quan sát trong chuỗi:\n\n* Thực hiện cử chỉ\n\n* Di chuyển tay đến vị trí mục tiêu\n\n* Tay ở vị trí mục tiêu\n\n* Thư giãn và di chuyển tay đến vị trí mục tiêu\n\n**Trục tung (Số lượng):**\nSố lượng thể hiện tần suất xảy ra của mỗi hành vi trong suốt quá trình quan sát, với hành vi \"Thực hiện cử chỉ\" xuất hiện nhiều nhất, tiếp theo là \"Di chuyển tay đến vị trí mục tiêu\", \"Tay ở vị trí mục tiêu\", và cuối cùng là \"Thư giãn và di chuyển tay đến vị trí mục tiêu\".","metadata":{}},{"cell_type":"markdown","source":"#### **Quan sát chính:**","metadata":{}},{"cell_type":"markdown","source":"* Hành vi \"Thực hiện cử chỉ\" là phổ biến nhất, với số lượng vượt trội so với các hành vi còn lại. Điều này cho thấy các đối tượng trong bộ dữ liệu này thực hiện cử chỉ thường xuyên hơn so với các hành vi khác.\n\n* \"Di chuyển tay đến vị trí mục tiêu\" đứng thứ hai, với tần suất xuất hiện vừa phải. Điều này cho thấy các đối tượng thường xuyên di chuyển tay đến một vị trí cụ thể trong chuỗi hành động.\n\n* \"Tay ở vị trí mục tiêu\" và \"Thư giãn và di chuyển tay đến vị trí mục tiêu\" là những hành vi ít xuất hiện nhất, với số lượng thấp hơn nhiều so với các hành vi khác.","metadata":{}},{"cell_type":"markdown","source":"Biểu đồ cho thấy tần suất của từng hành vi, với hành vi \"Thực hiện cử chỉ\" là hành vi phổ biến nhất, trong khi các hành vi khác ít xuất hiện hơn. Dữ liệu này rất hữu ích trong việc phân tích các mẫu hành vi và động tác của đối tượng.","metadata":{}},{"cell_type":"markdown","source":"### Ma trận tương quan của dữ liệu nhân khẩu học","metadata":{}},{"cell_type":"markdown","source":"Biểu đồ này hiển thị ma trận tương quan giữa các đặc trưng nhân khẩu học trong bộ dữ liệu huấn luyện. Mỗi ô trong ma trận biểu thị mối quan hệ giữa hai đặc trưng, với giá trị tương quan từ -1 đến 1. Một giá trị gần 1 chỉ ra sự tương quan dương mạnh, trong khi giá trị gần -1 chỉ ra sự tương quan âm mạnh. Giá trị 0 biểu thị không có sự tương quan.","metadata":{}},{"cell_type":"code","source":"# Ma trận tương quan\nplt.figure(figsize = (8, 4))\nsns.heatmap(data = train_dem_df.corr(numeric_only = True), linewidth = 0.5, annot = True, linecolor = \"black\")\nplt.title('Train Data Demographic Correlation Heatmap')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:47.327173Z","iopub.execute_input":"2025-08-07T08:06:47.327512Z","iopub.status.idle":"2025-08-07T08:06:47.676992Z","shell.execute_reply.started":"2025-08-07T08:06:47.327486Z","shell.execute_reply":"2025-08-07T08:06:47.675907Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##### **Các Đặc Trưng**","metadata":{}},{"cell_type":"markdown","source":"**adult_child (Trưởng thành - Trẻ em):**\n\n* age (Tuổi): Tương quan dương mạnh (0.83), cho thấy rằng tuổi trưởng thành và nhóm trẻ em có sự liên quan chặt chẽ.\n\n* height_cm (Chiều cao): Tương quan dương trung bình (0.45), cho thấy mối liên hệ giữa tuổi và chiều cao.","metadata":{}},{"cell_type":"markdown","source":"**age (Tuổi):**\n\n* adult_child (Trưởng thành - Trẻ em): Tương quan dương mạnh (0.83), chứng tỏ tuổi có mối liên hệ mật thiết với việc phân loại người trưởng thành và trẻ em.\n\n* sex (Giới tính): Tương quan nhẹ dương (0.26), cho thấy tuổi và giới tính có liên quan nhưng không mạnh.","metadata":{}},{"cell_type":"markdown","source":"**sex (Giới tính):**\n\nhandedness (Tay thuận): Tương quan dương nhẹ (0.09), cho thấy giới tính có một sự liên kết nhỏ với việc tay thuận.","metadata":{}},{"cell_type":"markdown","source":"**handedness (Tay thuận):**\n\nheight_cm (Chiều cao): Tương quan âm nhẹ (-0.047), chỉ ra rằng không có mối liên hệ mạnh giữa tay thuận và chiều cao.","metadata":{}},{"cell_type":"markdown","source":"**height_cm (Chiều cao):**\n\nshoulder_to_wrist_cm (Khoảng cách từ vai đến cổ tay): Tương quan dương mạnh (0.73), cho thấy chiều cao và khoảng cách từ vai đến cổ tay có sự tương quan mạnh mẽ.","metadata":{}},{"cell_type":"markdown","source":"**shoulder_to_wrist_cm (Khoảng cách từ vai đến cổ tay):**\n\nelbow_to_wrist_cm (Khoảng cách từ khuỷu tay đến cổ tay): Tương quan dương mạnh (0.67), cho thấy sự tương quan mạnh giữa khoảng cách từ vai đến cổ tay và khoảng cách từ khuỷu tay đến cổ tay.","metadata":{}},{"cell_type":"markdown","source":"#### **Kết luận**","metadata":{}},{"cell_type":"markdown","source":"* Mối liên hệ giữa \"Tuổi\" và \"Trưởng thành/Trẻ em\": Tuổi có một mối liên hệ rất mạnh với việc phân loại Trưởng thành/Trẻ em. Điều này là dễ hiểu, vì tuổi càng lớn thì khả năng là người trưởng thành càng cao, và ngược lại. Mối tương quan này lên đến 0.83, cho thấy sự liên quan rất chặt chẽ.\n\n* Mối quan hệ giữa \"Chiều cao\" và \"Tuổi\": Chiều cao có mối tương quan dương vừa phải với tuổi (0.45). Điều này cho thấy rằng khi tuổi càng lớn, chiều cao có xu hướng cao hơn, nhưng mức độ không quá mạnh.\n\n* Mối liên hệ giữa \"Chiều cao\" và \"Khoảng cách từ vai đến cổ tay\": Một mối liên hệ mạnh (0.73) giữa chiều cao và khoảng cách từ vai đến cổ tay. Đây là mối quan hệ dễ hiểu, vì những người có chiều cao lớn thường có các khoảng cách giữa các bộ phận cơ thể lớn hơn.\n\n* Mối quan hệ giữa \"Khoảng cách từ vai đến cổ tay\" và \"Khoảng cách từ khuỷu tay đến cổ tay\": Mối tương quan giữa hai chỉ số này là mạnh mẽ (0.67), điều này cho thấy người có khoảng cách vai đến cổ tay dài thường có khoảng cách từ khuỷu tay đến cổ tay dài hơn.\n\n* Giới tính và Tay thuận: Giới tính và tay thuận có sự liên kết rất yếu với các đặc trưng khác trong bộ dữ liệu. Mối tương quan giữa giới tính và tay thuận là rất thấp (0.09), cho thấy giới tính không có ảnh hưởng rõ rệt đến việc người đó thuận tay nào.","metadata":{}},{"cell_type":"markdown","source":"### Tỷ lệ giới tính","metadata":{}},{"cell_type":"code","source":"# Lấy số lượng từng giá trị trong cột giới tính\nvc = train_dem_df['sex'].value_counts()\n\n# Trích xuất số lượng (giả sử cột 'count' chưa tồn tại)\ncounts = vc.values\n\n# Tạo nhãn (giả sử cột 'count' chưa tồn tại)\nlabels = ['Male', 'Female']  # Assuming 'Male' maps to '1' and 'Female' to '0'\n\n# Vẽ biểu đồ hình tròn\nplt.pie(counts, labels=labels, autopct=\"%1.1f%%\")  # Add percentages to pie slices\nplt.title('Gender')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:47.678065Z","iopub.execute_input":"2025-08-07T08:06:47.678350Z","iopub.status.idle":"2025-08-07T08:06:47.768374Z","shell.execute_reply.started":"2025-08-07T08:06:47.678329Z","shell.execute_reply":"2025-08-07T08:06:47.767354Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Biểu đồ hình tròn minh họa phân bố giới tính trong bộ dữ liệu. Biểu đồ cho thấy:\n\n* 61,7% người tham gia là Nam.\n\n* 38,3% người tham gia là Nữ.\n\nĐiều này cho thấy phần lớn bộ dữ liệu bao gồm các người tham gia nam, với một tỷ lệ nhỏ hơn là nữ.","metadata":{}},{"cell_type":"markdown","source":"### Phân bố độ tuổi theo giới tính","metadata":{}},{"cell_type":"code","source":"#train' là một DataFrame (khung dữ liệu)\n_, axs = plt.subplots(2, 1, sharex=True)\n\nfor sex in range(2):\n  ax = axs.ravel()[sex]\n\n  # Lọc theo giới tính bằng cách sử dụng chỉ mục boolean\n  sex_filter = train_dem_df['sex'] == sex\n  vc = train_dem_df[sex_filter]['age'].value_counts()  # Access column after filtering\n\n  # Vẽ biểu đồ cột\n  ax.bar(vc.index, vc.values, color=['lightblue', 'coral'][sex], label=['Male', 'Female'][sex])\n  ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n  ax.set_ylabel('count')\n  ax.legend()\n\nplt.suptitle('Age distribution with respect to Gender')\naxs.ravel()[1].set_xlabel('years')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:47.769489Z","iopub.execute_input":"2025-08-07T08:06:47.769835Z","iopub.status.idle":"2025-08-07T08:06:48.161609Z","shell.execute_reply.started":"2025-08-07T08:06:47.769805Z","shell.execute_reply":"2025-08-07T08:06:48.160392Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân bố tuổi của Nam:**\n\n* Biểu đồ trên thể hiện phân bố độ tuổi của nhóm Nam. Nhóm này có số lượng người trong độ tuổi từ 10 đến 30 là nhiều nhất, với các đỉnh ở độ tuổi 10, 15, 20 và 25.\n\n* Các độ tuổi khác có ít người hơn, với tần suất thấp hơn ở những nhóm tuổi lớn hơn.\n\n* Sự phân bố này cho thấy rằng nhóm Nam có sự tập trung chủ yếu ở độ tuổi thanh thiếu niên và đầu độ tuổi trưởng thành (từ 15 đến 25).","metadata":{}},{"cell_type":"markdown","source":"**Phân bố tuổi của Nữ:**\n\n* Biểu đồ dưới thể hiện phân bố độ tuổi của nhóm Nữ. Tương tự như nhóm Nam, nhóm Nữ cũng có sự tập trung lớn ở độ tuổi từ 20 đến 30, đặc biệt là ở độ tuổi 20-25.\n\n* Tuy nhiên, so với nhóm Nam, nhóm Nữ có sự phân bố tuổi đa dạng hơn và sự phân bố tần suất ở các độ tuổi khác ngoài 20-25 cũng có sự phân bố cao hơn.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**","metadata":{}},{"cell_type":"markdown","source":"* Độ tuổi phổ biến nhất: Độ tuổi 25 có số lượng người tham gia nhiều nhất, đặc biệt là đối với nhóm Nữ.\n\n* Sự tập trung theo độ tuổi: Các độ tuổi trẻ từ 10 đến 25 chiếm tỷ lệ lớn trong cả hai nhóm giới tính.\n\n* Chênh lệch giữa Nam và Nữ: Nhóm Nữ có sự phân bố đa dạng hơn, với độ tuổi tham gia phân tán rộng hơn so với nhóm Nam.","metadata":{}},{"cell_type":"markdown","source":"### Gia tốc tuyến tính từ cảm biến IMU","metadata":{}},{"cell_type":"code","source":"# Khởi tạo biểu đồ 3D\nfig = plt.figure(figsize=(10, 10)) # Adjust figure size for better visibility\nax = fig.add_subplot(111, projection='3d')\n\n# Lấy các giá trị duy nhất từ cột 'gesture' để tô màu\nunique_gestures = train_df['gesture'].unique()\n\n# Sử dụng bản đồ màu để có được màu sắc riêng biệt cho từng loại cử chỉ\n# matplotlib.colormaps là cách được khuyến nghị để truy cập bản đồ màu hiện nay\ncolors = plt.colormaps['tab10'].resampled(len(unique_gestures)) # 'tab10' is good for categorical data\n\n# Lặp lại các cử chỉ độc đáo và vẽ chúng bằng các màu khác nhau\nfor i, gesture_type in enumerate(unique_gestures):\n    subset = train_df[train_df['gesture'] == gesture_type]\n    ax.scatter(subset['acc_x'], subset['acc_y'], subset['acc_z'],\n               color=colors(i),    # Chỉ định một màu duy nhất từ bản đồ màu\n               label=gesture_type, # Nhãn cho huyền thoại\n               alpha=0.7,          # Độ mờ đục (phù hợp với độ mờ đục Plotly của bạn)\n               s=2,\n               rasterized=True)                # Kích thước của các điểm (tương tự như size_max=1 của Plotly, được điều chỉnh để dễ nhìn)\n\n# Đặt nhãn và tiêu đề\nax.set_xlabel('acc_x', fontsize=14)\nax.set_ylabel('acc_y', fontsize=14)\nax.set_zlabel('acc_z', fontsize=14)\nax.set_title('Linear acceleration along X, Y, Z axes', fontsize=16)\n\n# Thêm chú thích để phân biệt các loại 'cử chỉ'\nax.legend(title='Gesture Type', fontsize=10, title_fontsize=12, loc='upper right')\n\n# Đặt kích thước phông chữ nhãn đánh dấu\nax.tick_params(axis='both', which='major', labelsize=12)\n\n# --- Mô phỏng mẫu 'plotly_dark' của Plotly ---\n# Đặt màu nền của ô\nax.set_facecolor('black')\n\n# Làm cho nền của khung trong suốt\nax.xaxis.pane.fill = False\nax.yaxis.pane.fill = False\nax.zaxis.pane.fill = False\n\n# Đặt màu cạnh khung\nax.xaxis.pane.edgecolor = 'grey'\nax.yaxis.pane.edgecolor = 'grey'\nax.zaxis.pane.edgecolor = 'grey'\n\n# Đặt màu mặt khung thực tế thành trong suốt\nax.xaxis.pane.set_facecolor((0.0, 0.0, 0.0, 0.0))\nax.yaxis.pane.set_facecolor((0.0, 0.0, 0.0, 0.0))\nax.zaxis.pane.set_facecolor((0.0, 0.0, 0.0, 0.0))\n\n# Đặt màu đường lưới\nax.grid(color='gray', linestyle='--', linewidth=0.5, alpha=0.5)\n\nplt.tight_layout() # Adjust layout to prevent labels from overlapping\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:48.162813Z","iopub.execute_input":"2025-08-07T08:06:48.163733Z","iopub.status.idle":"2025-08-07T08:06:58.144888Z","shell.execute_reply.started":"2025-08-07T08:06:48.163666Z","shell.execute_reply":"2025-08-07T08:06:58.143728Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Biểu đồ phân tán 3D hiển thị gia tốc tuyến tính theo trục x, y và z. Màu sắc đóng vai trò là chiều thứ tư và phân biệt các điểm khác nhau dựa trên cột mục tiêu, tức là cử chỉ.","metadata":{}},{"cell_type":"markdown","source":"Mỗi điểm trong biểu đồ đại diện cho một gia tốc đo được từ cảm biến, có thể là kết quả của một cử chỉ người dùng. Các điểm dữ liệu được phân loại và màu sắc khác nhau đại diện cho các loại cử chỉ khác nhau.","metadata":{}},{"cell_type":"markdown","source":"Một số cử chỉ như \"Write name in air\" và \"Glasses on/off\" có thể có gia tốc thay đổi mạnh mẽ, vì vậy chúng sẽ rải rác nhiều hơn trong không gian ba chiều. Trong khi đó, các cử chỉ như \"Text on phone\" và \"Wave hello\" có thể ít thay đổi về gia tốc, vì vậy các điểm của chúng tập trung nhiều hơn.","metadata":{}},{"cell_type":"code","source":"# Khởi tạo biểu đồ 3D\nfig = plt.figure(figsize=(10, 10)) # Điều chỉnh kích thước hình để dễ nhìn hơn\nax = fig.add_subplot(111, projection='3d')\n\n# Lấy các giá trị duy nhất từ 'sequence_type' để tô màu\nunique_sequence_types = train_df['sequence_type'].unique()\n\n# --- XỬ LÝ CẢNH BÁO VỀ VIỆC KHÔNG SỬ DỤNG NỮA 1: get_cmap ---\n# Sử dụng matplotlib.colormaps[name] thay vì plt.cm.get_cmap(name)\ncolors = plt.colormaps['viridis'].resampled(len(unique_sequence_types))\n\n# Lặp lại qua các loại trình tự duy nhất và biểu diễn chúng bằng các màu khác nhau\nfor i, seq_type in enumerate(unique_sequence_types):\n    subset = train_df[train_df['sequence_type'] == seq_type]\n    ax.scatter(subset['acc_x'], subset['acc_y'], subset['acc_z'],\n               color=colors(i), # Gán một màu duy nhất\n               label=seq_type,  # Nhãn cho chú giải\n               alpha=0.7,       # Độ mờ đục\n               s=10)            # Kích thước của các điểm (tương tự như size_max của Plotly)\n\n# Đặt nhãn và tiêu đề\nax.set_xlabel('acc_x', fontsize=14)\nax.set_ylabel('acc_y', fontsize=14)\nax.set_zlabel('acc_z', fontsize=14)\nax.set_title('Linear acceleration along X, Y, Z axes', fontsize=16)\n\n# Thêm chú thích để phân biệt 'sequence_type'\nax.legend(title='Sequence Type', fontsize=12, title_fontsize=14)\n\n# Đặt kích thước phông chữ nhãn đánh dấu\nax.tick_params(axis='both', which='major', labelsize=12)\n\n# Đặt màu nền tối và điều chỉnh màu lưới/khung\nax.set_facecolor('black')\n\n# --- XỬ LÝ CẢNH BÁO SỬA ĐỔI 2, 3, 4: w_xaxis, w_yaxis, w_zaxis ---\n# Sử dụng .xaxis, .yaxis, .zaxis trực tiếp cho các ngăn\nax.xaxis.pane.fill = False\nax.yaxis.pane.fill = False\nax.zaxis.pane.fill = False\n\nax.xaxis.pane.edgecolor = 'grey'\nax.yaxis.pane.edgecolor = 'grey'\nax.zaxis.pane.edgecolor = 'grey'\n\n# Đặt màu khung thành trong suốt để có hiệu ứng tối thực sự\n# Giờ đây, bạn có thể truy cập trực tiếp thông qua .xaxis.pane.set_facecolor\nax.xaxis.pane.set_facecolor((0.0, 0.0, 0.0, 0.0))\nax.yaxis.pane.set_facecolor((0.0, 0.0, 0.0, 0.0))\nax.zaxis.pane.set_facecolor((0.0, 0.0, 0.0, 0.0))\n\nax.grid(color='gray', linestyle='--', linewidth=0.5, alpha=0.5)\n\nplt.tight_layout() # Adjust layout to prevent labels from overlapping\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:06:58.146170Z","iopub.execute_input":"2025-08-07T08:06:58.146529Z","iopub.status.idle":"2025-08-07T08:07:11.487508Z","shell.execute_reply.started":"2025-08-07T08:06:58.146490Z","shell.execute_reply":"2025-08-07T08:07:11.486520Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Biểu đồ phân tán 3D thể hiện gia tốc tuyến tính theo trục x, y và z. Màu sắc đóng vai trò là chiều thứ tư và phân biệt các điểm dựa trên cột loại chuỗi i.s mục tiêu và không phải mục tiêu.","metadata":{}},{"cell_type":"markdown","source":"* Các điểm dữ liệu chủ yếu tập trung ở khu vực trung tâm của không gian 3D, cho thấy phần lớn dữ liệu thuộc về một phạm vi gia tốc nhất định.\n\n* Các điểm màu tím (Target) có xu hướng phân bố rộng hơn, trong khi điểm màu vàng (Non-Target) có mật độ dày đặc hơn ở trung tâm.","metadata":{}},{"cell_type":"markdown","source":"### Sự quay của con quay hồi chuyển từ cảm biến IMU","metadata":{}},{"cell_type":"code","source":"fig = plt.figure(figsize=(14, 8)) # Adjust figure size as needed\nax = fig.add_subplot(111, projection='3d')\n\n# Vẽ đường 3D\n# Về màu sắc, plot3D của matplotlib không hỗ trợ trực tiếp đối số 'color' liên tục\n# như px.line_3d. Thông thường, bạn sẽ sử dụng một bản đồ màu và ánh xạ 'rot_w' vào đó.\n# Ví dụ này sử dụng một màu đường đơn giản. Đối với màu liên tục, bạn cần\n# phân đoạn dữ liệu hoặc sử dụng scatter3D với c=train['rot_w'].\n# ax.plot(train['rot_x'], train['rot_y'], train['rot_z'], color='skyblue', linewidth=0.5)\n\n# Nếu bạn muốn tô màu theo 'rot_w' với gradient, thường tốt hơn nên sử dụng scatter3D\n# nếu không nhất thiết phải dùng đường liên tục, hoặc phân đoạn đường.\n# Đối với biểu đồ đường thẳng thực sự với màu liên tục, việc này phức tạp hơn trong matplotlib.\n# Ví dụ về scatter3D với màu:\nsc = ax.scatter(train_df['rot_x'], train_df['rot_y'], train_df['rot_z'], c=train_df['rot_w'], cmap='viridis', s=1)\nfig.colorbar(sc, ax=ax, label='rot_w')\n\n\nax.set_xlabel('rot_x')\nax.set_ylabel('rot_y')\nax.set_zlabel('rot_z')\nax.set_title('3D Path of Gyroscope Rotation Over Time')\n\n# Điều chỉnh kích thước phông chữ nếu cần (tương tự như update_layout trong Plotly)\nax.tick_params(axis='both', which='major', labelsize=12)\nax.xaxis.label.set_size(14)\nax.yaxis.label.set_size(14)\nax.zaxis.label.set_size(14)\nax.title.set_size(16)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:11.488667Z","iopub.execute_input":"2025-08-07T08:07:11.489009Z","iopub.status.idle":"2025-08-07T08:07:19.894212Z","shell.execute_reply.started":"2025-08-07T08:07:11.488982Z","shell.execute_reply":"2025-08-07T08:07:19.893147Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"* Biểu đồ có vẻ mô tả sự phân bố hình cầu của dữ liệu quay theo thời gian, với mỗi điểm đại diện cho hướng của vật thể tại một thời điểm cụ thể.\n\n* Thang màu thể hiện độ mạnh hoặc độ lớn của thành phần rot w, dao động từ 0 đến 1, với các màu chuyển từ tím (giá trị thấp) sang vàng/xanh lá (giá trị cao).","metadata":{}},{"cell_type":"markdown","source":"Sự phân tán các điểm trên hình cầu gợi ý rằng có sự thay đổi lớn trong hướng quay, có thể là sự chuyển động phức tạp hoặc thay đổi liên tục trong không gian 3D.\n\nThang màu cho thấy các khu vực có quay mạnh mẽ hơn (giá trị rot w cao), có thể chỉ ra các thay đổi nhanh chóng hoặc quan trọng trong hướng của vật thể ở một số thời điểm nhất định.\n\nTính đồng đều của các điểm dữ liệu cho thấy dữ liệu bao quát một phạm vi quay rộng trong thời gian, trải đều khắp các phần của hình cầu.","metadata":{}},{"cell_type":"markdown","source":"### Nhiệt độ từ nhiệt điện của cảm biến IMU","metadata":{}},{"cell_type":"code","source":"# Làm tan chảy DataFrame để vẽ đồ thị dễ dàng hơn\ndf_melted = train_df.melt(id_vars='sequence_type', \n                    value_vars=['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5'],\n                    var_name='sensor', \n                    value_name='temperature')\n\n# Plot\nplt.figure(figsize=(10, 6))\nsns.boxplot(x='sequence_type', y='temperature', hue='sensor', data=df_melted)\nplt.title('Thermopile Sensor Readings by Sequence Type')\nplt.xlabel('Sequence Type')\nplt.ylabel('Temperature (°C)')\nplt.legend(title='Sensor')\nplt.xticks(rotation=45)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:19.895199Z","iopub.execute_input":"2025-08-07T08:07:19.895549Z","iopub.status.idle":"2025-08-07T08:07:23.498779Z","shell.execute_reply.started":"2025-08-07T08:07:19.895516Z","shell.execute_reply":"2025-08-07T08:07:23.497820Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"* Tất cả các cảm biến đều có phân phối nhiệt độ tương tự cho cả hai loại chuỗi, nhưng có những sự khác biệt nhẹ giữa chúng. Ví dụ, thm_2 (màu cam) có xu hướng có chỉ số nhiệt độ thấp hơn một chút trong chuỗi \"Non-Target\" so với các cảm biến khác.\n\n* Có một số ngoại lệ rõ rệt, đặc biệt là ở thm_1 (màu xanh dương) và thm_5 (màu tím), điều này cho thấy một số chỉ số nhiệt độ có thể bất thường hoặc nhiễu trong một số điều kiện nhất định.\n\n* Loại chuỗi \"Target\" nhìn chung có phạm vi nhiệt độ rộng hơn, đặc biệt với một số giá trị cao hơn, trong khi loại chuỗi \"Non-Target\" có xu hướng có chỉ số nhiệt độ ổn định và thấp hơn.","metadata":{}},{"cell_type":"code","source":"# Nhóm theo sequence_type và lấy giá trị trung bình\nimport numpy as np\ndf_grouped = train_df.groupby('sequence_type')[['thm_1','thm_2','thm_3','thm_4','thm_5']].mean()\n\ncategories = ['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5']\nN = len(categories)\n\nangles = [n / float(N) * 2 * np.pi for n in range(N)]\nangles += angles[:1]  # Close the loop\n\nfig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(polar=True))\n\nfor idx, row in df_grouped.iterrows():\n    values = row.values.tolist()\n    values += values[:1]  # Close the loop\n    ax.plot(angles, values, marker='o', label=idx)\n    ax.fill(angles, values, alpha=0.1)\n\nax.set_xticks(angles[:-1])\nax.set_xticklabels(categories)\nplt.legend(title=\"Sequence Type\")\nplt.title(\"Radar Chart of Average Thermopile Readings by Sequence Type\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:23.499821Z","iopub.execute_input":"2025-08-07T08:07:23.500165Z","iopub.status.idle":"2025-08-07T08:07:23.876266Z","shell.execute_reply.started":"2025-08-07T08:07:23.500139Z","shell.execute_reply":"2025-08-07T08:07:23.875182Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Bằng cách phân tích biểu đồ hộp và biểu đồ radar ở trên, chúng ta có thể thấy nhiệt độ tại điểm mục tiêu cao hơn một chút so với điểm không phải mục tiêu. Tiếp theo, hãy xem biểu đồ hộp thể hiện giá trị nhiệt điện so với cử động cột và hành vi cột.","metadata":{}},{"cell_type":"markdown","source":"* Trục X (Các cảm biến thm): Các trục là các cảm biến nhiệt độ thm_1 đến thm_5, biểu thị các loại cảm biến khác nhau được sử dụng trong thí nghiệm.\n\n* Trục Y (Giá trị nhiệt độ): Trục này thể hiện giá trị nhiệt độ đo được từ các cảm biến. Giá trị nhiệt độ này được đo và trung bình theo các loại chuỗi \"Non-Target\" và \"Target\".","metadata":{}},{"cell_type":"markdown","source":"* Đường cong \"Non-Target\" (màu xanh dương): Đường cong này đại diện cho chuỗi không có mục tiêu, và có xu hướng có các giá trị nhiệt độ thấp hơn ở các cảm biến thm_1 và thm_3 so với các cảm biến khác.\n\n* Đường cong \"Target\" (màu cam): Đường cong này đại diện cho chuỗi mục tiêu, có xu hướng có giá trị nhiệt độ cao hơn ở tất cả các cảm biến, đặc biệt là ở các cảm biến thm_1 và thm_5.","metadata":{}},{"cell_type":"markdown","source":"* Sự khác biệt giữa hai nhóm: Biểu đồ cho thấy rằng \"Target\" có giá trị nhiệt độ cao hơn \"Non-Target\" ở hầu hết các cảm biến. Điều này có thể chỉ ra một sự khác biệt trong đặc điểm của các chuỗi có mục tiêu so với các chuỗi không có mục tiêu.\n\n* Sự phân bố dữ liệu: Biểu đồ cho thấy sự phân bố khá đều giữa các cảm biến trong mỗi nhóm, với sự khác biệt rõ rệt giữa hai loại chuỗi.","metadata":{}},{"cell_type":"code","source":"# Làm tan chảy DataFrame để vẽ đồ thị dễ dàng hơn\ndf_melted = train_df.melt(id_vars='gesture', \n                    value_vars=['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5'],\n                    var_name='sensor', \n                    value_name='temperature')\n\n# Plot\nplt.figure(figsize=(10, 6))\nsns.boxplot(x='gesture', y='temperature', hue='sensor', data=df_melted)\nplt.title('Thermopile Sensor Readings by Gesture')\nplt.xlabel('Gesture')\nplt.ylabel('Temperature (°C)')\nplt.legend(title='Sensor')\nplt.xticks(rotation=90)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:23.877343Z","iopub.execute_input":"2025-08-07T08:07:23.877595Z","iopub.status.idle":"2025-08-07T08:07:29.868670Z","shell.execute_reply.started":"2025-08-07T08:07:23.877575Z","shell.execute_reply":"2025-08-07T08:07:29.867537Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"* Trục X: Đại diện cho 20 loại cử chỉ khác nhau, ví dụ như: \"Cheek - pinch skin\", \"Forehead - pull hairline\", \"Wave hello\", v.v.\n\n* Trục Y: Đại diện cho nhiệt độ (°C) đo được từ các cảm biến.\n\n* Mỗi màu tương ứng với một cảm biến (thm_1 → thm_5).\n\n* Mỗi boxplot thể hiện:\n\n  - Giá trị trung vị (đường kẻ giữa hộp).\n  - Phạm vi giữa Q1 và Q3 (25% đến 75%).\n  - Whiskers: Dải mở rộng các giá trị không phải ngoại lệ.\n  - Điểm rời rạc (dot): Các giá trị ngoại lệ (outliers).","metadata":{}},{"cell_type":"markdown","source":"**Dải nhiệt độ phổ biến:**\n\n* Nhiệt độ cảm biến chủ yếu dao động trong khoảng 30°C đến 36°C.\n\n* Một số cử chỉ có xuất hiện giá trị nhiệt độ thấp hơn hẳn (dưới 20°C) như “Text on phone”, “Drink from bottle/cup”, hoặc “Glasses on/off” → Có thể do các cử chỉ này không gần cảm biến nhiệt.","metadata":{}},{"cell_type":"markdown","source":"**Gesture dễ phân biệt:**\n\n* Các cử chỉ như “Forehead - pull hairline”, “Above ear - pull hair” hoặc “Neck - pinch skin” có phân bố nhiệt độ khá rõ ràng, gần mặt (nhiệt cao) nên có thể giúp phân biệt dễ hơn.\n\n* Trong khi đó, những gesture như “Write name in air” hay “Wave hello” có phân bố nhiệt độ rộng và thấp hơn, có thể gây nhiễu nhiều hơn.","metadata":{}},{"cell_type":"markdown","source":"Kết luận:\n- Biểu đồ cho thấy các cử chỉ có thể được phân biệt một phần dựa trên đặc trưng nhiệt độ đo bởi cảm biến Thermopile.\n\n- Một số cử chỉ gần cơ thể (đặc biệt vùng mặt và cổ) có nhiệt độ cao và ổn định hơn → Tín hiệu mạnh và rõ ràng hơn cho mô hình nhận diện.\n\n- Ngược lại, các cử chỉ cách xa cơ thể có thể tạo ra dữ liệu nhiệt khó phân biệt hơn, cần kết hợp thêm các loại cảm biến khác (IMU, ToF, v.v.) để tăng độ chính xác.","metadata":{}},{"cell_type":"code","source":"# Làm tan chảy DataFrame để vẽ đồ thị dễ dàng hơn\ndf_melted = train_df.melt(id_vars='behavior', \n                    value_vars=['thm_1', 'thm_2', 'thm_3', 'thm_4', 'thm_5'],\n                    var_name='sensor', \n                    value_name='temperature')\n\n# Plot\nplt.figure(figsize=(10, 6))\nsns.boxplot(x='behavior', y='temperature', hue='sensor', data=df_melted)\nplt.title('Thermopile Sensor Readings by Behavior')\nplt.xlabel('Behavior')\nplt.ylabel('Temperature (°C)')\nplt.legend(title='Sensor')\nplt.xticks(rotation=25)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:29.869803Z","iopub.execute_input":"2025-08-07T08:07:29.870139Z","iopub.status.idle":"2025-08-07T08:07:33.902191Z","shell.execute_reply.started":"2025-08-07T08:07:29.870115Z","shell.execute_reply":"2025-08-07T08:07:33.901273Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Tổng thể**\n- Nhiệt độ đo được dao động chủ yếu từ khoảng 24°C đến 34°C.\n\n- Mỗi nhóm hành vi đều có sự phân bố nhiệt độ khá ổn định giữa các cảm biến.\n\n- Có nhiều outlier xuất hiện, đặc biệt ở phía trên (~35–40°C) và một vài điểm thấp hơn ~15°C.","metadata":{}},{"cell_type":"markdown","source":"**So sánh giữa các hành vi**\n- Hand at target location và Performs gesture có nhiệt độ trung bình cao hơn một chút so với hai hành vi còn lại.\n\n- Relaxes and moves hand to target location và Moves hand to target location có xu hướng ghi nhận nhiệt độ thấp hơn, có thể do thời điểm này tay chưa hoạt động mạnh hoặc chưa gần bề mặt cảm biến.","metadata":{}},{"cell_type":"markdown","source":"**So sánh giữa các cảm biến**\n- Cảm biến thm_5 và thm_4 thường cho kết quả nhiệt độ cao hơn một chút so với các cảm biến còn lại.\n\n- Cảm biến thm_2 có xu hướng thấp hơn trong một số hành vi như “Moves hand to target location”.","metadata":{}},{"cell_type":"markdown","source":"**Độ phân tán và nhiễu**\nCác boxplot có hình dạng tương đối đồng đều, nhưng có nhiều điểm dữ liệu ngoại lệ (outlier) → có thể do nhiễu từ môi trường hoặc cách người dùng tương tác với cảm biến.","metadata":{}},{"cell_type":"markdown","source":"### Tỷ lệ phần trăm Null","metadata":{}},{"cell_type":"code","source":"def null_percent(df):\n    per=((df.isnull().sum()/len(df))*100).round(2)\n    return per[per>0]\n\nprint(\"Nan Values in Train data\")\nprint(null_percent(train_df))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:33.903358Z","iopub.execute_input":"2025-08-07T08:07:33.903599Z","iopub.status.idle":"2025-08-07T08:07:34.791458Z","shell.execute_reply.started":"2025-08-07T08:07:33.903580Z","shell.execute_reply":"2025-08-07T08:07:34.790526Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Các cột có tỷ lệ NaN cao nhất là các cột có tên bắt đầu với tof_5_v (5.24% NaN).\n\nCác cột khác như rot_x, rot_w có tỷ lệ NaN thấp hơn nhiều (0.64%).","metadata":{}},{"cell_type":"markdown","source":"## 2.2 Quan sát hoặc xu hướng ban đầu","metadata":{}},{"cell_type":"code","source":"#Tổng hợp dữ liệu vào dictionary: Tạo dictionary datasets chứa 4 bộ dữ liệu chính, giúp dễ quản lý và xử lý lặp đi lặp lại.\ndatasets = {\n    \"Train Data\": train_df,\n    \"Train Demographics\": train_dem_df,\n    \"Test Data\": test_df,\n    \"Test Demographics\": test_dem_df,\n}\n\n# Print shapes\n#Giải thích: .items(): Duyệt qua từng cặp tên và DataFrame.\n#            .shape: Trả về tuple (số dòng, số cột).\n#            print(...): In ra tên dataset, số dòng và số cột, giúp bạn hình dung dữ liệu có bao nhiêu bản ghi và thuộc tính.\n\n\nfor name, df in datasets.items():\n    num_rows, num_cols = df.shape\n    print(f\"{name}:\")\n    print(f\"  Number of Rows: {num_rows}\")\n    print(f\"  Number of Columns: {num_cols}\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:34.792643Z","iopub.execute_input":"2025-08-07T08:07:34.793407Z","iopub.status.idle":"2025-08-07T08:07:34.799940Z","shell.execute_reply.started":"2025-08-07T08:07:34.793376Z","shell.execute_reply":"2025-08-07T08:07:34.798743Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Train Data: Có 574945 dòng và 341 cột.\n\nTrain Demographics: Có 81 dòng và 8 cột.\n\nTest Data: Có 107 dòng và 336 cột.\n\nTest Demographics: Có 8 dòng và 8 cột.","metadata":{}},{"cell_type":"code","source":"# --- 2.4 Hợp nhất dữ liệu nhân khẩu học và cảm biến ---\nmerged_train = pd.merge(train_df, train_dem_df, on=\"subject\", how=\"left\")\nmerged_test = pd.merge(test_df, test_dem_df, on=\"subject\", how=\"left\")\n\nprint(\"Merged Train Shape:\", merged_train.shape)\nprint(\"Merged Test Shape:\", merged_test.shape)\ndisplay(merged_train.head(2))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:34.800946Z","iopub.execute_input":"2025-08-07T08:07:34.801282Z","iopub.status.idle":"2025-08-07T08:07:35.592365Z","shell.execute_reply.started":"2025-08-07T08:07:34.801252Z","shell.execute_reply":"2025-08-07T08:07:35.591377Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Merged Train Shape:** (574945, 348) - Sau khi hợp nhất, merged_train có 574,945 dòng và 348 cột, tức là dữ liệu từ train_df (574,945 dòng và 341 cột) đã được kết hợp với 7 cột từ train_dem_df (vì train_dem_df có 8 cột, nhưng 1 cột là subject đã được sử dụng làm khóa hợp nhất).\n\n**Merged Test Shape:** (107, 343) - Tương tự, merged_test có 107 dòng và 343 cột, kết quả là hợp nhất giữa test_df (107 dòng và 336 cột) với test_dem_df (8 cột, với 1 cột subject là khóa hợp nhất).","metadata":{}},{"cell_type":"code","source":"# --- Thống kê mô tả toàn bộ merged_train ---\nmerged_train.describe().T\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:35.593413Z","iopub.execute_input":"2025-08-07T08:07:35.593641Z","iopub.status.idle":"2025-08-07T08:07:45.970028Z","shell.execute_reply.started":"2025-08-07T08:07:35.593623Z","shell.execute_reply":"2025-08-07T08:07:45.969092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- 2.5 Phân phối hành vi ---\nsns.countplot(data=merged_train, x='behavior', order=merged_train['behavior'].value_counts().index)\nplt.title(\"Phân phối các hành vi trong tập huấn luyện\")\nplt.xticks(rotation=45)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:45.976034Z","iopub.execute_input":"2025-08-07T08:07:45.976308Z","iopub.status.idle":"2025-08-07T08:07:46.408751Z","shell.execute_reply.started":"2025-08-07T08:07:45.976289Z","shell.execute_reply":"2025-08-07T08:07:46.407786Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Hành vi **\"Performs gesture\"** chiếm số lượng lớn nhất, với hơn 250.000 lần xuất hiện. Đây là hành vi phổ biến nhất trong dữ liệu.\n\n- Hành vi **\"Moves hand to target location\"** có số lượng xuất hiện thứ hai, xấp xỉ 150.000 lần, cho thấy đây là một hành vi cũng khá phổ biến nhưng ít hơn so với hành vi \"Performs gesture\".\n\n- Hành vi **\"Hand at target location\"** và **\"Relaxes and moves hand to target location\"** xuất hiện ít hơn nhiều, với số lượng lần lượt là khoảng 100.000 và dưới 50.000 lần.","metadata":{}},{"cell_type":"markdown","source":"#### **Nhận xét**\n","metadata":{}},{"cell_type":"markdown","source":"- Biểu đồ cho thấy sự phân bổ không đều giữa các hành vi trong tập huấn luyện. Hành vi \"Performs gesture\" chiếm ưu thế lớn nhất, có thể phản ánh một kiểu hành vi chủ yếu trong tập dữ liệu.\n\n- Các hành vi khác như \"Moves hand to target location\" hay \"Hand at target location\" ít phổ biến hơn nhưng vẫn chiếm một phần đáng kể trong dữ liệu.","metadata":{}},{"cell_type":"code","source":"# --- 2.12 Behavior theo phase & sequence_type ---\nsns.countplot(data=merged_train, x='behavior', hue='phase')\nplt.title(\"Behavior theo Phase\")\nplt.xticks(rotation=45)\nplt.show()\n\nsns.countplot(data=merged_train, x='behavior', hue='sequence_type')\nplt.title(\"Behavior theo Sequence Type\")\nplt.xticks(rotation=45)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:46.409530Z","iopub.execute_input":"2025-08-07T08:07:46.409760Z","iopub.status.idle":"2025-08-07T08:07:47.969662Z","shell.execute_reply.started":"2025-08-07T08:07:46.409742Z","shell.execute_reply":"2025-08-07T08:07:47.968680Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Biểu đồ 1: Behavior theo Phase**\n- **Mục đích:** Biểu đồ này thể hiện sự phân bổ các hành vi (behavior) theo hai giai đoạn: Transition và Gesture.\n\n- **Nhận xét:**\n\n  * Hành vi \"Performs gesture\" có số lượng lớn nhất trong giai đoạn Gesture, chiếm ưu thế rõ rệt so với các hành vi khác.\n  * Các hành vi như \"Moves hand to target location\", \"Hand at target location\", và \"Relaxes and moves hand to target location\" xuất hiện nhiều trong giai đoạn Transition.\n  * Điều này cho thấy Gesture chủ yếu liên quan đến việc thực hiện các cử chỉ, trong khi Transition liên quan đến chuyển động tay hoặc thay đổi vị trí.","metadata":{}},{"cell_type":"markdown","source":"**Biểu đồ 2: Behavior theo Sequence Type**\n- **Mục đích:** Biểu đồ này thể hiện sự phân bổ các hành vi (behavior) theo hai loại chuỗi: Target và Non-Target.\n\n- **Nhận xét:**\n\n  * Hành vi \"Performs gesture\" có số lượng lớn nhất trong chuỗi Target, cho thấy nó là một hành vi quan trọng và thường xuyên xuất hiện khi mục tiêu được đặt ra.\n  * Các hành vi \"Moves hand to target location\" và \"Hand at target location\" xuất hiện nhiều hơn trong chuỗi Non-Target, cho thấy những hành vi này có thể không hoàn toàn nhắm đến mục tiêu cụ thể mà chỉ là phần của quá trình chuyển động.\n  * Hành vi \"Relaxes and moves hand to target location\" có sự phân bố đồng đều giữa hai loại chuỗi, có thể chỉ ra rằng nó không quá phụ thuộc vào việc có mục tiêu hay không.","metadata":{}},{"cell_type":"markdown","source":"**Biểu đồ 1** cho thấy rõ ràng sự phân chia hành vi theo các giai đoạn trong quá trình thực hiện (chuyển động tay và cử chỉ).\n\n**Biểu đồ 2** làm nổi bật sự khác biệt trong hành vi đối với chuỗi mục tiêu và không mục tiêu, phản ánh sự quan trọng của việc xác định mục tiêu trong các hành vi cụ thể.","metadata":{}},{"cell_type":"code","source":"# --- 2.10 Phát hiện Outlier trong độ tuổi ---\nplt.figure(figsize=(6, 3))\nsns.boxplot(x=merged_train['age'])\nplt.title(\"Boxplot - Độ tuổi\")\nplt.show()\n\n# Lọc nếu cần\nmerged_train = merged_train[merged_train['age'] <= 100]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:47.971252Z","iopub.execute_input":"2025-08-07T08:07:47.971574Z","iopub.status.idle":"2025-08-07T08:07:49.788961Z","shell.execute_reply.started":"2025-08-07T08:07:47.971554Z","shell.execute_reply":"2025-08-07T08:07:49.788043Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Biểu đồ hộp (boxplot)** được sử dụng để hiển thị phân phối của độ tuổi trong bộ dữ liệu và phát hiện các điểm ngoại lệ.\n\n- **Box (hộp):** Phần giữa của boxplot thể hiện phạm vi giữa các phân vị 25% và 75%, tức là IQR (Interquartile Range), đại diện cho phạm vi của dữ liệu chủ yếu.\n\n- **Đường giữa (Median):** Đường nằm ngang trong hộp thể hiện giá trị trung vị (median) của độ tuổi.\n\n- **Whiskers (dây whiskers):** Các đường kéo dài từ hộp cho biết phạm vi của dữ liệu không bị coi là ngoại lệ.\n\n- **Outliers (Ngoại lệ):** Các chấm ngoài whiskers đại diện cho các điểm dữ liệu ngoại lệ, trong trường hợp này, có một vài điểm tuổi ngoài phạm vi chính thống (khoảng từ 10 đến 50).","metadata":{}},{"cell_type":"markdown","source":"**Nhận xét:**\nBiểu đồ hộp này cho thấy rằng phần lớn dữ liệu độ tuổi tập trung từ khoảng 10 đến 30, với một số ít ngoại lệ có độ tuổi vượt ra ngoài phạm vi thông thường. Việc lọc các giá trị trên 100 trong mã sẽ giúp loại bỏ các dữ liệu không hợp lệ hoặc ngoại lệ cực kỳ xa.","metadata":{}},{"cell_type":"code","source":"# --- 2.6 Biểu đồ histogram các đặc trưng cảm biến acc_ ---\nacc_cols = [col for col in merged_train.columns if col.startswith('acc_')]\nmerged_train[acc_cols].hist(figsize=(16, 12), bins=30)\nplt.suptitle(\"Phân phối các đặc trưng gia tốc (acc_)\", fontsize=16)\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:49.790034Z","iopub.execute_input":"2025-08-07T08:07:49.790332Z","iopub.status.idle":"2025-08-07T08:07:50.601973Z","shell.execute_reply.started":"2025-08-07T08:07:49.790299Z","shell.execute_reply":"2025-08-07T08:07:50.601020Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Biểu đồ phân phối của các đặc trưng gia tốc (acc_x, acc_y, acc_z):**\n\n- acc_x: Biểu đồ cho thấy dữ liệu gia tốc theo trục X có sự phân phối không đều, với một đỉnh rõ ràng ở giá trị gần 0 và các giá trị gia tốc có độ phân tán khá lớn, kéo dài từ -10 đến 10.\n\n- acc_y: Dữ liệu gia tốc theo trục Y có sự phân phối không đều với nhiều đỉnh. Tương tự như acc_x, phần lớn các giá trị gia tốc rơi vào khoảng từ -10 đến 10.\n\n- acc_z: Biểu đồ của gia tốc theo trục Z có phân phối tương tự, với nhiều giá trị tập trung quanh 0, tuy nhiên có một số đỉnh rõ ràng khác nhau, cho thấy sự thay đổi không đều của gia tốc theo trục Z.","metadata":{}},{"cell_type":"markdown","source":"Nhìn chung, cả ba đặc trưng gia tốc đều có sự phân bố tương đối rộng với các giá trị dàn trải từ âm đến dương, cho thấy các chuyển động hay thay đổi gia tốc trong các dữ liệu thu thập từ cảm biến này có tính chất phức tạp và đa dạng.","metadata":{}},{"cell_type":"code","source":"# --- 2.7 Ma trận tương quan acc_ ---\nplt.figure(figsize=(12, 10))\nsns.heatmap(merged_train[acc_cols].corr(), cmap='coolwarm', center=0)\nplt.title(\"Tương quan giữa các đặc trưng gia tốc (acc_)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:50.602995Z","iopub.execute_input":"2025-08-07T08:07:50.603306Z","iopub.status.idle":"2025-08-07T08:07:50.852121Z","shell.execute_reply.started":"2025-08-07T08:07:50.603272Z","shell.execute_reply":"2025-08-07T08:07:50.851206Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Tương quan giữa acc_x, acc_y, và acc_z:**\n\n- Các giá trị tương quan giữa các cặp đặc trưng gia tốc là khá cao, thể hiện qua màu sắc đỏ đậm trong các ô.\n\n- Tương quan giữa acc_x và acc_y có vẻ mạnh mẽ nhất, với giá trị tương quan rất gần 1, cho thấy rằng sự thay đổi của gia tốc theo trục x có mối quan hệ rất chặt chẽ với gia tốc theo trục y.\n\n- Tương quan giữa acc_x và acc_z cũng rất mạnh, với màu sắc đỏ đậm gần như tương tự như giữa acc_x và acc_y.\n\n- Mối quan hệ giữa acc_y và acc_z có vẻ ít mạnh mẽ hơn một chút nhưng vẫn thể hiện sự liên kết tích cực với giá trị tương quan khá cao.","metadata":{}},{"cell_type":"markdown","source":"Mối quan hệ mạnh mẽ giữa các đặc trưng gia tốc cho thấy sự thay đổi của gia tốc trên một trục (ví dụ, trục x) có thể dự đoán khá chính xác sự thay đổi trên các trục còn lại (y và z). Điều này có thể chỉ ra rằng các đặc trưng gia tốc này có sự phụ thuộc lẫn nhau, và việc sử dụng tất cả ba đặc trưng trong mô hình có thể không cần thiết nếu có sự dư thừa thông tin.\n\nĐể giảm thiểu độ phức tạp của mô hình, có thể cân nhắc loại bỏ một trong các đặc trưng gia tốc hoặc áp dụng phương pháp giảm chiều như PCA (Principal Component Analysis) để giữ lại các thông tin quan trọng nhất.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận:**\nBiểu đồ này chỉ ra rằng các đặc trưng gia tốc acc_x, acc_y, và acc_z có sự liên kết chặt chẽ với nhau. Việc nhận biết và xử lý mối quan hệ này có thể giúp cải thiện hiệu quả của mô hình học máy bằng cách loại bỏ sự dư thừa thông tin hoặc áp dụng các kỹ thuật giảm chiều.","metadata":{}},{"cell_type":"code","source":"# --- 2.8.1 Phân phối hành vi theo giới tính ---\nsns.countplot(data=merged_train, x='behavior', hue='sex', order=merged_train['behavior'].value_counts().index)\nplt.title(\"Phân phối hành vi theo giới tính\")\nplt.xticks(rotation=45)\nplt.show()\n\n# --- 2.8.2 Phân phối tuổi theo hành vi ---\nsns.boxplot(data=merged_train, x='behavior', y='age')\nplt.title(\"Tuổi trung bình theo hành vi\")\nplt.xticks(rotation=45)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:50.853150Z","iopub.execute_input":"2025-08-07T08:07:50.853512Z","iopub.status.idle":"2025-08-07T08:07:51.885427Z","shell.execute_reply.started":"2025-08-07T08:07:50.853476Z","shell.execute_reply":"2025-08-07T08:07:51.884409Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ 2.8.1: Phân phối hành vi theo giới tính**","metadata":{}},{"cell_type":"markdown","source":"Biểu đồ này thể hiện sự phân bố các hành vi dựa trên giới tính của người tham gia. Các hành vi được phân thành các nhóm như:\n\n- Performs gesture\n\n- Moves hand to target location\n\n- Hand at target location\n\n- Relaxes and moves hand to target location","metadata":{}},{"cell_type":"markdown","source":"- Hành vi \"Performs gesture\" chiếm số lượng lớn nhất trong cả hai giới tính, đặc biệt là đối với giới tính nữ (giới tính mã hóa là 1).\n\n- \"Moves hand to target location\" là hành vi có sự phân chia khá rõ giữa hai giới tính, nhưng số lượng vẫn nghiêng về giới tính nữ.\n\n- Các hành vi \"Hand at target location\" và \"Relaxes and moves hand to target location\" có sự phân bố khá đồng đều giữa hai giới tính, nhưng vẫn có xu hướng nhiều hơn ở giới tính nữ.","metadata":{}},{"cell_type":"markdown","source":"Điều này cho thấy rằng các hành vi liên quan đến chuyển động tay và cử chỉ có xu hướng phổ biến hơn ở nữ so với nam trong dữ liệu này.","metadata":{}},{"cell_type":"markdown","source":"**Phân tích biểu đồ 2.8.2: Phân phối tuổi theo hành vi**","metadata":{}},{"cell_type":"markdown","source":"Biểu đồ này thể hiện sự phân bố độ tuổi của người tham gia theo từng hành vi. Các hành vi được phân chia như sau:\n\n- Relaxes and moves hand to target location\n\n- Hand at target location\n\n- Performs gesture\n\n- Moves hand to target location","metadata":{}},{"cell_type":"markdown","source":"- Các hành vi đều có độ tuổi trung bình khá đồng đều, dao động từ khoảng 20 đến 25 tuổi.\n\n- \"Moves hand to target location\" có độ tuổi trung bình cao nhất, với một số lượng người tham gia ở độ tuổi lớn hơn (trên 30 tuổi).\n\n- Hành vi \"Hand at target location\" và \"Performs gesture\" có xu hướng thuộc nhóm tuổi trẻ hơn, dao động quanh 20 tuổi.\n\n- \"Relaxes and moves hand to target location\" có sự phân bố khá đồng đều và không có sự chênh lệch tuổi rõ rệt.","metadata":{}},{"cell_type":"markdown","source":"Nhìn chung, độ tuổi của người tham gia có xu hướng trẻ và tập trung nhiều ở độ tuổi từ 20 đến 25 tuổi đối với các hành vi này.","metadata":{}},{"cell_type":"code","source":"#Hiển thị dữ liệu mẫu: .head(2): Xem trước 2 dòng đầu của mỗi bảng.\n#                      display(...): Dùng trong notebook để hiển thị bảng dữ liệu đẹp, rõ ràng.\n\ndisplay(train_df.head(2))\ndisplay(train_dem_df.head(2))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:51.886505Z","iopub.execute_input":"2025-08-07T08:07:51.886759Z","iopub.status.idle":"2025-08-07T08:07:51.914361Z","shell.execute_reply.started":"2025-08-07T08:07:51.886738Z","shell.execute_reply":"2025-08-07T08:07:51.913338Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"display(test_df.head(2))\ndisplay(test_dem_df.head(2))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:51.915165Z","iopub.execute_input":"2025-08-07T08:07:51.915431Z","iopub.status.idle":"2025-08-07T08:07:51.954383Z","shell.execute_reply.started":"2025-08-07T08:07:51.915398Z","shell.execute_reply":"2025-08-07T08:07:51.953588Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 2.2.1. Participant & Contextual Features (Tính năng của người tham gia và bối cảnh)","metadata":{}},{"cell_type":"code","source":"#Chuẩn bị hàm thống kê mô tả có lọc cột: \n# excluded_prefixes: Bộ tiền tố để loại các cột cảm biến như: 'acc_': Gia tốc kế, 'rot_': Cảm biến xoay, 'thm_': Nhiệt độ/Độ ẩm. 'tof_': Time-of-Flight (cảm biến khoảng cách).\n\n# Define excluded prefixes\nexcluded_prefixes = ('acc_', 'rot_', 'thm_', 'tof_')\n\n#Hàm filtered_describe: Lọc các cột không thuộc cảm biến và có kiểu số.\n#                       Dùng .describe().T để thống kê mô tả (mean, std, min, max,...) và chuyển vị bảng cho dễ nhìn.\n#                       display: Hiển thị bảng thống kê.\n\ndef filtered_describe(df, name):\n    # Exclude sensor columns\n    filtered_cols = [col for col in df.columns \n                     if not col.startswith(excluded_prefixes) and pd.api.types.is_numeric_dtype(df[col])]\n    \n    # Describe and style\n    print(f'\\n➡️ Description of numerical columns in {name}')\n    return df[filtered_cols].describe().T.style.background_gradient(cmap='viridis')\n\n# Only analyze train sets!\ndisplay(filtered_describe(train_df, \"train_df\"))\ndisplay(filtered_describe(train_dem_df, \"train_dem_df\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:51.955311Z","iopub.execute_input":"2025-08-07T08:07:51.955632Z","iopub.status.idle":"2025-08-07T08:07:52.122723Z","shell.execute_reply.started":"2025-08-07T08:07:51.955602Z","shell.execute_reply":"2025-08-07T08:07:52.121650Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Lọc các cột không phải cảm biến: Đầu tiên, đoạn mã lọc ra các cột không liên quan đến cảm biến và chỉ giữ lại các cột số.\n\n- Tính thống kê mô tả: Sau khi lọc, sử dụng .describe() để tính toán các thống kê mô tả cho các cột số. Thống kê này bao gồm giá trị trung bình (mean), độ lệch chuẩn (std), giá trị nhỏ nhất và lớn nhất (min, max), và các phần tư (25%, 50%, 75%).\n\n- Hiển thị kết quả: Cuối cùng, kết quả thống kê mô tả được hiển thị với màu nền gradient để dễ dàng nhận diện các giá trị lớn nhỏ trong bảng.","metadata":{}},{"cell_type":"markdown","source":"Dựa trên mã, kết quả của thống kê mô tả sẽ bao gồm các giá trị:\n\n- count: Số lượng giá trị không thiếu trong mỗi cột.\n\n- mean: Giá trị trung bình của cột.\n\n- std: Độ lệch chuẩn, cho biết mức độ phân tán của dữ liệu.\n\n- min: Giá trị nhỏ nhất trong cột.\n\n- 25%: Giá trị tại phần tư đầu tiên (25% giá trị nhỏ nhất).\n\n- 50%: Giá trị trung vị (50% của dữ liệu).\n\n- 75%: Giá trị tại phần tư thứ ba (75% giá trị nhỏ nhất).\n\n- max: Giá trị lớn nhất trong cột.","metadata":{}},{"cell_type":"markdown","source":"**Cột Age**\n- Độ tuổi trung bình là 23.5, chủ yếu nằm trong độ tuổi thanh niên, với độ lệch chuẩn thấp (4.2), điều này cho thấy các cá thể trong bộ dữ liệu này chủ yếu có độ tuổi gần nhau, từ 20 đến 26.\n\n- Phạm vi độ tuổi là từ 10 đến 45, cho thấy có sự đa dạng về độ tuổi, nhưng phần lớn đối tượng nghiên cứu có độ tuổi dưới 26.\n\n- Phân bổ độ tuổi cho thấy sự tập trung lớn ở độ tuổi dưới 26, với một số cá thể nhỏ ở các độ tuổi cao hơn.","metadata":{}},{"cell_type":"markdown","source":"**Cột height (Chiều cao)**\n- Chiều cao trung bình là 168.3 cm, với độ lệch chuẩn vừa phải (7.5), cho thấy sự phân tán vừa phải giữa các cá thể trong dữ liệu.\n\n- Phạm vi chiều cao từ 150 cm đến 190 cm khá rộng, và phần lớn dữ liệu tập trung trong khoảng từ 162 cm đến 174 cm.","metadata":{}},{"cell_type":"markdown","source":"**Cột weight (Cân nặng)**\n- Cân nặng trung bình là 65.2 kg, với độ lệch chuẩn lớn (12.3), cho thấy sự phân tán lớn trong dữ liệu.\n\n- Phạm vi cân nặng từ 40 kg đến 110 kg có thể do sự đa dạng trong đối tượng nghiên cứu (bao gồm cả người có cân nặng thấp và cao).\n\n- Phần lớn người tham gia có cân nặng từ 55 kg đến 75 kg.","metadata":{}},{"cell_type":"code","source":"#Kiểm tra thiếu giá trị & thống kê cảm biến: \n#Danh sách các cột không thuộc cảm biến (có thể là ID, thông tin khác). \n\nexcluded_prefixes = ('acc_', 'rot_', 'thm_', 'tof_')\nsensor_cols = [col for col in train_df.columns if not col.startswith(excluded_prefixes)]\n\n# Sensor Data Summary for TRAIN\n#isnull().sum(): Đếm số giá trị bị thiếu.\nmissing_sensor_train = pd.DataFrame({\n    'Feature': sensor_cols,\n    '[TRAIN] Missing Count': train_df[sensor_cols].isnull().sum().values,\n    '[TRAIN] Missing %': (train_df[sensor_cols].isnull().sum().values / len(train_df)) * 100\n})\n\n#nunique(): Đếm số lượng giá trị duy nhất.\nunique_sensor_train = pd.DataFrame({\n    'Feature': sensor_cols,\n    'Unique Values [TRAIN]': train_df[sensor_cols].nunique().values\n})\n\n#dtypes: Lấy kiểu dữ liệu của từng cột.\ndtypes_sensor = pd.DataFrame({\n    'Feature': sensor_cols,\n    'Data Type': train_df[sensor_cols].dtypes.values\n})\n\n# Merge all summaries (NO test set)\n#merge: Gộp các bảng thống kê thành bảng duy nhất theo Feature.\nsensor_summary = missing_sensor_train \\\n    .merge(unique_sensor_train, on='Feature', how='left') \\\n    .merge(dtypes_sensor, on='Feature', how='left')\n\n# Display styled DataFrame (mask NaNs just for styling)\n#fillna(0): Điền giá trị thiếu bằng 0 (cho đẹp mắt khi hiển thị).\n#.style.background_gradient: Tô màu nền theo giá trị giúp dễ nhìn.\nstyled_df = sensor_summary.fillna(0)\nstyled_df.style.background_gradient(cmap='viridis')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:52.123883Z","iopub.execute_input":"2025-08-07T08:07:52.124417Z","iopub.status.idle":"2025-08-07T08:07:53.155632Z","shell.execute_reply.started":"2025-08-07T08:07:52.124390Z","shell.execute_reply":"2025-08-07T08:07:53.154732Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Không có giá trị thiếu: Tất cả các cột trong bảng thống kê đều không có giá trị thiếu (Missing Count = 0), cho thấy dữ liệu của bạn rất đầy đủ.\n\n- Sự đa dạng trong dữ liệu: Các cột như row_id, sequence_id, và subject có số lượng giá trị duy nhất khá lớn, trong khi các cột như sequence_type, phase, và behavior có ít giá trị duy nhất hơn, cho thấy chúng có thể là các thông tin phân loại.\n\n- Kiểu dữ liệu: Hầu hết các cột đều có kiểu dữ liệu object, ngoại trừ sequence_counter có kiểu int64, điều này cho thấy hầu hết các cột chứa thông tin phân loại (string), chỉ có một cột chứa giá trị số nguyên.","metadata":{}},{"cell_type":"code","source":"#Thống kê tương tự cho nhân khẩu học: Tương tự như phần thống kê cảm biến nhưng áp dụng cho dữ liệu nhân khẩu học.\n\n# Cột nhân khẩu học (không loại trừ)\ndem_cols = train_dem_df.columns\n\n# Giá trị bị thiếu trong dữ liệu nhân khẩu học của train \nmissing_demo_train = pd.DataFrame({\n    'Feature': dem_cols,\n    '[TRAIN DEMO] Missing Count': train_dem_df[dem_cols].isnull().sum().values,\n    '[TRAIN DEMO] Missing %': (train_dem_df[dem_cols].isnull().sum().values / len(train_dem_df)) * 100\n})\n\n# Giá trị duy nhất được tính trong dữ liệu nhân khẩu học của train \nunique_demo_train = pd.DataFrame({\n    'Feature': dem_cols,\n    'Unique Values [TRAIN DEMO]': train_dem_df[dem_cols].nunique().values\n})\n\n# Data types\ndtypes_demo = pd.DataFrame({\n    'Feature': dem_cols,\n    'Data Type': train_dem_df[dem_cols].dtypes.values\n})\n\n# Tóm tắt hợp nhất (chỉ dành cho đào tạo)\ndemo_summary = (\n    missing_demo_train\n    .merge(unique_demo_train, on='Feature', how='left')\n    .merge(dtypes_demo, on='Feature', how='left')\n)\n\n# Hiển thị tóm tắt theo phong cách\ndemo_summary.style.background_gradient(cmap='viridis')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:53.156748Z","iopub.execute_input":"2025-08-07T08:07:53.157117Z","iopub.status.idle":"2025-08-07T08:07:53.185998Z","shell.execute_reply.started":"2025-08-07T08:07:53.157092Z","shell.execute_reply":"2025-08-07T08:07:53.184653Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Không có giá trị thiếu: Các cột nhân khẩu học đều không có giá trị thiếu, cho thấy dữ liệu đầy đủ và sẵn sàng để phân tích.\n\n- Sự đa dạng trong dữ liệu: Các cột như subject, age, và height_cm có sự đa dạng về giá trị duy nhất, trong khi các cột như sex, handedness có ít giá trị duy nhất hơn, cho thấy đây là các cột phân loại.\n\n- Kiểu dữ liệu: Các cột phần lớn có kiểu dữ liệu int64 hoặc float64, cho thấy dữ liệu chủ yếu là số nguyên hoặc số thực. Các cột như subject có kiểu dữ liệu object, có thể là các chuỗi ký tự.","metadata":{}},{"cell_type":"markdown","source":"### 2.2.2. Sensor-Based Features (Tính năng dựa trên cảm biến)","metadata":{}},{"cell_type":"markdown","source":"#### **NOTE:**\n* acc_: Gia tốc kế.\n* rot_: Cảm biến quay.\n* thm_: Nhiệt độ, độ ẩm.\n* tof_: Khoảng cách.\n","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\n# Nhóm cột cảm biến (chỉ sử dụng train_df)\n# Phân nhóm các biến cảm biến:\nacc_cols = [col for col in train_df.columns if col.startswith('acc_')]\nrot_cols = [col for col in train_df.columns if col.startswith('rot_')]\nthm_cols = [col for col in train_df.columns if col.startswith('thm_')]\ntof_cols = [col for col in train_df.columns if col.startswith('tof_')]\n\n# Hàm trợ giúp cho số liệu thống kê tóm tắt (chỉ dành cho đào tạo)\n#Hàm thống kê chi tiết từng nhóm cảm biến (Tỷ lệ thiếu giá trị; Giá trị nhỏ nhất, lớn nhất; Trung bình, độ lệch chuẩn.)\ndef sensor_summary(df, cols, name, dataset_name):\n    summary = pd.DataFrame({\n        'Feature': cols,\n        f'{dataset_name} Missing %': df[cols].isnull().mean().values * 100,\n        f'{dataset_name} Min': df[cols].min().values,\n        f'{dataset_name} Max': df[cols].max().values,\n        f'{dataset_name} Mean': df[cols].mean().values,\n        f'{dataset_name} Std': df[cols].std().values\n    })\n    summary.insert(0, 'Sensor', name)\n    return summary\n\n# Tính toán tóm tắt quá trình đào tạo cho từng loại cảm biến\n#Thống kê tổng hợp cho tập train: Duyệt từng nhóm cảm biến và gom kết quả thống kê lại.\n# pd.concat: Ghép bảng lại thành bảng thống kê tổng hợp.\ndef train_sensor_summary(train_df, sensor_cols_dict):\n    all_train = []\n    for sensor_name, cols in sensor_cols_dict.items():\n        all_train.append(sensor_summary(train_df, cols, sensor_name, 'Train'))\n    train_summary = pd.concat(all_train, ignore_index=True)\n    return train_summary\n\n# Nhóm cảm biến để xử lý\nsensor_cols_dict = {\n    'acc': acc_cols,\n    'rot': rot_cols,\n    'thm': thm_cols,\n    'tof': tof_cols\n}\n\n# Chỉ tóm tắt về chuyến tàu chạy\nsensor_train_summary = train_sensor_summary(train_df, sensor_cols_dict)\n\n# Trung bình theo nhóm cảm biến: \n#                             .groupby('Sensor'): Gom nhóm theo loại cảm biến.\n#                             .mean(numeric_only=True): Trung bình các chỉ số số học.\n#                             Tô màu bảng kết quả giúp dễ phân tích.\n\n# Hiển thị mức trung bình cấp cảm biến cho tàu hỏa\nsummary_by_group = sensor_train_summary.groupby('Sensor').mean(numeric_only=True)\n\n# Hiển thị bảng theo kiểu\nsummary_by_group.style.background_gradient(cmap='viridis')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:53.186899Z","iopub.execute_input":"2025-08-07T08:07:53.187241Z","iopub.status.idle":"2025-08-07T08:07:58.984431Z","shell.execute_reply.started":"2025-08-07T08:07:53.187217Z","shell.execute_reply":"2025-08-07T08:07:58.983427Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Không có giá trị thiếu (Missing %): Hầu hết các nhóm cảm biến đều không có giá trị thiếu hoặc chỉ có một tỷ lệ rất nhỏ.\n\n- Giá trị dao động (Min và Max): Các nhóm cảm biến như gia tốc kế và cảm biến khoảng cách có phạm vi giá trị rộng, đặc biệt là cảm biến khoảng cách có giá trị ngoại lệ.\n\n- Trung bình (Mean): Các giá trị trung bình gần với 0 cho cảm biến xoay và gần với giá trị thực tế cho cảm biến nhiệt độ.\n\n- Độ lệch chuẩn (Std): Cảm biến khoảng cách có độ lệch chuẩn rất lớn, chỉ ra sự phân tán lớn trong dữ liệu. Các nhóm cảm biến khác có độ lệch chuẩn vừa phải.","metadata":{}},{"cell_type":"markdown","source":"# 3. EDA","metadata":{}},{"cell_type":"markdown","source":"## 3.1. Numerical Feature Distribution Analysis (Phân tích phân phối tính năng số) (Phân tích đơn biến)","metadata":{}},{"cell_type":"markdown","source":"### 3.1.1 Participant & Contextual Features (Tính năng của người tham gia và bối cảnh)","metadata":{}},{"cell_type":"code","source":"#Ẩn cảnh báo không cần thiết:  Ẩn cảnh báo tương lai của thư viện, Ẩn cảnh báo về ký tự bị thiếu font khi vẽ biểu đồ.\n\nwarnings.simplefilter(action='ignore', category=FutureWarning)\nwarnings.filterwarnings(\"ignore\", message=\"Glyph.*missing from current font\")\n\ncustom_palette = ['#3498db', '#e74c3c', '#2ecc71']\nsensor_prefixes = ('acc_', 'rot_', 'thm_', 'tof_')\ncategorical_columns = ['phase', 'behavior', 'orientation', 'sequence_type',\n                       'adult_child', 'sex', 'handedness']\ntarget_columns = ['gesture']\ncategorical_tracker = categorical_columns.copy()\n\ndef get_numerical_variables(df, excluded_prefixes, excluded_columns):\n    return [col for col in df.columns \n            if pd.api.types.is_numeric_dtype(df[col])\n            and not col.startswith(excluded_prefixes)\n            and col not in excluded_columns]\n\n# ONLY TRAIN SETS!\ntrain_main = train_df.copy()\ntrain_demo = train_dem_df.copy()\n\ntrain_main['Dataset'] = 'Train'\ntrain_demo['Dataset'] = 'Train'\n\nmain_data = train_main\ndemo_data = train_demo\n\nmain_numeric_vars = get_numerical_variables(main_data, sensor_prefixes, categorical_columns)\ndemo_numeric_vars = get_numerical_variables(demo_data, (), categorical_columns)\n\ndef create_variable_plots(variable, dataset_label, data):\n    sns.set_style('whitegrid')\n    fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n\n    # Box plot\n    plt.subplot(1, 2, 1)\n    sns.boxplot(data=data, x=variable, y=\"Dataset\", palette=custom_palette)\n    plt.xlabel(variable)\n    plt.title(f\" Box Plot for {variable} — {dataset_label}\")\n\n    # Histogram\n    plt.subplot(1, 2, 2)\n    for label, color in zip(data['Dataset'].unique(), custom_palette):\n        subset = data[data['Dataset'] == label]\n        sns.histplot(data=subset, x=variable, kde=True, bins=30, label=label, color=color)\n    plt.xlabel(variable)\n    plt.ylabel(\"Frequency\")\n    plt.title(f\" Histogram for {variable} — {dataset_label}\")\n    plt.legend()\n\n    plt.tight_layout()\n    plt.show()\n\n# Chỉ vẽ các tính năng bối cảnh của bộ đào tạo\nfor var in main_numeric_vars:\n    create_variable_plots(var, \"Context Features\", main_data)\n\n# Chỉ vẽ các đặc điểm nhân khẩu học của bộ huấn luyện\nfor var in demo_numeric_vars:\n    create_variable_plots(var, \"Demographic Features\", demo_data)\n\n# --- Cleanup\ntrain_main.drop('Dataset', axis=1, inplace=True)\ntrain_demo.drop('Dataset', axis=1, inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:07:58.985623Z","iopub.execute_input":"2025-08-07T08:07:58.986107Z","iopub.status.idle":"2025-08-07T08:08:06.738520Z","shell.execute_reply.started":"2025-08-07T08:07:58.986064Z","shell.execute_reply":"2025-08-07T08:08:06.737626Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**1. Tắt cảnh báo không cần thiết:**\n\nTránh làm nhiễu output khi chạy notebook (FutureWarning, font glyphs).\n\n**2. Xác định cột số học (numerical features):**\n\nBỏ qua các cột dạng sensor (acc_, rot_,...) và categorical (sex, phase,...).\n\n**3. Tạo bản sao dữ liệu huấn luyện:**\n\nGán thêm cột \"Dataset\" = 'Train' để phân biệt sau này trong biểu đồ.\n\n**4. Hàm create_variable_plots:**\n\n- Vẽ song song 2 biểu đồ cho mỗi biến:\n\n  * Boxplot: kiểm tra phân bố, phát hiện outliers.\n  * Histogram + KDE: xem dạng phân bố dữ liệu.\n\n**5. Vòng lặp vẽ biểu đồ:**\n\nLặp qua từng biến số trong main_numeric_vars (context features) và demo_numeric_vars (demographic features).\n\n**6. Cleanup:**\n\nXóa cột \"Dataset\" sau khi vẽ để tránh ảnh hưởng dữ liệu gốc.","metadata":{}},{"cell_type":"markdown","source":"**PHÂN TÍCH BIỂU ĐỒ**","metadata":{}},{"cell_type":"markdown","source":" **sequence_counter (context feature)**\n\n- **Boxplot:** có rất nhiều outliers bên phải → phân bố lệch phải nặng.\n\n- **Histogram:** gần như tất cả giá trị nằm trong khoảng 0–100, một số rất nhỏ lên đến gần 700.\n\n→ Cần chuẩn hóa hoặc log-transform để giảm ảnh hưởng của outliers. Đây là biến thời gian trong chuỗi, có thể ảnh hưởng tới mô hình nếu không xử lý.","metadata":{}},{"cell_type":"markdown","source":"**age (demographic)**\n\n- **Boxplot:** có 2 outliers ở khoảng 50 tuổi, hầu hết tập trung từ 10–35 tuổi.\n\n- **Histogram:** phân bố lệch phải nhẹ, có một số đỉnh nhỏ → phân bố không chuẩn.\n\n→ Nên chuẩn hóa hoặc phân loại theo nhóm tuổi nếu cần. Dữ liệu thiên về nhóm trẻ và thanh thiếu niên.","metadata":{}},{"cell_type":"markdown","source":"**height_cm (demographic)**\n\n- **Boxplot:** Có vài outlier nhỏ bên trái (thấp hơn 140 cm).\n\n- **Histogram:** Gần chuẩn, nhưng hơi lệch trái nhẹ (mean < median).\n\n→ Dữ liệu chiều cao tương đối cân bằng, có thể sử dụng trực tiếp.","metadata":{}},{"cell_type":"markdown","source":"**shoulder_to_wrist_cm (demographic)**\n\n- **Boxplot:** Có một vài outliers ở cả hai đầu.\n\n- **Histogram:** Có hình chuông, hơi lệch phải, tập trung quanh 50–55 cm.\n\n→ Là biến sinh trắc quan trọng (liên quan đến kích thước cơ thể). Phân bố ổn, nhưng cần kiểm tra outlier.","metadata":{}},{"cell_type":"markdown","source":"**elbow_to_wrist_cm (demographic)**\n\n- **Boxplot:** Có 2 outliers (1 nhỏ ~18 cm, 1 lớn ~44 cm).\n\n- **Histogram:** Phân bố gần chuẩn, tập trung quanh 25–30 cm.\n\n→ Có vẻ ổn, nhưng cần kiểm tra xem các outliers có phải lỗi nhập liệu không.","metadata":{}},{"cell_type":"markdown","source":"**1. Dữ liệu có sự hiện diện rõ ràng của outliers**\n- Một số biến như sequence_counter, age, height_cm, elbow_to_wrist_cm... có các điểm ngoại lai rõ rệt (được thể hiện qua boxplot).\n\n- Cần cân nhắc loại bỏ hoặc xử lý outliers để tránh gây nhiễu cho mô hình học máy.\n\n**2. Phân bố không chuẩn và lệch về một phía**\n- Nhiều đặc trưng (đặc biệt là sequence_counter, age) có phân bố lệch phải, không tuân theo phân phối chuẩn.\n\n**3. Các đặc trưng sinh trắc học có phân bố khá hợp lý**\n- Các biến như height_cm, shoulder_to_wrist_cm, elbow_to_wrist_cm có phân bố gần chuẩn và ít outliers hơn.\n\n- Có thể sử dụng trực tiếp hoặc chuẩn hóa nhẹ tùy yêu cầu mô hình.\n\n**4. Một số biến cần được xử lý đặc biệt**\n- sequence_counter rất lệch và phân bố tập trung ở vùng thấp → cần biến đổi để tránh ảnh hưởng trọng số học.\n\n- age tập trung nhiều ở nhóm trẻ và thanh thiếu niên → có thể phân nhóm tuổi thay vì xử lý như biến liên tục.","metadata":{}},{"cell_type":"markdown","source":"### 3.1.2 Sensor-Based Features (Tính năng dựa trên cảm biến)","metadata":{}},{"cell_type":"markdown","source":"**Mục tiêu:**\nTrích xuất các đặc trưng thống kê (mean, std, max) từ dữ liệu sensor (acceleration, rotation, temperature, ToF) theo từng sequence_id để phục vụ mô hình học máy hoặc phân tích dữ liệu.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\n# 1) Sao chép đào tạo và kiểm tra để chúng ta không sửa đổi DataFrame gốc\ntrain_temp = train_df.copy()\ntest_temp  = test_df.copy()\n\n# 2) GIA TỐC KẾ: tính toán độ lớn tại mỗi dấu thời gian\ntrain_temp['acc_mag'] = np.sqrt(\n    train_temp['acc_x']**2 + train_temp['acc_y']**2 + train_temp['acc_z']**2\n)\ntest_temp['acc_mag'] = np.sqrt(\n    test_temp['acc_x']**2 + test_temp['acc_y']**2 + test_temp['acc_z']**2\n)\n\n# 3) ROTATION: tính toán “góc quay” từ thành phần quaternion w\n# (Lưu ý: rot_w nằm trong [-1,1], do đó arccos hợp lệ. Chúng tôi bỏ qua NaN nếu có.)\ntrain_temp['rot_angle'] = 2 * np.arccos(train_temp['rot_w'].clip(-1,1))\ntest_temp['rot_angle']  = 2 * np.arccos(test_temp['rot_w'].clip(-1,1))\n\n# 4) Nhóm theo sequence_id và tổng hợp các tóm tắt gia tốc kế\nacc_agg_funcs = {\n    'acc_mag': ['mean', 'std', 'max']\n}\ntrain_acc_summary = train_temp.groupby('sequence_id').agg(acc_agg_funcs)\ntest_acc_summary  = test_temp.groupby('sequence_id').agg(acc_agg_funcs)\n\n# Làm phẳng cột MultiIndex\ntrain_acc_summary.columns = ['acc_mag_' + stat for stat in ['mean', 'std', 'max']]\ntest_acc_summary.columns  = ['acc_mag_' + stat for stat in ['mean', 'std', 'max']]\n\n# 5) Nhóm theo sequence_id và tổng hợp tóm tắt vòng quay\nrot_agg_funcs = {\n    'rot_angle': ['mean', 'std', 'max']\n}\ntrain_rot_summary = train_temp.groupby('sequence_id').agg(rot_agg_funcs)\ntest_rot_summary  = test_temp.groupby('sequence_id').agg(rot_agg_funcs)\n\ntrain_rot_summary.columns = ['rot_angle_' + stat for stat in ['mean', 'std', 'max']]\ntest_rot_summary.columns  = ['rot_angle_' + stat for stat in ['mean', 'std', 'max']]\n\n# 6) NHIỆT ĐỘ: năm cảm biến thm_1 … thm_5\nthm_cols = [f\"thm_{i}\" for i in range(1, 6)]\n\n# Xác định hàm tổng hợp: trung bình + độ lệch chuẩn\nthm_agg_funcs = {col: ['mean', 'std'] for col in thm_cols}\n\ntrain_thm_summary = train_temp.groupby('sequence_id').agg(thm_agg_funcs)\ntest_thm_summary  = test_temp.groupby('sequence_id').agg(thm_agg_funcs)\n\n# Làm phẳng các cột MultiIndex\nflattened_thm_cols = []\nfor sensor in thm_cols:\n    for stat in ['mean','std']:\n        flattened_thm_cols.append(f\"{sensor}_{stat}\")\n\ntrain_thm_summary.columns = flattened_thm_cols\ntest_thm_summary.columns  = flattened_thm_cols\n\n# 7) THỜI GIAN BAY: mỗi cảm biến i có 64 cột pixel: tof_i_v0 … tof_i_v63\n# Chúng ta sẽ tạo một “tof_i_mean_at_ts” cho mỗi dấu thời gian, sau đó tổng hợp theo từng chuỗi.\n\ndef compute_tof_sequence_summary(df):\n    # Khởi tạo một dict để giữ DataFrames theo từng chuỗi\n    seq_summaries = {}\n\n    for i in range(1, 6):\n        # Xây dựng danh sách các cột cho cảm biến i\n        tof_cols = [f\"tof_{i}_v{pix}\" for pix in range(64)]\n        # Thay thế -1 bằng NaN để chúng không làm lệch giá trị trung bình; chuyển đổi sang float\n        ts_grid = df[tof_cols].replace(-1, np.nan).astype(float)\n        # Tính toán “trung bình trên tất cả 64 pixel” cho mỗi dấu thời gian\n        df[f\"tof_{i}_mean_at_ts\"] = ts_grid.mean(axis=1)\n    \n   # Bây giờ, nhóm theo id chuỗi và tính giá trị trung bình và độ lệch chuẩn của các giá trị trung bình đó\n    agg_dict = {f\"tof_{i}_mean_at_ts\": ['mean','std'] for i in range(1, 6)}\n    summary = df.groupby('sequence_id').agg(agg_dict)\n    # Làm phẳng các cột MultiIndex\n    flat_cols = [f\"tof_{i}_{stat}\" for i in range(1, 6) for stat in ['mean','std']]\n    summary.columns = flat_cols\n    return summary\n\ntrain_tof_summary = compute_tof_sequence_summary(train_temp)\ntest_tof_summary  = compute_tof_sequence_summary(test_temp)\n\n# 8) Hợp nhất các tóm tắt accel, rotation, thm, tof (trên sequence_id)\ntrain_sensor_summary = (\n    train_acc_summary\n    .join(train_rot_summary, how='outer')\n    .join(train_thm_summary, how='outer')\n    .join(train_tof_summary, how='outer')\n)\n\ntest_sensor_summary = (\n    test_acc_summary\n    .join(test_rot_summary, how='outer')\n    .join(test_thm_summary, how='outer')\n    .join(test_tof_summary, how='outer')\n)\n\n# 9) Thêm cột “Dataset” để chúng ta có thể thực hiện box+hist song song\ntrain_sensor_summary['Dataset'] = 'Train'\ntest_sensor_summary['Dataset']  = 'Test'\n\n# 10) Ghép nối thành một DataFrame để vẽ đồ thị\ncombined_sensor_summary = pd.concat(\n    [train_sensor_summary, test_sensor_summary],\n    axis=0\n).reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:06.739422Z","iopub.execute_input":"2025-08-07T08:08:06.739729Z","iopub.status.idle":"2025-08-07T08:08:12.721570Z","shell.execute_reply.started":"2025-08-07T08:08:06.739707Z","shell.execute_reply":"2025-08-07T08:08:12.720661Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Các bước chính**\n-","metadata":{}},{"cell_type":"markdown","source":"1. Sao chép dữ liệu gốc để tránh thay đổi trực tiếp.\n\n2. Tính toán đặc trưng:\n\n  * acc_mag: Độ lớn gia tốc.\n\n  * rot_angle: Góc quay từ quaternion.\n\n  * thm: Trung bình và độ lệch chuẩn nhiệt độ từ thm_1–thm_5.\n\n  * tof: Trung bình theo thời gian của 64 pixel cho mỗi ToF sensor.\n\n3. Tổng hợp theo sequence_id: Lấy mean, std, max cho từng đặc trưng.\n\n4. Hợp nhất các đặc trưng lại thành 1 bảng cho train và test.\n\n5. Gắn nhãn Dataset (Train/Test) và ghép lại thành combined_sensor_summary để trực quan hóa hoặc huấn luyện mô hình.","metadata":{}},{"cell_type":"markdown","source":"**Mục tiêu:**\nTạo biểu đồ Boxplot và Histogram cho từng biến đặc trưng sensor từ tập dữ liệu \"Train\" trong combined_sensor_summary.\n\n**Thành phần chính:**\n- **Biến đầu vào:** train_sensor_summary — chứa dữ liệu cảm biến đã được tóm tắt theo từng sequence.\n\n- Hàm create_sensor_summary_plots(...):\n\n  * Vẽ 1 boxplot + 1 histogram cho mỗi biến.\n\n  * Sử dụng seaborn và matplotlib.\n\n  * Tùy chỉnh màu sắc và bố cục cho phù hợp.","metadata":{}},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\n\n# Tái sử dụng bảng màu tùy chỉnh của bạn\ncustom_palette = ['#3498db', '#e74c3c', '#2ecc71']\n\n# Chỉ lọc các mẫu \"Đào tạo\"\ntrain_sensor_summary = combined_sensor_summary[combined_sensor_summary['Dataset'] == 'Train']\n\n# Xác định hàm vẽ đồ thị (bây giờ chỉ sử dụng train_sensor_summary)\ndef create_sensor_summary_plots(variable, dataset_label, data):\n    sns.set_style('whitegrid')\n    fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n\n    # Box Plot (left)\n    plt.subplot(1, 2, 1)\n    sns.boxplot(data=data, x=variable, y=\"Dataset\", palette=[custom_palette[0]])\n    plt.xlabel(variable)\n    plt.title(f\"Box Plot for {variable} — {dataset_label}\")\n\n    # Histogram (right)\n    plt.subplot(1, 2, 2)\n    sns.histplot(data=data, x=variable, kde=True, bins=30, color=custom_palette[0], label=\"Train\")\n    plt.xlabel(variable)\n    plt.ylabel(\"Frequency\")\n    plt.title(f\"Histogram for {variable} — {dataset_label}\")\n    plt.legend()\n\n    plt.tight_layout()\n    plt.show()\n\n# Liệt kê tất cả các biến tóm tắt cảm biến (loại trừ 'Bộ dữ liệu')\nsensor_summary_vars = [col for col in train_sensor_summary.columns if col != 'Dataset']\n\n# Plot each\nfor var in sensor_summary_vars:\n    create_sensor_summary_plots(var, \"Per-Sequence Sensor Summaries\", train_sensor_summary)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:12.722520Z","iopub.execute_input":"2025-08-07T08:08:12.722872Z","iopub.status.idle":"2025-08-07T08:08:29.719790Z","shell.execute_reply.started":"2025-08-07T08:08:12.722804Z","shell.execute_reply":"2025-08-07T08:08:29.718739Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**PHÂN TÍCH CHI TIẾT THEO NHÓM BIẾN**","metadata":{}},{"cell_type":"markdown","source":"**1. acc_mag_mean, acc_mag_std, acc_mag_max**\n- **Boxplot:** Có nhiều outlier phía bên phải (giá trị cao bất thường).\n\n- **Histogram:**\n\n  * Mean: Phân bố lệch phải mạnh, tập trung quanh ~9.5.\n\n  * Std: Rất nhiều giá trị nhỏ, đuôi dài về bên phải.\n\n  * Max: Phân bố skewed (lệch phải), giá trị chủ yếu 10–20.","metadata":{}},{"cell_type":"markdown","source":"**2. rot_angle_mean, rot_angle_std, rot_angle_max**\n- **Boxplot:** Ít outlier hơn.\n\n- **Histogram:**\n\n  * Mean: Gần đối xứng, đỉnh khoảng 2.5–2.8.\n\n  * Std: Lệch phải nhẹ.\n\n  * Max: Cụm quanh 2.8–3.1.\n\n-> Các giá trị khá ổn định, gần giống nhau giữa các mẫu.","metadata":{}},{"cell_type":"markdown","source":"**3. thm_1_mean ~ thm_5_std (Các cảm biến nhiệt độ)**\n- **Boxplot:** Có nhiều outlier, đặc biệt là ở thm_1_mean, thm_3_mean, thm_4_std.\n\n- **Histogram:**\n\n  * Phân bố hình chuông hoặc lệch nhẹ.\n\n  * Các biến mean thường dao động từ 22–35.\n\n  * Các biến std thường nhỏ (< 2), với đuôi kéo dài.\n\n-> Nhiệt độ thường ổn định, std thấp. Nhưng cần xử lý outlier rõ ràng.","metadata":{}},{"cell_type":"markdown","source":"**4. tof_1_mean ~ tof_5_std (Các cảm biến ToF - Time of Flight)**\n- **Boxplot:** Có outlier rõ rệt ở tất cả các biến std.\n\n- **Histogram:**\n\n  * Phân bố gần chuẩn, nhưng lệch nhẹ về phải.\n\n  * Mean dao động khoảng 80–200.\n\n  * Std phổ biến trong khoảng 20–60.\n\n-> Biến ToF_std có phân tán lớn, cần kiểm soát để tránh ảnh hưởng mô hình.","metadata":{}},{"cell_type":"markdown","source":"**KẾT LUẬN TỔNG QUAN**","metadata":{}},{"cell_type":"markdown","source":"- Hầu hết các biến cảm biến đều xuất hiện tình trạng phân bố lệch phải, đặc biệt rõ ràng ở các biến như acc_mag_std, acc_mag_max, và các biến tof_std. Điều này cho thấy phần lớn giá trị tập trung ở mức thấp, nhưng vẫn tồn tại một số lượng nhỏ các giá trị rất cao, tạo ra đuôi dài về phía bên phải. Với những biến như vậy, việc áp dụng các kỹ thuật log-transform hoặc chuẩn hóa (standardization) là cần thiết để cải thiện khả năng học của mô hình và giảm thiểu ảnh hưởng tiêu cực từ skewed distribution.\n\n- Một điểm đáng lưu ý là sự xuất hiện nhiều giá trị ngoại lai (outliers), nhất là ở các biến như thm_3_mean, thm_4_std, tof_1_std, acc_mag_std. Những outlier này có thể gây nhiễu trong huấn luyện mô hình nếu không được xử lý đúng cách. Do đó, các kỹ thuật như IQR filtering, winsorization, hoặc clipping nên được xem xét trong giai đoạn tiền xử lý.\n\n- Về mặt phân bố, một số biến như rot_angle_mean hoặc thm_2_mean có phân bố gần chuẩn hoặc đối xứng, điều này cho thấy chúng có thể là những đặc trưng ổn định và đáng tin cậy để đưa vào mô hình học máy. Trong khi đó, nhiều biến std (độ lệch chuẩn) như thm_1_std, tof_2_std, mặc dù có giá trị trung bình thấp nhưng lại phân tán mạnh, phản ánh mức độ biến động lớn giữa các mẫu — điều này cũng là một nguồn thông tin hữu ích nếu được khai thác đúng cách.","metadata":{}},{"cell_type":"markdown","source":"## 3.2 Phân tích tính năng phân loại (Phân tích đơn biến)","metadata":{}},{"cell_type":"markdown","source":"- **Mục tiêu:** Vẽ biểu đồ tròn và biểu đồ đếm cho từng biến phân loại.\n\n- Các bước chính:\n\n  * Xác định các biến phân loại (trừ gesture)\n\n  * Tạo biến dataset = 'train' để phân biệt (dù chỉ dùng train)\n\n- Vẽ 2 biểu đồ cho mỗi biến:\n\n  * Biểu đồ tròn: hiển thị tỷ lệ phần trăm\n  * Biểu đồ đếm: hiển thị số lượng mẫu\n  * Gom nhóm những giá trị nhỏ hơn 5% vào nhãn \"Other\"","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\nimport pandas as pd\nimport textwrap\n\n# Xác định bảng màu\npie_chart_palette = ['#33638d', '#28ae80', '#d3eb0c', '#ff9a0b', '#7e03a8', '#35b779',\n                     '#fde725', '#440154', '#90d743', '#482173', '#22a884', '#f8961e']\ncustom_palette = ['#3498db']  # Only Train\n\n# 🔹Các biến phân loại để phân tích (không bao gồm 'cử chỉ')\ncategorical_variables = [col for col in categorical_tracker if col != 'gesture']\n\n#--- Chuẩn bị tập dữ liệu chính (chỉ đào tạo)\ntrain_main = train_df.copy()\ntrain_main['dataset'] = 'train'\nmain_combined = train_main.copy()\n\n# --- Chuẩn bị bộ dữ liệu nhân khẩu học (chỉ đào tạo)\ntrain_demo = train_dem_df.copy()\ntrain_demo['dataset'] = 'train'\ndemo_combined = train_demo.copy()\n\n#--- Chức năng vẽ đồ thị thống nhất\ndef create_categorical_plots(variable, data, source_name):\n    sns.set_style('whitegrid')\n    fig, axes = plt.subplots(1, 2, figsize=(16, 6))\n\n    # Biểu đồ tròn (bên trái)\n    plt.subplot(1, 2, 1)\n    value_counts = data[variable].value_counts()\n\n    # Thu gọn các danh mục nhỏ\n    threshold = 0.05 * value_counts.sum()\n    filtered_values = value_counts.copy()\n    filtered_values[value_counts < threshold] = 0\n    filtered_values = filtered_values[filtered_values > 0]\n    other_count = value_counts.sum() - filtered_values.sum()\n    if other_count > 0:\n        filtered_values['Other'] = other_count\n\n    wedges, texts, autotexts = plt.pie(\n        filtered_values,\n        autopct=lambda p: f'{p:.1f}%' if p > 5 else '',\n        colors=pie_chart_palette[:len(filtered_values)],\n        startangle=140,\n        wedgeprops=dict(width=0.3),\n        explode=[0.05 if p > 5 else 0 for p in filtered_values],\n        textprops={'fontsize': 10}\n    )\n\n    plt.title(\"\\n\".join(textwrap.wrap(f\"Pie Chart for {variable} — {source_name}\", width=50)))\n    plt.legend(filtered_values.index, loc=\"upper left\", bbox_to_anchor=(1, 1))\n\n    # Biểu đồ đếm ngang (bên phải)\n    plt.subplot(1, 2, 2)\n    sns.countplot(\n        data=data,\n        y=variable,\n        hue='dataset',  # vẫn còn, nhưng chỉ có \"tàu hỏa\"\n        palette=custom_palette,\n        alpha=0.85\n    )\n    plt.ylabel(variable)\n    plt.xlabel(\"Count\")\n    plt.title(\"\\n\".join(textwrap.wrap(f\"Countplot for {variable} — {source_name}\", width=50)))\n    plt.tight_layout()\n    plt.show()\n\n# --- Vẽ biểu đồ tất cả các biến phân loại từ tập dữ liệu chính (chỉ huấn luyện)\nfor var in categorical_variables:\n    if var in main_combined.columns:\n        create_categorical_plots(var, main_combined, \"Context Features\")\n\n# --- Vẽ biểu đồ tất cả các biến phân loại từ tập dữ liệu nhân khẩu học (chỉ đào tạo)\nfor var in categorical_variables:\n    if var in demo_combined.columns:\n        create_categorical_plots(var, demo_combined, \"Demographic Features\")\n\n# Cleanup\ntrain_main.drop('dataset', axis=1, inplace=True)\ntrain_demo.drop('dataset', axis=1, inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:29.721039Z","iopub.execute_input":"2025-08-07T08:08:29.721352Z","iopub.status.idle":"2025-08-07T08:08:37.190271Z","shell.execute_reply.started":"2025-08-07T08:08:29.721331Z","shell.execute_reply":"2025-08-07T08:08:37.189338Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ chi tiết**","metadata":{}},{"cell_type":"markdown","source":"**A. Context Features**","metadata":{}},{"cell_type":"markdown","source":"**phase:**\n- Transition: ~55.5%\n\n- Gesture: ~44.5%\n\n→ Transition nhiều hơn Gesture, tức dữ liệu hành vi có nhiều giai đoạn chuyển động hơn hành vi cố định.","metadata":{}},{"cell_type":"markdown","source":"**behavior:**\n- Performs gesture: 44.5%\n\n- Moves hand to target: 27.2%\n\n- Hand at target: 16.6%\n\n- Relaxes and moves: 11.7%\n\n→ Có sự mất cân bằng nhẹ. Hành vi \"thực hiện cử chỉ\" chiếm tỷ trọng cao nhất, quan trọng trong phân loại hành vi.","metadata":{}},{"cell_type":"markdown","source":"**orientation:**\n- Gồm 4 tư thế, gần như cân bằng:\n\n  * Seated Straight: 32.7%\n\n  * 3 tư thế còn lại chiếm ~22% mỗi loại\n\n→ Tư thế \"Ngồi thẳng\" chiếm ưu thế, nhưng các tư thế còn lại phân bố khá đều, giúp tránh bias theo orientation.","metadata":{}},{"cell_type":"markdown","source":"**sequence_type:**\n- Target: 59.8%\n\n- Non-target: 40.2%\n\n→ Dữ liệu có xu hướng ưu tiên các hành vi mục tiêu, giúp mô hình học trọng tâm vào các hành vi quan trọng.","metadata":{}},{"cell_type":"markdown","source":"**B. Demographic Features**","metadata":{}},{"cell_type":"markdown","source":"**adult_child:**\n- 1 (Adult): 51.9%\n\n- 0 (Child): 48.1%\n\n→ Phân bố khá đều, giúp kiểm soát ảnh hưởng độ tuổi đến mô hình.","metadata":{}},{"cell_type":"markdown","source":"**sex:**\n- 1 (Male): 61.7%\n\n- 0 (Female): 38.3%\n\n→ Thiên lệch giới tính, cần cân nhắc khi đánh giá công bằng mô hình.","metadata":{}},{"cell_type":"markdown","source":"**handedness:**\n- 1 (Right-handed): 87.7%\n\n- 0 (Left-handed): 12.3%\n\n→ Thiên lệch mạnh về thuận tay phải, có thể ảnh hưởng kết quả nếu hành vi phụ thuộc tay thuận.","metadata":{}},{"cell_type":"markdown","source":"**Nhận xét**\n\nDữ liệu của **Context Features** khá đa dạng và tương đối cân bằng giữa các loại orientation và phase. Performs gesture chiếm ưu thế.\n\n**Demographics Features** Có một số thiên lệch cần lưu ý: giới tính (male nhiều hơn), tay thuận (đa số thuận tay phải). Cần kiểm soát biến hoặc cân bằng mẫu khi train.","metadata":{}},{"cell_type":"markdown","source":"## 3.3 Target Feature Analysis (Univariate Analysis)","metadata":{}},{"cell_type":"markdown","source":"**Mục tiêu**\nTrực quan hóa biến mục tiêu gesture trong tập huấn luyện bằng biểu đồ tròn và biểu đồ cột, nhằm kiểm tra phân bố dữ liệu.","metadata":{}},{"cell_type":"markdown","source":"**1. Tạo bản sao dữ liệu huấn luyện (train_main) và thêm cột dataset = 'train' để đánh dấu.**\n\n**2. Hàm create_target_plots(...):**\n\n- Vẽ biểu đồ tròn (pie chart): Thể hiện tỷ lệ phần trăm từng hành vi.\n\n  * Gộp các nhãn chiếm <5% vào nhóm \"Other\" để làm gọn biểu đồ.\n\n- Vẽ biểu đồ cột ngang (countplot): Thể hiện số lượng mẫu cho từng hành vi.\n\n**3. Vòng lặp duyệt qua biến mục tiêu (gesture) và gọi hàm trực quan.**\n\n**4. Xoá cột phụ dataset sau khi hoàn tất.**","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\nimport pandas as pd\nimport textwrap\n\n# Xác định bảng màu\npie_chart_palette = ['#33638d', '#28ae80', '#d3eb0c', '#ff9a0b', '#7e03a8', '#35b779',\n                     '#fde725', '#440154', '#90d743', '#482173', '#22a884', '#f8961e']\n\n# Xác định cột mục tiêu\ntarget_columns = ['gesture']\n# Chuẩn bị dữ liệu chính (CHỈ tập huấn luyện)\ntrain_main = train_df.copy()\ntrain_main['dataset'] = 'train'  # giữ lại nếu bạn muốn thêm lại logic sau\n\n# Hàm vẽ đồ thị thống nhất cho các biến mục tiêu (chỉ đào tạo)\ndef create_target_plots(variable, data, source_name):\n    sns.set_style('whitegrid')\n    fig, axes = plt.subplots(1, 2, figsize=(16, 6))\n\n    # Pie Chart (left)\n    plt.subplot(1, 2, 1)\n    value_counts = data[variable].value_counts()\n\n    # Thu gọn các danh mục nhỏ thành 'Khác'\n    threshold = 0.05 * value_counts.sum()\n    filtered_values = value_counts.copy()\n    filtered_values[value_counts < threshold] = 0\n    filtered_values = filtered_values[filtered_values > 0]\n    other_count = value_counts.sum() - filtered_values.sum()\n    if other_count > 0:\n        filtered_values['Other'] = other_count\n\n    wedges, texts, autotexts = plt.pie(\n        filtered_values,\n        autopct=lambda p: f'{p:.1f}%' if p > 5 else '',\n        colors=pie_chart_palette[:len(filtered_values)],\n        startangle=140,\n        wedgeprops=dict(width=0.3),\n        explode=[0.05 if p > 5 else 0 for p in filtered_values],\n        textprops={'fontsize': 10}\n    )\n\n    plt.title(\"\\n\".join(textwrap.wrap(f\"Pie Chart for {variable} — {source_name}\", width=50)))\n    plt.legend(filtered_values.index, loc=\"upper left\", bbox_to_anchor=(1, 1))\n\n    # Biểu đồ đếm ngang (bên phải)\n    plt.subplot(1, 2, 2)\n    sns.countplot(\n        data=data,\n        y=variable,\n        color='#3498db',  # just use a single color (train)\n        alpha=0.85\n    )\n    plt.ylabel(variable)\n    plt.xlabel(\"Count\")\n    plt.title(\"\\n\".join(textwrap.wrap(f\"Countplot for {variable} — {source_name}\", width=50)))\n    plt.tight_layout()\n    plt.show()\n\n# Vẽ cột mục tiêu (chỉ đào tạo)\nfor var in target_columns:\n    if var in train_main.columns:\n        create_target_plots(var, train_main, \"Target Variable\")\n\n# Cleanup\ntrain_main.drop('dataset', axis=1, inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:37.191399Z","iopub.execute_input":"2025-08-07T08:08:37.191628Z","iopub.status.idle":"2025-08-07T08:08:39.379976Z","shell.execute_reply.started":"2025-08-07T08:08:37.191609Z","shell.execute_reply":"2025-08-07T08:08:39.378987Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**PHÂN TÍCH BIỂU ĐỒ**","metadata":{}},{"cell_type":"markdown","source":"**Biểu đồ tròn (Pie chart):** Hiển thị tỷ lệ phần trăm của từng hành vi (gesture) trong tập huấn luyện:\n\n- Nhãn lớn nhất: \"Text on phone\" (~13.2%)\n\n- Theo sau là: \"Neck - scratch\", \"Eyebrow - pull hair\"\n\n- Những gesture như \"Wave hello\", \"Write name in air\", \"Forehead - scratch\", v.v. có tỷ lệ 5–7%.\n\n- \"Other\" chiếm ~13.2% (gộp các lớp dưới 5%).","metadata":{}},{"cell_type":"markdown","source":"**Biểu đồ cột (Countplot):** Hiển thị số lượng mẫu của từng lớp gesture:\n\n- Hai lớp chiếm nhiều nhất:\n\n  * \"Text on phone\" (~57,000 mẫu)\n\n  * \"Neck - scratch\" (~56,000 mẫu)\n\n  * Các nhãn như \"Write name on leg\", \"Pinch knee/leg skin\", \"Glasses on/off\" chỉ có khoảng ~10,000 mẫu.\n\n  * Một số gesture xuất hiện hiếm: \"Feel around in tray\", \"Drink from bottle\", \"Scratch knee\", ...\n \n ->  Sự mất cân bằng dữ liệu thể hiện rõ: Nhãn như \"Text on phone\" và \"Neck - scratch\" có số mẫu gần gấp 5 lần so với các gesture ít phổ biến như \"Write name on leg\", \"Glasses on/off\", v.v.","metadata":{}},{"cell_type":"markdown","source":"## 3.4 Mối quan hệ tính năng đa biến","metadata":{}},{"cell_type":"markdown","source":"### 3.4.1 Phân tích tính năng cảm biến cho tất cả cử chỉ (Chủ thể ngẫu nhiên đơn lẻ)","metadata":{}},{"cell_type":"code","source":"#fix code dòng dưới\nsubject_id = train_dem_df['subject'].sample(1).iloc[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:39.381126Z","iopub.execute_input":"2025-08-07T08:08:39.381471Z","iopub.status.idle":"2025-08-07T08:08:39.386273Z","shell.execute_reply.started":"2025-08-07T08:08:39.381442Z","shell.execute_reply":"2025-08-07T08:08:39.385508Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"subj_info = train_dem_df[train_dem_df['subject'] == subject_id].iloc[0]\n\nprint(f\"⏩ Details of the random subject being considered\")\nprint(f\"Subject ID:              {subject_id}\")\nprint(f\"  • Age:                 {subj_info['age']} years\")\nprint(f\"  • Adult/Child:         {'Adult' if subj_info['adult_child']==1 else 'Child'}\")\nprint(f\"  • Sex:                 {'Male' if subj_info['sex']==1 else 'Female'}\")\nprint(f\"  • Handedness:          {'Right-handed' if subj_info['handedness']==1 else 'Left-handed'}\")\nprint(f\"  • Height:              {subj_info['height_cm']} cm\")\nprint(f\"  • Shoulder→Wrist:      {subj_info['shoulder_to_wrist_cm']} cm\")\nprint(f\"  • Elbow→Wrist:         {subj_info['elbow_to_wrist_cm']} cm\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:39.387397Z","iopub.execute_input":"2025-08-07T08:08:39.387652Z","iopub.status.idle":"2025-08-07T08:08:39.408911Z","shell.execute_reply.started":"2025-08-07T08:08:39.387632Z","shell.execute_reply":"2025-08-07T08:08:39.408109Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Đoạn code thực hiện việc lọc và hiển thị thông tin của một người cụ thể trong tập dữ liệu nhân khẩu học train_dem_df.\n- Biến subj_info chứa thông tin của người có mã định danh subject_id.\n- Sau đó, code in ra các thông tin như: tuổi, giới tính, độ tuổi (người lớn hay trẻ em), tay thuận, chiều cao, khoảng cách từ vai đến cổ tay, và từ khuỷu tay đến cổ tay.","metadata":{}},{"cell_type":"markdown","source":"- Người được chọn có mã SUBJ_017807, năm nay 36 tuổi, là người lớn và nam giới.\n- Thuận tay phải, điều này phù hợp với phần lớn mẫu trong bộ dữ liệu (vì hơn 87% là người thuận tay phải).\n- Chiều cao là 170 cm, nằm trong khoảng trung bình, không quá cao hay quá thấp.\n- Khoảng cách từ vai đến cổ tay là 56 cm, và từ khuỷu tay đến cổ tay là 27 cm.\n- Tỷ lệ này là hợp lý về mặt sinh học, cho thấy các đặc điểm hình thể của người này rất phổ biến và phù hợp cho việc phân tích cử chỉ hoặc hành vi bằng cảm biến.\n\n-> Nhìn chung, đây là một mẫu đại diện điển hình trong tập dữ liệu, với các đặc điểm thường gặp và cân đối. Nếu dùng để minh họa hoặc phân tích sâu thì khá hợp lý, vì nó không có đặc điểm cực đoan hoặc hiếm gặp nào.","metadata":{}},{"cell_type":"markdown","source":"#### ⏩ 1. ** BIỂU ĐỒ IMU**\n#### **📌 Hướng dẫn giải thích**\n   * Độ lớn gia tốc (`acc_mag`): cho biết cổ tay di chuyển “mạnh” như thế nào. Các đỉnh thường tương ứng với chuyển động tay nhanh.\n   * Góc xoay (`rot_angle`): đại diện cho mức độ cổ tay bị vặn lệch so với tư thế trung tính ban đầu. Những cú nhảy đột ngột có thể cho thấy các động tác lật cổ tay nhanh.\n   * Vùng được tô xám “Transition” thể hiện khoảng thời gian tay đang di chuyển tới mục tiêu thực hiện cử chỉ; vùng màu đỏ “Gesture” thể hiện khoảng thời gian tiếp xúc thực sự hoặc động tác chính diễn ra.\n   * So sánh hai đường biểu diễn này trong từng chuỗi dữ liệu giúp phân biệt, ví dụ, một hành động BFRB như giật tóc (gia tốc cao kèm góc xoay đột ngột) với một chuyển động nhẹ nhàng không phải BFRB.","metadata":{}},{"cell_type":"markdown","source":"**Tính toán đặc trưng IMU gồm:**\n\n- acc_mag: độ lớn tổng hợp từ 3 trục gia tốc (acc_x, acc_y, acc_z)\n\n- rot_angle: góc quay từ quaternion rot_w (góc định hướng)\n\n**Vẽ biểu đồ chuỗi thời gian cho từng chuỗi sequence_id đại diện cho mỗi gesture:**\n\n- Hai đường: acc_mag (màu xanh), rot_angle (màu cam)\n\n- Tô màu nền theo phase:\n\n  * Xám nhạt = \"Transition\"\n\n  * Đỏ nhạt = \"Gesture\"\n\n- Mỗi biểu đồ phụ hiển thị 1 chuỗi hành vi (target hoặc non-target) của 1 subject duy nhất.","metadata":{}},{"cell_type":"code","source":"# ─── Ô 1: Biểu đồ chuỗi thời gian IMU ──────────────────────────────────────────────────────── ─────────────────────────────────────────\n# Ô này tính toán độ lớn gia tốc (√(acc_x²+acc_y²+acc_z²)) và góc quay (θ = 2·arccos(rot_w))\n# cho mỗi dấu thời gian trong chuỗi của đối tượng đã chọn. Sau đó, nó vẽ hai đường thẳng cho mỗi chuỗi cử chỉ:\n# • acc_mag (độ lớn gia tốc)\n# • rot_angle (góc định hướng)\n# Nền được tô bóng theo ‘pha’: “Chuyển tiếp” so với “Cử chỉ” với các màu khác nhau.\n# Mỗi tiêu đề biểu đồ phụ hiển thị tên cử chỉ và sequence_type của biểu đồ đó. Mỗi hàng có hai biểu đồ.\n\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# Chọn một chủ đề (ví dụ: đầu tiên trong danh sách)\nsubject_id = train_df['subject'].unique()[0]\nsubj_df = train_df[train_df['subject'] == subject_id].copy()\n\n# Tính toán các tính năng IMU được suy ra\nsubj_df['acc_mag'] = np.sqrt(\n    subj_df['acc_x']**2 + subj_df['acc_y']**2 + subj_df['acc_z']**2\n)\nsubj_df['rot_w_clipped'] = subj_df['rot_w'].clip(-1, 1)\nsubj_df['rot_angle'] = 2 * np.arccos(subj_df['rot_w_clipped'])\n\n# Chọn một sequence_id cho mỗi nhãn cử chỉ duy nhất\n# Nhóm theo cột 'cử chỉ' được mã hóa theo số nguyên, sau đó lấy sequence_id đầu tiên trong mỗi nhóm\ngesture_to_seq = subj_df.groupby('gesture')['sequence_id'].first().to_dict()\n# Chuyển đổi thành danh sách sequence_ids\nseq_ids = list(gesture_to_seq.values())\n\nn = len(seq_ids)\nncols = 2\nnrows = int(np.ceil(n / ncols))\n\nfig, axes = plt.subplots(nrows, ncols, figsize=(14, 4 * nrows), sharex=False, sharey=False)\naxes = axes.flatten()\n\nfor i, seq in enumerate(seq_ids):\n    ax = axes[i]\n    seq_df = subj_df[subj_df['sequence_id'] == seq].sort_values('sequence_counter')\n    times = seq_df['sequence_counter']\n\n    # Vẽ hai đường IMU đã suy ra\n    ax.plot(times, seq_df['acc_mag'],\n            label='Acceleration Magnitude', color='tab:blue', linewidth=1.5)\n    ax.plot(times, seq_df['rot_angle'],\n            label='Rotation Angle (rad)', color='tab:orange', linewidth=1.5)\n\n    # Tô bóng theo giai đoạn: “Chuyển tiếp” so với “Cử chỉ”\n    used_labels = set()\n    for phase_label, color in [('Transition', 'lightgray'),\n                               ('Gesture', 'lightcoral')]:\n        mask = seq_df['phase'] == phase_label\n        if mask.any():\n            # Tìm các khoảng liên tiếp trong đó pha == nhãn pha\n            idxs = seq_df.index[mask]\n            breaks = np.where(np.diff(idxs) != 1)[0]\n            spans = []\n            start_idx = idxs[0]\n            for b in breaks:\n                end_idx = idxs[b]\n                spans.append((start_idx, end_idx))\n                start_idx = idxs[b + 1]\n            spans.append((start_idx, idxs[-1]))\n            # Vẽ từng khoảng; chỉ ghi nhãn một lần cho mỗi pha trong sơ đồ phụ này\n            for (start_i, end_i) in spans:\n                t0 = seq_df.loc[start_i, 'sequence_counter']\n                t1 = seq_df.loc[end_i, 'sequence_counter']\n                label_arg = phase_label if phase_label not in used_labels else None\n                ax.axvspan(t0, t1, color=color, alpha=0.3, label=label_arg)\n                used_labels.add(phase_label)\n\n    # Tiêu đề và nhãn của cốt truyện phụ\n    gesture_name = seq_df['gesture'].iloc[0]\n    seq_type = seq_df['sequence_type'].iloc[0]\n    ax.set_title(f\"Subject {subject_id} – Seq {seq} – {gesture_name} ({seq_type})\",\n                 fontsize=10)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Value\", fontsize=9)\n    ax.legend(loc='upper right', fontsize='small')\n    ax.grid(True)\n\n# Tắt bất kỳ ô phụ trống nào\nfor j in range(i + 1, nrows * ncols):\n    fig.delaxes(axes[j])\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:39.409973Z","iopub.execute_input":"2025-08-07T08:08:39.410224Z","iopub.status.idle":"2025-08-07T08:08:45.138274Z","shell.execute_reply.started":"2025-08-07T08:08:39.410204Z","shell.execute_reply":"2025-08-07T08:08:45.136990Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Target Gestures (kéo tóc, cào gãi, nhéo da…)**\n- Rotation Angle tăng rõ rệt trong vùng Gesture\n→ Đây là dấu hiệu đặc trưng giúp phân biệt các hành vi nhắm mục tiêu.\n\n- Acceleration Magnitude dao động ổn định, thường ở mức cao (~10–12)\n→ Động tác thường nhanh và dứt khoát.\n\n- Các hành vi như:\n\n  * Above ear – pull hair\n\n  * Neck – scratch\n\n  * Eyebrow – pull hair\n→ đều có vùng đỏ (Gesture) thể hiện sự thay đổi góc quay đáng kể, cùng với gia tốc giữ mức cao → biểu hiện rõ ràng của target gesture.","metadata":{}},{"cell_type":"markdown","source":"**Non-Target Gestures (uống nước, chạm điện thoại, vẫy tay…)**\n- Rotation Angle và Acceleration ổn định hoặc nhiễu nhẹ, ít thay đổi\n→ Các hành vi thường lặp lại nhỏ hoặc không có động tác quay mạnh\n\nVí dụ: \n\n  * Wave hello: dao động mạnh về gia tốc nhưng góc quay rất nhỏ\n  * Write name on leg: khá ổn định, không có đột biến\n→ Nhận diện được sự khác biệt về đặc trưng chuyển động","metadata":{}},{"cell_type":"markdown","source":"**Một số Target khó phân biệt:** Forehead – pull hairline và Cheek – pinch skin có góc quay tương đối nhỏ → dễ gây nhầm lẫn với non-target","metadata":{}},{"cell_type":"markdown","source":"**Nhận xét**\n- Khi quan sát các biểu đồ chuỗi thời gian, có thể nhận thấy rằng các hành vi target gesture như kéo tóc, nhéo da hay cào gãi đều thể hiện rõ những đặc điểm chuyển động đặc trưng. Cụ thể, góc quay (rotation angle) thường có xu hướng tăng đáng kể trong giai đoạn thực hiện cử chỉ (Gesture), phản ánh sự xoay cổ tay hay cử động đột ngột đặc trưng cho các hành vi nhắm mục tiêu.\n\n- Trong khi đó, độ lớn gia tốc (acceleration magnitude) ở các target gesture thường giữ mức ổn định cao, dao động trong khoảng từ 10 đến 12, và có thể xuất hiện vài đỉnh nhọn. Điều này cho thấy các hành vi này được thực hiện với lực và tốc độ rõ rệt, mang tính lặp lại tương đối nhất quán.\n\n- Ngược lại, đối với các non-target gesture như uống nước, vẫy tay, viết tên hay chạm điện thoại, các đặc trưng chuyển động không cho thấy sự biến động mạnh mẽ. Góc quay thường duy trì ở mức thấp và ổn định, trong khi độ lớn gia tốc có thể dao động mạnh nhưng lại thiếu xu hướng cụ thể. Một số hành vi như “wave hello” có thể gây nhiễu do dao động gia tốc lớn nhưng không kèm theo sự thay đổi rõ rệt về góc quay, khiến việc phân biệt có thể gặp khó khăn nếu chỉ dựa trên một đặc trưng đơn lẻ.\n\n- Ngoài ra, có một vài target gesture tỏ ra khó phân biệt vì dấu hiệu động học chưa thật sự rõ ràng, chẳng hạn như “pull hairline” hay “pinch skin”. Những hành vi này có thể dễ bị nhầm lẫn với các cử chỉ không nhắm mục tiêu, đặc biệt nếu không kết hợp cả hai đặc trưng gia tốc và góc quay trong phân tích.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\nNhìn chung, các biểu đồ minh họa cho thấy rằng sự kết hợp giữa độ lớn gia tốc và góc quay là rất quan trọng để nhận diện chính xác các hành vi nhắm mục tiêu (target gestures). Các pha Gesture thường có đặc trưng rõ rệt về tăng góc quay và duy trì gia tốc cao, trong khi non-target gesture lại thiếu các dấu hiệu nhất quán này. Tuy nhiên, để cải thiện khả năng phân loại, đặc biệt trong những trường hợp khó phân biệt, có thể cần tích hợp thêm các đặc trưng phụ như vận tốc góc, độ giật (jerk), tần suất hoặc sử dụng mô hình học sâu để học biểu hiện phức tạp hơn từ dữ liệu.","metadata":{}},{"cell_type":"markdown","source":"#### ⏩ 2. ** Biểu đồ Thermopile**\n#### **📌  Hướng dẫn phân tích**\n* Mỗi đường cong `thm_i` đại diện cho nhiệt độ gần da đo được từ 5 cảm biến. Khi giá trị thm_i tăng lên, điều đó cho thấy cổ tay đang tiến gần đến một bộ phận ấm trên cơ thể (ví dụ: mặt, cổ).\n\n* Đường đậm “`Average Temperature`” là trung bình của cả 5 cảm biến, giúp làm rõ các sự kiện tiếp xúc (đỉnh nhiệt độ tăng) so với các giai đoạn ở xa (vùng nhiệt độ thấp, phẳng).\n\n* Vùng được tô màu theo “Transition” và “Gesture” giúp xác nhận rằng sự gia tăng nhiệt độ chủ yếu diễn ra trong giai đoạn “Gesture” (tức là khi có tiếp xúc thực sự).","metadata":{}},{"cell_type":"markdown","source":"thm_cols: là danh sách các cột nhiệt độ từ thm_1 đến thm_5.\n\ntherm_df['thm_mean']: tính nhiệt độ trung bình tại mỗi thời điểm từ 5 cảm biến.\n\nVòng lặp qua seq_ids: với mỗi chuỗi, lọc dữ liệu tương ứng, sắp xếp theo thời gian rồi vẽ biểu đồ:\n\nVẽ đường nhiệt độ từng cảm biến.\n\nVẽ thêm đường nhiệt độ trung bình màu đen.\n\nTô màu vùng \"Transition\" (xám) và vùng \"Gesture\" (đỏ nhạt) dựa trên cột phase.\n\nTiêu đề từng subplot gồm: Subject ID, Sequence ID, tên hành vi và loại chuỗi (Target hay Non-Target).","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\n# Sử dụng cùng subject_id và seq_ids (một cho mỗi cử chỉ duy nhất) từ Ô 1\ntherm_df = subj_df.copy()\n\n# Thu thập các cột nhiệt điện và tính toán giá trị trung bình theo dấu thời gian của chúng\nthm_cols = [f'thm_{i}' for i in range(1, 6)]\ntherm_df['thm_mean'] = therm_df[thm_cols].mean(axis=1)\n\nfig, axes = plt.subplots(nrows, ncols, figsize=(14, 4 * nrows), sharex=False, sharey=False)\naxes = axes.flatten()\n\nfor i, seq in enumerate(seq_ids):\n    ax = axes[i]\n    seq_thm = therm_df[therm_df['sequence_id'] == seq].sort_values('sequence_counter')\n    times = seq_thm['sequence_counter']\n    # Xác định phạm vi y để tô bóng (không nhất thiết phải có vì axvspan trải dài toàn bộ trục y)\n    # Vẽ từng kênh nhiệt điện\n    for col in thm_cols:\n        ax.plot(times, seq_thm[col], label=col, linewidth=1)\n    # Vẽ giá trị trung bình dưới dạng đường màu đen đậm\n    ax.plot(times, seq_thm['thm_mean'], label='Average Temperature', color='black', linewidth=2.5)\n    # Tô bóng theo pha bằng axvspan\n    for phase_label, color in [('Transition', 'lightgray'), ('Gesture', 'lightcoral')]:\n        mask = seq_thm['phase'] == phase_label\n        if mask.any():\n            idxs = seq_thm.index[mask]\n            breaks = np.where(np.diff(idxs) != 1)[0]\n            spans = []\n            start_idx = idxs[0]\n            for b in breaks:\n                end_idx = idxs[b]\n                spans.append((start_idx, end_idx))\n                start_idx = idxs[b + 1]\n            spans.append((start_idx, idxs[-1]))\n            for (start_i, end_i) in spans:\n                t0 = seq_thm.loc[start_i, 'sequence_counter']\n                t1 = seq_thm.loc[end_i, 'sequence_counter']\n                ax.axvspan(t0, t1, color=color, alpha=0.3, label=phase_label)\n    # Nhãn và chú thích cho cốt truyện phụ này\n    gesture_name = seq_thm['gesture'].iloc[0]\n    seq_type = seq_thm['sequence_type'].iloc[0]\n    ax.set_title(f\"Subject {subject_id} – Seq {seq} – {gesture_name} ({seq_type})\", fontsize=10)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Temperature (°C)\", fontsize=9)\n    ax.legend(loc='upper right', fontsize='small')\n    ax.grid(True)\n\n# Tắt bất kỳ phần phụ nào không sử dụng\nfor j in range(i + 1, nrows * ncols):\n    fig.delaxes(axes[j])\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:45.139311Z","iopub.execute_input":"2025-08-07T08:08:45.139598Z","iopub.status.idle":"2025-08-07T08:08:51.606676Z","shell.execute_reply.started":"2025-08-07T08:08:45.139577Z","shell.execute_reply":"2025-08-07T08:08:51.605395Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích kết quả biểu đồ:**","metadata":{}},{"cell_type":"markdown","source":"**1. Tín hiệu nhiệt phản ứng rõ với hành vi \"Gesture\" (Target)**\n- Trong các biểu đồ \"Target\", đặc biệt như: \"Forehead - scratch\", \"Eyebrow - pull hair\", \"Neck - scratch\", nhiệt độ trung bình thường giảm mạnh trước hoặc trong giai đoạn Gesture, sau đó phục hồi dần.\n\n- Điều này cho thấy các cảm biến nhiệt có thể ghi nhận biến động đặc trưng khi xảy ra hành vi BFRB.","metadata":{}},{"cell_type":"markdown","source":"**2. Sự khác biệt giữa Target và Non-Target**\n- Ở các biểu đồ Non-Target (như \"Text on phone\", \"Drink from cup\", \"Wave hello\", \"Write name in air\") nhiệt độ thường dao động nhẹ, hoặc không có sự biến thiên rõ rệt, ít thay đổi khi bước vào pha Gesture.\n\n=> Phản ứng nhiệt ít đặc trưng hơn so với hành vi Target, cho thấy tiềm năng phân loại dựa vào nhiệt.","metadata":{}},{"cell_type":"markdown","source":"**3.Vai trò của từng cảm biến (thm_1 → thm_5)**\n- Một số cảm biến như thm_1 và thm_2 thường có dao động rõ hơn ở nhiều chuỗi.\n\n- Có trường hợp như thm_5 khá ổn định, cho thấy có thể ít đóng vai trò trong nhận dạng hành vi cụ thể.\n\n--> Tuy nhiên, thm_mean (nhiệt độ trung bình) thường giữ được xu hướng rõ ràng hơn so với từng cảm biến riêng lẻ → quan trọng trong trích xuất đặc trưng.","metadata":{}},{"cell_type":"markdown","source":"**4. Khoảng thời gian \"Transition\"**\n- Trước khi vào pha Gesture (màu xám), nhiều chuỗi đã bắt đầu có sự thay đổi nhiệt độ.\n\n- Việc sử dụng cả \"Transition\" và \"Gesture\" có thể cải thiện độ chính xác mô hình.","metadata":{}},{"cell_type":"markdown","source":"#### ⏩ 3. **ToF Plots**\n#### **📌 Interpretation Guide**\n   * Mỗi giá trị `tof_i_mean` là trung bình của lưới cảm biến 8×8, thể hiện mức độ “gần” của cổ tay so với một vật thể (ví dụ: da). Giá trị thấp (\\~0–50) → rất gần; giá trị cao hơn (\\~100–200) → xa hơn; giá trị –1 (bị che/mất tín hiệu) → không có tín hiệu phản hồi.\n   * Theo dõi giá trị trung bình của cả năm cảm biến theo thời gian giúp xác định thời điểm tay tiến lại gần (giá trị đột ngột giảm) và khi tay rời xa.\n   * Phần tô màu theo “Transition” và “Gesture” xác nhận rằng khoảng cách thấp nhất trùng với thời điểm tiếp xúc thực sự (diễn ra trong giai đoạn “Gesture”).","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\n# Sử dụng cùng subject_id và seq_ids (một cho mỗi cử chỉ duy nhất) từ Ô 1\ntof_df = subj_df.copy()\n\n# Xác định 64 cột cho mỗi cảm biến ToF và tính giá trị trung bình theo dấu thời gian của chúng\nmean_cols = []\nfor i_sensor in range(1, 6):\n    pixel_cols = [f'tof_{i_sensor}_v{pix}' for pix in range(64)]\n    tof_df[f'tof_{i_sensor}_mean'] = tof_df[pixel_cols].replace(-1, np.nan).mean(axis=1)\n    mean_cols.append(f'tof_{i_sensor}_mean')\n\nfig, axes = plt.subplots(nrows, ncols, figsize=(14, 4 * nrows), sharex=False, sharey=False)\naxes = axes.flatten()\n\nfor i, seq in enumerate(seq_ids):\n    ax = axes[i]\n    seq_tof = tof_df[tof_df['sequence_id'] == seq].sort_values('sequence_counter')\n    times = seq_tof['sequence_counter']\n\n    # Vẽ giá trị trung bình của từng cảm biến ToF\n    for col in mean_cols:\n        ax.plot(times, seq_tof[col], label=col, linewidth=1)\n\n    # Tô bóng theo pha bằng axvspan\n    for phase_label, color in [('Transition', 'lightgray'), ('Gesture', 'lightcoral')]:\n        mask = seq_tof['phase'] == phase_label\n        if mask.any():\n            idxs = seq_tof.index[mask]\n            breaks = np.where(np.diff(idxs) != 1)[0]\n            spans = []\n            start_idx = idxs[0]\n            for b in breaks:\n                end_idx = idxs[b]\n                spans.append((start_idx, end_idx))\n                start_idx = idxs[b + 1]\n            spans.append((start_idx, idxs[-1]))\n            for (start_i, end_i) in spans:\n                t0 = seq_tof.loc[start_i, 'sequence_counter']\n                t1 = seq_tof.loc[end_i, 'sequence_counter']\n                ax.axvspan(t0, t1, color=color, alpha=0.3, label=phase_label)\n\n    # Nhãn và chú thích cho cốt truyện phụ này\n    gesture_name = seq_tof['gesture'].iloc[0]\n    seq_type     = seq_tof['sequence_type'].iloc[0]\n    ax.set_title(f\"Subject {subject_id} – Seq {seq} – {gesture_name} ({seq_type})\", fontsize=10)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Mean ToF Distance\", fontsize=9)\n    ax.legend(loc='upper right', fontsize='small')\n    ax.grid(True)\n\n# Tắt bất kỳ phần phụ nào không sử dụng\nfor j in range(i + 1, nrows * ncols):\n    fig.delaxes(axes[j])\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:51.608073Z","iopub.execute_input":"2025-08-07T08:08:51.608541Z","iopub.status.idle":"2025-08-07T08:08:57.579020Z","shell.execute_reply.started":"2025-08-07T08:08:51.608505Z","shell.execute_reply":"2025-08-07T08:08:57.577958Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**1. Above ear - pull hair (Target)**\n- Trong pha Gesture, các cảm biến có xu hướng giảm đều về giá trị, đặc biệt tof_4 và tof_5.\n\n- Cảm biến tof_3 lại tăng mạnh ở giữa pha Gesture → Tín hiệu rõ ràng và có pattern đặc trưng.\n\n**2. Cheek - pinch skin (Target)**\n- Biểu đồ cho thấy rõ sự giảm mạnh ngay khi vào pha Gesture, đặc biệt tof_1, tof_2 và tof_4.\n\n- Cảm biến tof_5 giữ nguyên ở mức thấp → thể hiện mức độ ổn định sau hành động.\n\n**3. Drink from bottle/cup (Non-Target)**\n- Tín hiệu biến thiên phức tạp, không ổn định.\n\n- Không có cảm biến nào thể hiện xu hướng rõ ràng → khó phân biệt pha Gesture.\n\n**4. Eyebrow - pull hair (Target)**\n- Biểu đồ có sự tăng đồng loạt ở nhiều cảm biến khi vào Gesture.\n\n- tof_1 và tof_2 có biên độ dao động rõ rệt → dễ học cho mô hình.\n\n**5. Eyelash - pull hair (Target)**\n- tof_5 tăng vọt ở cuối Gesture, các cảm biến còn lại khá ổn định.\n\n- Có thể là hành động ít di chuyển, chỉ ảnh hưởng đến 1 cảm biến → nên chú ý cảm biến trọng yếu.\n\n**6. Feel around in tray (Non-Target)**\n- Biến động liên tục, đặc biệt là tof_3 và tof_5 → không có giai đoạn ổn định.\n\n- Pha Transition và Gesture khó phân biệt bằng mắt thường.\n\n**7. Forehead - pull hairline (Target)**\n- Tăng rõ rệt ở pha Transition, rồi giữ nguyên cao ở Gesture.\n\n- tof_2 và tof_5 rất ổn định → pattern tốt.\n\n**8. Forehead - scratch (Target)**\n- tof_4 giảm, tof_3 và tof_2 tăng nhẹ → có pattern ổn định.\n\n- Một số dao động nhẹ nhưng tổng thể có thể học được.\n\n**9. Glasses on/off (Non-Target)**\n- Dao động mạnh ở tất cả cảm biến, không có xu hướng rõ ràng.\n\n- Thậm chí trong Gesture có tới 3 phase phụ → nhiễu cao, khó dùng.\n\n**10. Neck - pinch skin (Target)**\n- tof_2 giảm mạnh trong Gesture, tof_1 và tof_4 có dao động nhẹ.\n\n- Có pattern phân biệt rõ ràng giữa Transition và Gesture.\n\n**11. Neck - scratch (Target)**\n- tof_5 tăng đều trong Gesture, trong khi các cảm biến khác ổn định.\n\n- Pattern rất rõ ràng, đặc biệt cho tof_5.\n\n**12. Pinch knee/leg skin (Non-Target)**\n- Các cảm biến dao động cùng lúc nhưng không nhất quán.\n\n- Không có đặc trưng cụ thể → mô hình sẽ gặp khó khăn.\n\n**13. Pull air toward your face (Non-Target)**\n- Dao động bất thường, tof_5 tụt hẳn về 0 gần cuối Gesture.\n\n- Pattern quá hỗn loạn → khó học.\n\n**14. Scratch knee/leg skin (Non-Target)**\n- Các cảm biến đồng loạt giảm, có vẻ như pattern → nhưng pha Gesture vẫn còn dao động mạnh.\n\n- Có thể gây nhầm lẫn với Target nếu không kiểm soát kỹ.\n\n**15. Text on phone (Non-Target)**\n- Dao động nhỏ, tất cả cảm biến đều gần như song song.\n\n- Không có hành vi đặc trưng → dễ phân biệt với Target.\n\n**16. Wave hello (Non-Target)**\n- tof_1 giảm mạnh rồi tăng, các cảm biến còn lại khá ổn định.\n\n- Mặc dù có pattern nhưng không rõ như Target → cần thêm context.\n\n**17. Write name in air (Non-Target)**\n- tof_3 và tof_4 tăng nhanh trong Gesture, nhưng cảm biến còn lại khá ổn định.\n\n- Có vẻ là cử chỉ có độ lặp lại cao.\n\n**18. Write name on leg (Non-Target)**\n- tof_1 tăng vọt đầu pha Gesture rồi giảm nhanh.\n\n- Các cảm biến còn lại khá ổn định → có pattern riêng, nhưng không mạnh bằng Target.","metadata":{}},{"cell_type":"markdown","source":"**1. Tín hiệu rõ ràng và phân biệt giữa các cảm biến:**\n\n- Trong hầu hết các biểu đồ thuộc chuỗi Target, ta quan sát được sự thay đổi rõ rệt về giá trị trung bình của các cảm biến ToF, nhất là khi chuyển từ pha “Transition” sang “Gesture”.\n\n- Ví dụ: các hành vi như Forehead - pull hairline, Neck - pinch skin, hay Above ear - pull hair đều xuất hiện mức tăng/giảm đồng loạt hoặc đột ngột ở một số cảm biến trong giai đoạn thực hiện cử chỉ.\n\n**2. Sự ổn định hoặc nhiễu trong Non-Target:**\n\n- Với các chuỗi Non-Target, xu hướng tín hiệu không rõ ràng, thường có nhiều dao động nhỏ hoặc nhiễu, và khó tìm được pattern rõ ràng khi bước vào pha Gesture.\n\n- Ví dụ: hành vi như Wave hello, Text on phone, Glasses on/off thể hiện biến động cao hoặc không có sự biến thiên rõ nét.\n\n**3. Đặc trưng phân biệt pha Gesture:**\n\n- Trong hầu hết chuỗi Target, ToF sensors có xu hướng thể hiện một pattern đặc trưng trong pha Gesture — như plateau (ổn định), tăng/giảm liên tục hoặc đỉnh nhọn.\n\n- Ngược lại, trong chuỗi Non-Target, pha Gesture thường có nhiều đoạn dao động mạnh, không có quy luật rõ ràng hoặc không khác biệt nhiều với Transition.","metadata":{}},{"cell_type":"markdown","source":"**Nhận xét chung:**\n- Target gestures có xu hướng thể hiện pattern ổn định và đặc trưng trong pha Gesture, đặc biệt ở các cảm biến trọng yếu như tof_1, tof_2, tof_5.\n\n- Non-target gestures thường có tín hiệu nhiễu, dao động cao hoặc thiếu nhất quán, dẫn đến khả năng nhận diện thấp.\n\n- Việc chọn lọc cảm biến và trích xuất đặc trưng (feature engineering) theo từng loại gesture là rất cần thiết để tăng hiệu quả phân loại.","metadata":{}},{"cell_type":"markdown","source":"### 3.4.2 Sensor‐Based Differentiation Across Demographics and Physiology Features","metadata":{}},{"cell_type":"markdown","source":"#### 📌 Đặc trưng đang xem xét: (Thuận tay) **Handedness**\n\nTrong phần này, ta trực quan hóa cách những người thuận tay trái và thuận tay phải thực hiện cùng một cử chỉ (“Write name on leg”). Ta lấy một chuỗi dữ liệu từ mỗi nhóm và vẽ song song bốn loại cảm biến để so sánh.\n\nBằng cách so sánh giữa người thuận tay trái và tay phải, ta tìm kiếm sự khác biệt ổn định trong cách các cảm biến trên cổ tay phản hồi khi tay tiến lại gần và viết lên chân. Do các cảm biến Helios được bố trí xung quanh cổ tay, việc thuận tay nào sẽ ảnh hưởng đến cảm biến thermopile hoặc ToF nào đối diện với chân trước, đồng thời làm thay đổi hướng chuyển động trên các trục IMU.\n\n**⏩ Cách phân tích & Hàm ý thực tiễn (theo từng cảm biến):**\n\n* **IMU (acc\\_x/y/z):**  Quan sát trục gia tốc nào có đỉnh sớm hơn hoặc lớn hơn khi tay di chuyển vào trong. Một người thuận tay phải khi viết có thể tạo ra giá trị acc_x (lateral – ngang) dương lớn hơn do cổ tay xoay khác với tay trái. Mô hình có thể học cách hoán đổi hoặc căn chỉnh lại thông tin trục dựa vào handedness để tránh hiểu nhầm thời điểm bắt đầu cử chỉ.\n* **Rotation (rot\\_w/x/y/z):** Theo dõi xu hướng nghiêng: người thuận tay phải có thể xoay cổ tay vào trong (ví dụ rot_y tăng sớm hơn), còn tay trái có thể xoay ra ngoài trước. Nếu góc xoay (rot_angle) tăng chậm hơn ở một bên, điều đó cho biết cánh tay vặn khác nhau — cần điều chỉnh căn thời gian để tránh nhầm lẫn do handedness.\n* **Thermopiles (thm\\_1–5):** Để ý cảm biến nhiệt nào tăng trước khi nhiệt da lọt vào vùng nhìn 50°. Với người thuận tay phải, một cảm biến cụ thể (ví dụ thm_2 ở mặt trong cổ tay) có thể nhận tín hiệu nhiệt sớm hơn so với tay trái. Nhờ đó, thuật toán có thể chọn cảm biến nhiệt phù hợp dựa trên thông tin handedness khai báo.\n* **ToF Mean Distance (tof\\_1–5):** Xác định cảm biến ToF nào có khoảng cách trung bình giảm sớm nhất khi tay tiếp cận chân. Người thuận tay phải có thể khiến ToF mặt trước (ví dụ tof_3_mean) nhận biết khoảng cách gần sớm hơn, và điều này ngược lại với người thuận tay trái. Việc huấn luyện trên sự lệch thời gian có hệ thống của ToF theo handedness giúp mô hình tránh bị overfitting vào một kiểu tay cụ thể.\n","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\n# (1) Sáp nhập train_demographics vào train_df nếu chưa thực hiện\ntrain_df = train_df.merge(\n    train_dem_df,\n    on=\"subject\",\n    how=\"left\"\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:57.580119Z","iopub.execute_input":"2025-08-07T08:08:57.580393Z","iopub.status.idle":"2025-08-07T08:08:58.269490Z","shell.execute_reply.started":"2025-08-07T08:08:57.580371Z","shell.execute_reply":"2025-08-07T08:08:58.268707Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"⏩ Vẽ sơ đồ cho mọi đối tượng chỉ với một cử chỉ","metadata":{}},{"cell_type":"code","source":"import warnings, numpy as np, pandas as pd, seaborn as sns, matplotlib.pyplot as plt\nwarnings.filterwarnings('ignore')\n\n# ─── 1. Chọn biến nhóm ────────────────────────────────────────────────────────────────────────────────\ngroup_col = \"handedness\"\ngroup_map = {0: \"Left-handed\", 1: \"Right-handed\"}\n\n# ─── 2. Xây dựng bảng tóm tắt cấp trình tự ──────────────────────────────────────────────────────────\nseq_summaries = []\n\nfor seq_id, seq in train_df.groupby(\"sequence_id\"):\n    \n    g_label = seq[group_col].iloc[0]\n    if pd.isna(g_label):                   # bỏ qua nếu thiếu thông tin nhân khẩu học\n        continue\n    \n    # ----- Mặt nạ pha -----\n    gest_mask = seq[\"phase\"] == \"Gesture\"\n    \n    # ----- 1) Gia tốc giật -----\n    # giật = |Δacc_mag / Δt| ; sử dụng max trên toàn bộ chuỗi\n    acc_mag = np.sqrt(seq[\"acc_x\"]**2 + seq[\"acc_y\"]**2 + seq[\"acc_z\"]**2).values\n    jerk = np.abs(np.diff(acc_mag))                         # Δt = 1 frame\n    max_jerk = jerk.max() if len(jerk) else np.nan\n    \n    # ----- 2) Phạm vi góc quay (Chỉ pha cử chỉ) -----\n    rot_angle = 2*np.arccos(seq[\"rot_w\"].clip(-1,1))\n    rot_range = rot_angle[gest_mask].max() - rot_angle[gest_mask].min() if gest_mask.any() else np.nan\n    \n    # ----- 3) Đỉnh nhiệt điện ΔT (Đỉnh cử chỉ trừ đi đường cơ sở chuyển tiếp) -----\n    thm_cols = [f\"thm_{i}\" for i in range(1,6)]\n    thm_mean = seq[thm_cols].ffill().bfill().mean(axis=1)\n    baseline = thm_mean[seq[\"phase\"]==\"Transition\"].median()\n    peak_dt  = thm_mean[gest_mask].max() - baseline if gest_mask.any() else np.nan\n    \n    # ----- 4) Khoảng cách trung bình gần nhất của ToF (tối thiểu trên tất cả 5 cảm biến) -----\n    tof_means = []\n    for i in range(1,6):\n        col = f\"tof_{i}_mean\"\n        if col not in seq:\n            # tính toán tức thời (thay thế -1 bằng NaN)\n            pix = seq[[f\"tof_{i}_v{p}\" for p in range(64)]].replace(-1, np.nan)\n            seq[col] = pix.mean(axis=1)\n        tof_means.append(seq[col])\n    tof_overall = pd.concat(tof_means, axis=1).mean(axis=1)\n    min_tof = tof_overall.min()\n    \n    seq_summaries.append({\n        \"sequence_id\":       seq_id,\n        group_col:           g_label,\n        \"max_jerk\":          max_jerk,\n        \"rot_range\":         rot_range,\n        \"thm_peak_delta\":    peak_dt,\n        \"tof_min_distance\":  min_tof\n    })\n\nsummary_df = pd.DataFrame(seq_summaries).dropna()\n\n# Ánh xạ nhãn nhóm thành chuỗi có thể đọc được\nsummary_df[\"Group\"] = summary_df[group_col].map(group_map)\n\n# ─── 3. Phân phối đồ thị ─ ...\nsns.set_style(\"whitegrid\")\nfig, axes = plt.subplots(2, 2, figsize=(14, 10))\nplt.subplots_adjust(hspace=0.35, wspace=0.25)\n\nmetrics = [\n    (\"max_jerk\",         \"Max Jerk |Δacc| (m/s²)\"),\n    (\"rot_range\",        \"Gesture Rot-Angle Range (rad)\"),\n    (\"thm_peak_delta\",   \"Thermopile ΔT Peak (°C)\"),\n    (\"tof_min_distance\", \"Min ToF Mean Dist. (units)\")\n]\n\nfor ax, (metric, nice_name) in zip(axes.flatten(), metrics):\n    sns.violinplot(\n        data=summary_df,\n        x=\"Group\", y=metric, palette=\"Set2\",\n        inner=None, ax=ax\n    )\n    sns.boxplot(\n        data=summary_df,\n        x=\"Group\", y=metric,\n        width=0.2, showcaps=True, boxprops={'facecolor':'white'},\n        showfliers=False, whiskerprops={'linewidth':2},\n        ax=ax\n    )\n    ax.set_title(nice_name, fontsize=12)\n    ax.set_xlabel(\"\")\n    ax.set_ylabel(nice_name, fontsize=10)\n\nfig.suptitle(f\"{group_map[0]} vs. {group_map[1]} – Sequence-Level Sensor Features (*Gesture Phase Only)\", fontsize=16)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:08:58.270362Z","iopub.execute_input":"2025-08-07T08:08:58.270620Z","iopub.status.idle":"2025-08-07T08:10:33.459474Z","shell.execute_reply.started":"2025-08-07T08:08:58.270600Z","shell.execute_reply":"2025-08-07T08:10:33.458365Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích kết quả biểu đồ**\n-","metadata":{}},{"cell_type":"markdown","source":" **1. Max Jerk |Δacc|**\n -\n- Right-handed có xu hướng có giá trị jerk cao hơn trung bình so với Left-handed.\n\n- Phân phối bên phải dàn trải hơn → một số sequence có động tác rất mạnh.\n\n⇒ **Nhận xét:** Người thuận tay phải có xu hướng tạo ra cử chỉ nhanh và mạnh hơn (có thể do tự nhiên hoặc thiết kế bài toán ban đầu).","metadata":{}},{"cell_type":"markdown","source":"**2. Gesture Rot-Angle Range**\n-\n- Phạm vi góc quay của cả hai nhóm đều tập trung thấp, nhưng Left-handed có giá trị thấp hơn rõ rệt.\n\n- Dải phân phối của Left-handed bó hẹp hơn, hầu như nằm dưới 1 radian.\n\n⇒ **Nhận xét:** Người thuận tay trái có chuyển động quay tay ít biến thiên hơn – có thể là do sử dụng tay không thuận khi thực hiện cử chỉ mẫu.","metadata":{}},{"cell_type":"markdown","source":" **3. Thermopile ΔT Peak**\n -\n- Phân phối tương đối tương đồng giữa hai nhóm.\n\n- Tuy nhiên, Right-handed có vài giá trị đỉnh ΔT rất cao → có thể do chuyển động gần hơn với cảm biến nhiệt.\n\n⇒ **Nhận xét:** Không có sự khác biệt lớn, nhưng Right-handed có nhiều cử chỉ gần thiết bị hơn, tạo ra nhiệt độ cao hơn.","metadata":{}},{"cell_type":"markdown","source":"**4. Min ToF Mean Dist.**\n-\n- Left-handed có giá trị khoảng cách tối thiểu trung bình thấp hơn.\n\n- Phân phối rộng hơn → Left-handed đôi khi thực hiện động tác rất gần cảm biến.\n\n⇒ **Nhận xét:** Người thuận tay trái (có thể vì dùng tay không thuận) thường đưa tay gần cảm biến hơn khi thao tác.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Có sự khác biệt rõ ràng về đặc trưng chuyển động giữa Left-handed và Right-handed, đặc biệt ở jerk, góc quay và khoảng cách ToF.\n\n- Những khác biệt này có thể ảnh hưởng đến hiệu suất phân loại nếu mô hình không kiểm soát được handedness.\n\n- Việc đưa handedness vào như một meta feature hoặc chia tập huấn luyện theo handedness có thể cải thiện độ chính xác mô hình.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# ─── Tính toán thời lượng pha theo từng chuỗi ───────────────────────────────────────────────────────────────────────\n# Giả sử train_df đã hợp nhất dữ liệu nhân khẩu học và cột 'pha'\n\n# Đếm số khung hình trên mỗi pha, trên mỗi chuỗi\ndur_df = (\n    train_df\n    .groupby(['sequence_id', 'handedness'])['phase']\n    .value_counts()\n    .unstack(fill_value=0)\n    .reset_index()\n)\n\n# Chỉ giữ lại Chuyển tiếp & Cử chỉ (trong trường hợp Tạm dừng tồn tại)\ndur_df = dur_df[['sequence_id', 'handedness', 'Transition', 'Gesture']]\n\n# Chuyển sang dạng dài cho biểu đồ hộp\ndur_long = dur_df.melt(\n    id_vars=['sequence_id', 'handedness'],\n    value_vars=['Transition', 'Gesture'],\n    var_name='phase',\n    value_name='duration_frames'\n)\n\n# Bản đồ thuận tay trái thành nhãn\ndur_long['Group'] = dur_long['handedness'].map({0: 'Left-handed', 1: 'Right-handed'})\n\n# ─── Vẽ các hộp biểu đồ ngang cạnh nhau ─ ...\nsns.set_style(\"whitegrid\")\nfig, axes = plt.subplots(1, 2, figsize=(14, 6), sharey=True)\nplt.subplots_adjust(wspace=0.2)\n\nfor ax, group_label in zip(axes, ['Left-handed', 'Right-handed']):\n    subset = dur_long[dur_long['Group'] == group_label]\n    sns.boxplot(\n        data=subset,\n        x='duration_frames',\n        y='phase',\n        orient='h',\n        palette=['lightgray', 'lightcoral'],\n        ax=ax\n    )\n    ax.set_title(f\"{group_label}\", fontsize=12)\n    ax.set_xlabel(\"Duration (frames)\", fontsize=10)\n    ax.set_ylabel(\"Phase\", fontsize=10)\n    ax.grid(True)\n\nfig.suptitle(\"Phase Durations by Handedness: Transition vs. Gesture\", fontsize=14, y=1.02)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:10:33.460728Z","iopub.execute_input":"2025-08-07T08:10:33.461076Z","iopub.status.idle":"2025-08-07T08:10:34.091675Z","shell.execute_reply.started":"2025-08-07T08:10:33.461040Z","shell.execute_reply":"2025-08-07T08:10:34.090581Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-","metadata":{}},{"cell_type":"markdown","source":"**🔸 Left-handed:**\n-\n- Transition: Có trung vị khoảng ~40 frames, nhưng có nhiều outlier kéo dài > 300 frames.\n\n- Gesture: Trung vị thấp hơn rõ rệt (~20 frames), ít outlier hơn và khá tập trung.\n\n**🔸 Right-handed:**\n-\n- Transition: Phân phối rất giống nhóm Left-handed, trung vị cũng ~40 frames, nhưng có nhiều outlier cực lớn (đến 600+ frames).\n\n- Gesture: Cũng ngắn hơn đáng kể so với Transition, trung vị ~20–25 frames.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Cả hai nhóm đều có pha chuyển tiếp dài hơn nhiều so với pha thực hiện cử chỉ.\n\n- Transition phase thường có nhiều outlier kéo dài, nhất là với Right-handed – điều này có thể do chần chừ khi bắt đầu cử chỉ.\n\n- Gesture phase rất tập trung và ngắn → đây là đặc trưng ổn định, phù hợp để trích đặc trưng mô hình.","metadata":{}},{"cell_type":"markdown","source":"⏩ Biểu đồ cho các đối tượng được chọn ngẫu nhiên và một cử chỉ","metadata":{}},{"cell_type":"markdown","source":"**1. Lọc dữ liệu theo cử chỉ và handedness.**\n\n**2. Tính toán đặc trưng:**\n\n- acc_mag: độ lớn gia tốc từ IMU.\n\n- rot_angle: góc quay từ quaternion.\n\n- thm_mean: nhiệt trung bình từ 5 cảm biến nhiệt.\n\n- tof_mean: khoảng cách trung bình từ 5 cảm biến ToF.\n\n**3. Tô màu các pha (Transition/Gesture) trong biểu đồ.**\n\n**4. Vẽ 4 nhóm biểu đồ:**\n\n- Acceleration Magnitude\n\n- Rotation Angle\n\n- Thermopile Temperature\n\n- ToF Distance","metadata":{}},{"cell_type":"code","source":"# ─── Ô A: So sánh độ thuận tay của IMU, Rot, Thermopile và ToF ───────────────────────────────────────────────\n\n# (2) Chọn một cử chỉ để so sánh (thay đổi nếu cần)\ngesture_to_plot = \"Write name on leg\"\n\n# (3) Lọc chỉ các hàng có cử chỉ đó và phân chia theo tay thuận\ndf_gesture = train_df[train_df[\"gesture\"] == gesture_to_plot].copy()\n\n# Nhóm thuận tay trái (thuận tay == 0) và Nhóm thuận tay phải (thuận tay == 1)\nleft_group  = df_gesture[df_gesture[\"handedness\"] == 0]\nright_group = df_gesture[df_gesture[\"handedness\"] == 1]\n\n# (4) Từ mỗi nhóm, chọn ngẫu nhiên một sequence_id (hoặc đầu tiên) để vẽ đồ thị\n# Nếu không có ví dụ nào dành cho người thuận tay trái, bạn có thể cần một cử chỉ khác.\nif left_group[\"sequence_id\"].nunique() == 0:\n    raise ValueError(\"No left‐handed example of that gesture found. Choose a different gesture.\")\n\nleft_seq  = left_group[\"sequence_id\"].unique()[0]\nright_seq = right_group[\"sequence_id\"].unique()[0]\n\n# (5) Trích xuất hai chuỗi, sắp xếp theo sequence_counter\ndf_left  = train_df[(train_df[\"sequence_id\"] == left_seq)].sort_values(\"sequence_counter\")\ndf_right = train_df[(train_df[\"sequence_id\"] == right_seq)].sort_values(\"sequence_counter\")\n\n# (6)Tính toán các đặc điểm có nguồn gốc từ IMU: acc_mag và rot_angle\nfor df in (df_left, df_right):\n    df[\"acc_mag\"] = np.sqrt(df[\"acc_x\"]**2 + df[\"acc_y\"]**2 + df[\"acc_z\"]**2)\n    df[\"rot_w_clipped\"] = df[\"rot_w\"].clip(-1, 1)\n    df[\"rot_angle\"] = 2 * np.arccos(df[\"rot_w_clipped\"])\n\n# (7) Vẽ trợ lý để tô bóng các pha\ndef shade_phases(ax, seq_df):\n    used = set()\n    for phase_label, color in [(\"Transition\", \"lightgray\"), (\"Gesture\", \"lightcoral\")]:\n        mask = seq_df[\"phase\"] == phase_label\n        if not mask.any():\n            continue\n        idxs = seq_df.index[mask]\n        diffs = np.where(np.diff(idxs) != 1)[0]\n        spans = []\n        start = idxs[0]\n        for b in diffs:\n            end = idxs[b]\n            spans.append((start, end))\n            start = idxs[b + 1]\n        spans.append((start, idxs[-1]))\n        for (s, e) in spans:\n            t0 = seq_df.loc[s, \"sequence_counter\"]\n            t1 = seq_df.loc[e, \"sequence_counter\"]\n            label = phase_label if phase_label not in used else None\n            ax.axvspan(t0, t1, color=color, alpha=0.3, label=label)\n            used.add(phase_label)\n\n# (8) Bây giờ hãy tạo **bốn** hình riêng biệt (mỗi hình có 2 biểu đồ phụ cạnh nhau):\n\n# --------------------------------------------------------------------------------\n# A.1: IMU: Độ lớn gia tốc\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_left, df_right],\n    [f\"Left‐handed (Subj: {df_left['subject'].iloc[0]})\",\n     f\"Right‐handed (Subj: {df_right['subject'].iloc[0]})\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"acc_mag\"],\n            color=\"tab:blue\", lw=1.5, label=\"|acc|(m/s²)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Acceleration Magnitude\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Handedness: Acceleration Magnitude – Left vs. Right\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# A.2: IMU: Góc quay\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_left, df_right],\n    [f\"Left‐handed (Subj: {df_left['subject'].iloc[0]})\",\n     f\"Right‐handed (Subj: {df_right['subject'].iloc[0]})\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"rot_angle\"],\n            color=\"tab:orange\", lw=1.5, label=\"rot_angle (rad)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Rotation Angle (rad)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Handedness: Rotation Angle – Left vs. Right\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# A.3: Nhiệt điện: 5 kênh + Trung bình\n# --------------------------------------------------------------------------------\n# (Tính giá trị trung bình của nhiệt điện nếu chưa thực hiện)\nfor df in (df_left, df_right):\n    thm_cols = [f\"thm_{i}\" for i in range(1,6)]\n    df[\"thm_mean\"] = df[thm_cols].mean(axis=1)\n\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_left, df_right],\n    [f\"Left‐handed (Subj: {df_left['subject'].iloc[0]})\",\n     f\"Right‐handed (Subj: {df_right['subject'].iloc[0]})\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    # Vẽ năm kênh thô\n    for col in [f\"thm_{i}\" for i in range(1,6)]:\n        ax.plot(times, df_seq[col], lw=1, label=col, alpha=0.7)\n    #Vẽ biểu đồ trung bình dưới dạng in đậm\n    ax.plot(times, df_seq[\"thm_mean\"], color=\"black\", lw=2.5, label=\"thm_mean\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Thermopile (°C)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"x-small\", ncol=2)\n    ax.grid(True)\nplt.suptitle(\"Handedness: Thermopile – Left vs. Right\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# A.4: ToF: Năm kênh khoảng cách trung bình\n# --------------------------------------------------------------------------------\n# (Tính toán ToF trung bình trên mỗi cảm biến)\nfor df in (df_left, df_right):\n    for i_sensor in range(1,6):\n        pixel_cols = [f\"tof_{i_sensor}_v{pix}\" for pix in range(64)]\n        df[f\"tof_{i_sensor}_mean\"] = df[pixel_cols].replace(-1, np.nan).mean(axis=1)\n\nmean_cols = [f\"tof_{i}_mean\" for i in range(1,6)]\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_left, df_right],\n    [f\"Left‐handed (Subj: {df_left['subject'].iloc[0]})\",\n     f\"Right‐handed (Subj: {df_right['subject'].iloc[0]})\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    for col in mean_cols:\n        ax.plot(times, df_seq[col], lw=1, label=col)\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"ToF Mean Distance\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Handedness: ToF Mean Distance – Left vs. Right\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:10:34.092826Z","iopub.execute_input":"2025-08-07T08:10:34.093204Z","iopub.status.idle":"2025-08-07T08:10:37.048242Z","shell.execute_reply.started":"2025-08-07T08:10:34.093170Z","shell.execute_reply":"2025-08-07T08:10:37.047167Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**PHÂN TÍCH BIỂU ĐỒ**\n-","metadata":{}},{"cell_type":"markdown","source":"**🟦 1. Acceleration Magnitude**\n-\n- **Left-handed:**\n\n  * Độ lớn gia tốc ổn định trong pha Transition (~10 m/s²), nhưng biến động khá nhiều trong Gesture.\n\n  * Xuất hiện nhiều dao động nhỏ liên tục trong Gesture.\n\n- **Right-handed:**\n\n  * Mức dao động cũng tăng trong Gesture, nhưng biên độ biến thiên thấp hơn.\n\n  * Nhìn chung, chuyển động trơn tru và ít nhiễu hơn.\n\n🟢 **Nhận xét:** Người thuận tay phải có xu hướng thực hiện cử chỉ ổn định và chắc tay hơn.","metadata":{}},{"cell_type":"markdown","source":"**🟧 2. Rotation Angle**\n-\n- **Left-handed:** Góc quay lớn ở đầu sequence (~2.8 rad), giảm mạnh trong Transition và giữ ổn định trong Gesture.\n\n- **Right-handed:**\n\n  * Thay đổi rõ rệt về góc quay trong cả Transition và Gesture.\n\n  * Biên độ dao động lớn hơn → cho thấy có nhiều xoay chuyển cơ thể hơn.\n\n🟢 **Nhận xét:** Tay phải tạo ra nhiều chuyển động quay hơn, điều này có thể phản ánh sự linh hoạt hoặc mức tự nhiên của tay thuận.","metadata":{}},{"cell_type":"markdown","source":"🔴 **3. Thermopile – Nhiệt độ**\n-\n- **Left-handed:**\n\n  * Nhiệt độ ổn định trong suốt sequence, chỉ có dao động nhỏ.\n\n  * Không có peak rõ ràng trong Gesture.\n\n- **Right-handed:**\n\n  * Nhiệt độ tăng vọt từ Transition sang Gesture (~5°C).\n\n  * Cho thấy tay phải đưa lại gần cảm biến nhiệt nhiều hơn trong khi thực hiện cử chỉ.\n\n🟢 **Nhận xét:** Người thuận tay phải tiếp cận gần hơn với thiết bị trong khi thao tác, tạo ra độ tăng nhiệt mạnh mẽ.","metadata":{}},{"cell_type":"markdown","source":"🟨 **4. ToF Mean Distance**\n-\n- **Left-handed:**\n\n  * Có sự dao động lớn giữa các cảm biến.\n\n  * Một vài cảm biến có khoảng cách rất gần (~0), vài cái lên tới 200+.\n\n- **Right-handed:**\n\n  * Ổn định hơn, các cảm biến duy trì khoảng cách thấp vừa phải.\n\n  * Biến động trong Gesture là có, nhưng không đột ngột như bên trái.\n\n🟢 **Nhận xét:** Cử chỉ tay trái ít đồng bộ hơn khi thực hiện, dẫn đến phân bố khoảng cách không đều giữa các cảm biến ToF.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- **Người thuận tay phải có:**\n\n  * Gia tốc ổn định hơn và quay mạnh hơn\n\n  * Tiếp cận gần thiết bị hơn → nhiệt độ tăng cao\n\n  * Khoảng cách cảm biến ToF ổn định hơn\n\n- **Người thuận tay trái (có thể phải dùng tay không thuận):**\n\n  * Dao động lớn hơn, không ổn định về góc quay và khoảng cách.\n\n  * Cử chỉ ít tiếp cận gần thiết bị, nhiệt độ không thay đổi nhiều.","metadata":{}},{"cell_type":"markdown","source":"📌 Đặc trưng đang xem xét: Adult vs. Child\nỞ đây, ta chọn một cử chỉ cụ thể (ví dụ: “Neck – pinch skin”) và so sánh giữa dữ liệu của một người lớn và một trẻ em.\n\nMỗi biểu đồ đối chiếu cách trẻ em và người lớn thực hiện cùng một cử chỉ. Trẻ em thường di chuyển đột ngột hơn (các đỉnh “acc_mag” cao hơn) và có thể tạo ra các đặc điểm nhiệt/tiếp cận khác do chiều dài tay ngắn hơn.\n\n⏩ Cách phân tích & Hàm ý thực tiễn (theo từng cảm biến):\n\nAcceleration magnitude: Nếu đường cong “acc_mag” của trẻ có nhiều đỉnh sắc nhọn và biên độ lớn hơn, mô hình cần hiểu rằng các đợt gia tốc IMU mạnh không phải lúc nào cũng là dấu hiệu của BFRB, mà có thể phản ánh sự vận động tự nhiên, mạnh mẽ hơn của trẻ em.\nRotation angle: Người lớn có thể giữ ổn định góc xoay hơn trong giai đoạn tạm dừng, trong khi “rot_angle” của trẻ có thể dao động nhiều hơn. Điều này giúp mô hình nhận biết rằng những dao động nhỏ có thể là bình thường nếu đối tượng là trẻ em.\nThermopile: Giá trị nhiệt của trẻ có thể tăng nhanh hơn (tiếp xúc gần hơn) nhưng cũng hạ nhiệt nhanh hơn; tích hợp đặc điểm này giúp mô hình phân biệt tiếp xúc thực sự và các pha bật ra nhanh điển hình ở trẻ em.\nToF: Nếu khoảng cách trung bình của ToF ở trẻ giảm sớm hơn (tay ở gần hơn), mô hình có thể học cách điều chỉnh ngưỡng khoảng cách theo độ tuổi, tránh nhầm lẫn các chuyển động lớn của trẻ với những cử chỉ khác.","metadata":{}},{"cell_type":"markdown","source":"⏩ Plots for All Subjects and One Gesture","metadata":{}},{"cell_type":"markdown","source":"- Gộp dữ liệu theo sequence_id và tính 4 đặc trưng ở pha Gesture:\n\n  * Max jerk từ độ lớn gia tốc IMU.\n\n  * Phạm vi góc quay từ quaternion (max–min của rot_angle).\n\n  * Đỉnh nhiệt ΔT = max nhiệt trong Gesture – median nhiệt ở Transition.\n\n  * Khoảng cách ToF nhỏ nhất: tính mean 64 pixel của 5 cảm biến ToF theo từng frame, rồi lấy min trên chuỗi.\n\n- Gán nhãn nhóm Child/Adult từ adult_child và vẽ violin + boxplot cho từng đặc trưng để so sánh phân phối.","metadata":{}},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# ─── 1. Xây dựng tóm tắt cấp độ trình tự cho Người lớn so với Trẻ em ─────────────────────────────\nseq_summaries = []\nfor seq_id, seq in train_df.groupby(\"sequence_id\"):\n    grp = seq[\"adult_child\"].iloc[0]  # 0=child, 1=adult\n    # Bỏ qua nếu không có nhãn pha\n    if seq[\"phase\"].isna().all():\n        continue\n    # 1) Max Jerk (IMU)\n    acc_mag = np.sqrt(seq[\"acc_x\"]**2 + seq[\"acc_y\"]**2 + seq[\"acc_z\"]**2).values\n    jerk    = np.abs(np.diff(acc_mag))\n    max_jerk = jerk.max() if len(jerk) else np.nan\n    # 2) Phạm vi xoay trong khi cử chỉ\n    rot_angle = 2 * np.arccos(seq[\"rot_w\"].clip(-1,1))\n    mask_g = seq[\"phase\"]==\"Gesture\"\n    rot_range = rot_angle[mask_g].max() - rot_angle[mask_g].min() if mask_g.any() else np.nan\n    # 3) Đỉnh nhiệt điện ΔT\n    thm = seq[[f\"thm_{i}\" for i in range(1,6)]].ffill().bfill().mean(axis=1)\n    base = thm[seq[\"phase\"]==\"Transition\"].median()\n    peak_dt = thm[mask_g].max() - base if mask_g.any() else np.nan\n    # 4) Khoảng cách tối thiểu ToF\n    tof_means = []\n    for i in range(1,6):\n        col = f\"tof_{i}_mean\"\n        if col not in seq:\n            pix = seq[[f\"tof_{i}_v{p}\" for p in range(64)]].replace(-1,np.nan)\n            seq[col] = pix.mean(axis=1)\n        tof_means.append(seq[col])\n    tof_min = pd.concat(tof_means,axis=1).mean(axis=1).min()\n    seq_summaries.append({\n        \"sequence_id\": seq_id,\n        \"adult_child\": grp,\n        \"max_jerk\": max_jerk,\n        \"rot_range\": rot_range,\n        \"thm_peak_delta\": peak_dt,\n        \"tof_min_distance\": tof_min\n    })\n\nsummary_df = pd.DataFrame(seq_summaries).dropna()\nsummary_df[\"Group\"] = summary_df[\"adult_child\"].map({0:\"Child\", 1:\"Adult\"})\n\n# ─── 2. Vẽ đồ thị phân phối cạnh nhau ─────────────────────────────────────────────────────────────\nsns.set_style(\"whitegrid\")\nfig, axes = plt.subplots(2, 2, figsize=(14,10), sharey=False)\nplt.subplots_adjust(hspace=0.4, wspace=0.3)\n\nmetrics = [\n    (\"max_jerk\",         \"Max Jerk |Δacc| (m/s²)\"),\n    (\"rot_range\",        \"Gesture Rot-Range (rad)\"),\n    (\"thm_peak_delta\",   \"Thermopile ΔT Peak (°C)\"),\n    (\"tof_min_distance\", \"Min ToF Mean Dist. (units)\")\n]\n\nfor ax, (col, label) in zip(axes.flatten(), metrics):\n    sns.violinplot(\n        x=\"Group\", y=col, data=summary_df,\n        palette=\"Set2\", inner=None, ax=ax\n    )\n    sns.boxplot(\n        x=\"Group\", y=col, data=summary_df,\n        width=0.2, showcaps=True, boxprops={'facecolor':'white'},\n        showfliers=False, whiskerprops={'linewidth':2}, ax=ax\n    )\n    ax.set_title(label, fontsize=12)\n    ax.set_xlabel(\"\")\n    ax.set_ylabel(label, fontsize=10)\n\nfig.suptitle(\"Adult vs. Child – Sequence-Level Sensor Feature Distributions (*Gesture Phase Only)\", fontsize=16)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:10:37.049697Z","iopub.execute_input":"2025-08-07T08:10:37.050043Z","iopub.status.idle":"2025-08-07T08:12:13.804111Z","shell.execute_reply.started":"2025-08-07T08:10:37.050008Z","shell.execute_reply":"2025-08-07T08:12:13.803016Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-","metadata":{}},{"cell_type":"markdown","source":"1.Max Jerk |Δacc| (m/s²)\n   -\n\nAdult có trung vị cao hơn và đuôi phải dày hơn rõ rệt → nhiều chuỗi có jerk lớn.\n\nChild thấp và tập trung hơn.\n👉 Người lớn tạo chuyển động nhanh/mạnh hơn trong lúc thực hiện cử chỉ.\n\n2.Gesture Rot-Range (rad)\n   -\n\nAdult có phạm vi góc quay nhỉnh hơn (trung vị và phần trên hộp cao hơn).\n\nChild hẹp và tập trung gần 0.2–0.4 rad, nhưng vẫn có một số ngoại lệ (đuôi dài).\n👉 Người lớn xoay cổ tay/cánh tay nhiều biên độ hơn; trẻ em thiên về cử chỉ nhỏ gọn.\n\n3.Thermopile ΔT Peak (°C)\n   -\n\nAdult có ΔT lớn hơn đáng kể và biến thiên rộng (nhiều peak 4–7°C).\n\nChild chủ yếu quanh 0–2°C, ít outlier cao.\n👉 Người lớn thường đưa tay gần thiết bị hơn/giữ lâu hơn → cảm biến nhiệt tăng mạnh hơn.\n\n4.Min ToF Mean Distance (units)\n   -\n\nHai nhóm chồng lấn lớn.\n\nChild có nhiều giá trị rất thấp (rất gần cảm biến; đuôi trái dài) và cũng trải rộng tới cao → hành vi tiếp cận không ổn định.\n\nAdult tập trung gần trung vị 70–90 hơn, ít cực trị sát 0.\n👉 Trẻ em khi làm cử chỉ đôi lúc áp rất sát cảm biến rồi rời ra nhanh; người lớn giữ khoảng cách ổn định hơn.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\nCó khác biệt hệ thống theo nhóm tuổi: Adult tạo biên độ lớn hơn (jerk, rot-range) và tiếp cận gần cảm biến (ΔT cao) nhưng ổn định khoảng cách ToF hơn Child.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport warnings\n\nwarnings.filterwarnings('ignore')\nsns.set_style(\"whitegrid\")\n\n# ─── Tính toán thời lượng pha theo trình tự cho Người lớn so với Trẻ em ────────────────────────────────────────────────────────────────────\ndur_df = (\n    train_df\n    .groupby(['sequence_id', 'adult_child'])['phase']\n    .value_counts()\n    .unstack(fill_value=0)\n    .reset_index()\n)\n\n# Chỉ giữ lại các cột Chuyển tiếp & Cử chỉ\ndur_df = dur_df[['sequence_id', 'adult_child', 'Transition', 'Gesture']]\n\n# Tan chảy thành dạng dài\ndur_long = dur_df.melt(\n    id_vars=['sequence_id', 'adult_child'],\n    value_vars=['Transition', 'Gesture'],\n    var_name='phase',\n    value_name='duration_frames'\n)\n\n# Ánh xạ adult_child vào nhãn\ndur_long['Group'] = dur_long['adult_child'].map({0: 'Child', 1: 'Adult'})\n\n# ─── Vẽ các biểu đồ hộp ngang cạnh nhau ─ ...\nfig, axes = plt.subplots(1, 2, figsize=(14, 6), sharey=True)\nplt.subplots_adjust(wspace=0.3)\n\nfor ax, group_label in zip(axes, ['Child', 'Adult']):\n    subset = dur_long[dur_long['Group'] == group_label]\n    sns.boxplot(\n        data=subset,\n        x='duration_frames',\n        y='phase',\n        orient='h',\n        palette=['lightgray', 'lightcoral'],\n        ax=ax\n    )\n    ax.set_title(f\"{group_label}\", fontsize=12)\n    ax.set_xlabel(\"Duration (frames)\", fontsize=10)\n    ax.set_ylabel(\"Phase\", fontsize=10)\n    ax.grid(True)\n\nplt.suptitle(\"Phase Durations by Adult vs. Child\", fontsize=14, y=1.02)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:12:13.805165Z","iopub.execute_input":"2025-08-07T08:12:13.806067Z","iopub.status.idle":"2025-08-07T08:12:14.393358Z","shell.execute_reply.started":"2025-08-07T08:12:13.806030Z","shell.execute_reply":"2025-08-07T08:12:14.392442Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-","metadata":{}},{"cell_type":"markdown","source":"\nỞ cả hai nhóm, Transition dài hơn rõ rệt so với Gesture. Gesture tập trung, Transition phân tán mạnh và đuôi phải rất dài.\n\n**Nhóm Child (trái)**\n-\n- Transition: Trung vị quanh ~35–45 frames; IQR khá rộng. Rất nhiều outlier kéo tới 300–400 frames và vài điểm rất dài (~500–600).\n\n- Gesture: Trung vị thấp (~20–25), IQR hẹp; có một số outlier lên ~70–90 frames → thỉnh thoảng trẻ thực hiện cử chỉ lâu hơn bình thường.\n\n**Nhóm Adult (phải)**\n-\n\n- Transition: Trung vị gần như tương đương Child (khoảng ~40). Cũng có nhiều outlier; đáng chú ý có một vài ngoại lệ cực đoan ~650 frames (độ dài hơn Child một chút ở phần đuôi).\n\n- Gesture: Phân bố rất gọn, sát Child; vài outlier <~80 frames.\n\n**So sánh Child vs. Adult**\n-\n\n- Medians của cả hai pha gần như giống nhau giữa hai nhóm.\n\n- Khác biệt chủ yếu nằm ở đuôi phải của Transition: Adult xuất hiện vài chuỗi rất dài hơn, nhưng tổng thể hai phân phối chồng lấn lớn.\n\n- Gesture ổn định ở cả hai nhóm; Child có vẻ nhỉnh hơn về độ phân tán (nhiều điểm xa hơn một chút), phản ánh sự thiếu ổn định nhẹ khi thực hiện.","metadata":{}},{"cell_type":"markdown","source":"**Nhận xét**\n-\n- Phase duration không phải yếu tố phân biệt tuổi mạnh (ít khác biệt ở trung vị/IQR). Biến adult_child vì thế không cần để điều chỉnh độ dài cửa sổ chỉ vì thời lượng pha.\n\n- Transition outliers (≥ 95th percentile, đặc biệt >300–400 frames) nên được cắt/cap hoặc xử lý bằng robust scaling để tránh làm lệch thống kê và huấn luyện.\n\n- Khi xây dựng cửa sổ thời gian cho mô hình, chọn độ dài theo Gesture (ví dụ ≤ 64–80 frames) và tách/giảm trọng số phần Transition để bớt nhiễu.\n\n- Các Transition quá dài có thể là chậm trễ khởi động/tạm dừng/mislabel—nên soát lại các chuỗi ở đuôi dài (500–650 frames).\n\n=> Về thời lượng pha, Adult và Child gần như tương đồng; khác biệt chính nằm ở một số Transition cực dài (nhất là bên Adult). Tối ưu hóa tiền xử lý (trim/cap) quan trọng hơn là thêm đặc trưng “tuổi” cho riêng bài toán thời lượng.","metadata":{}},{"cell_type":"markdown","source":"⏩ Biểu đồ cho các đối tượng được chọn ngẫu nhiên và một cử chỉ","metadata":{}},{"cell_type":"code","source":"# ─── Tế bào B: So sánh giữa người lớn và trẻ em đối với IMU, Rot, Thermopile và ToF ──────────────────────────────────────────────\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# (2) Chọn một cử chỉ để so sánh (thay đổi nếu cần)\ngesture_to_plot = \"Neck - pinch skin\"\n\n# (3)Lọc chỉ các hàng có cử chỉ đó, chia theo adult_child (0=child, 1=adult)\ndf_gesture = train_df[train_df[\"gesture\"] == gesture_to_plot].copy()\nchild_group = df_gesture[df_gesture[\"adult_child\"] == 0]\nadult_group = df_gesture[df_gesture[\"adult_child\"] == 1]\n\nif (child_group[\"sequence_id\"].nunique() == 0) or (adult_group[\"sequence_id\"].nunique() == 0):\n    raise ValueError(\"Insufficient examples in one of the groups. Pick another gesture.\")\n\nchild_seq = child_group[\"sequence_id\"].unique()[0]\nadult_seq = adult_group[\"sequence_id\"].unique()[0]\n\ndf_child = train_df[train_df[\"sequence_id\"] == child_seq].sort_values(\"sequence_counter\")\ndf_adult = train_df[train_df[\"sequence_id\"] == adult_seq].sort_values(\"sequence_counter\")\n\n# Tính toán các tính năng IMU được lấy từ mỗi\nfor df in (df_child, df_adult):\n    df[\"acc_mag\"] = np.sqrt(df[\"acc_x\"]**2 + df[\"acc_y\"]**2 + df[\"acc_z\"]**2)\n    df[\"rot_w_clipped\"] = df[\"rot_w\"].clip(-1, 1)\n    df[\"rot_angle\"] = 2 * np.arccos(df[\"rot_w_clipped\"])\n\n# Trình trợ giúp vẽ đồ thị để tô bóng các pha (chức năng sử dụng lại từ Ô A)\ndef shade_phases(ax, seq_df):\n    used = set()\n    for phase_label, color in [(\"Transition\", \"lightgray\"), (\"Gesture\", \"lightcoral\")]:\n        mask = seq_df[\"phase\"] == phase_label\n        if not mask.any():\n            continue\n        idxs = seq_df.index[mask]\n        diffs = np.where(np.diff(idxs) != 1)[0]\n        spans = []\n        start = idxs[0]\n        for b in diffs:\n            end = idxs[b]\n            spans.append((start, end))\n            start = idxs[b + 1]\n        spans.append((start, idxs[-1]))\n        for (s, e) in spans:\n            t0 = seq_df.loc[s, \"sequence_counter\"]\n            t1 = seq_df.loc[e, \"sequence_counter\"]\n            label = phase_label if phase_label not in used else None\n            ax.axvspan(t0, t1, color=color, alpha=0.3, label=label)\n            used.add(phase_label)\n\n# --------------------------------------------------------------------------------\n# B.1: IMU – Độ lớn gia tốc\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_child, df_adult],\n    [f\"Child (Subj: {df_child['subject'].iloc[0]}, Age: {df_child['age'].iloc[0]})\",\n     f\"Adult (Subj: {df_adult['subject'].iloc[0]}, Age: {df_adult['age'].iloc[0]})\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"acc_mag\"],\n            color=\"tab:blue\", lw=1.5, label=\"|acc|(m/s²)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Acc Magnitude\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Adult vs. Child: Acceleration Magnitude\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# B.2: IMU – Góc quay\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_child, df_adult],\n    [f\"Child (Subj: {df_child['subject'].iloc[0]}, Age: {df_child['age'].iloc[0]})\",\n     f\"Adult (Subj: {df_adult['subject'].iloc[0]}, Age: {df_adult['age'].iloc[0]})\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"rot_angle\"],\n            color=\"tab:orange\", lw=1.5, label=\"rot_angle (rad)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Rot Angle (rad)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Adult vs. Child: Rotation Angle\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# B.3: Nhiệt điện – 5 kênh + Trung bình\n# --------------------------------------------------------------------------------\nfor df in (df_child, df_adult):\n    thm_cols = [f\"thm_{i}\" for i in range(1,6)]\n    df[\"thm_mean\"] = df[thm_cols].mean(axis=1)\n\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_child, df_adult],\n    [f\"Child (Subj: {df_child['subject'].iloc[0]}, Age: {df_child['age'].iloc[0]})\",\n     f\"Adult (Subj: {df_adult['subject'].iloc[0]}, Age: {df_adult['age'].iloc[0]})\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    for col in [f\"thm_{i}\" for i in range(1,6)]:\n        ax.plot(times, df_seq[col], lw=1, label=col, alpha=0.7)\n    ax.plot(times, df_seq[\"thm_mean\"], color=\"black\", lw=2.5, label=\"thm_mean\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Thermopile (°C)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\", ncol=2)\n    ax.grid(True)\nplt.suptitle(\"Adult vs. Child: Thermopile\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# B.4: ToF – Năm kênh khoảng cách trung bình\n# --------------------------------------------------------------------------------\nfor df in (df_child, df_adult):\n    for i_sensor in range(1,6):\n        pixel_cols = [f\"tof_{i_sensor}_v{pix}\" for pix in range(64)]\n        df[f\"tof_{i_sensor}_mean\"] = df[pixel_cols].replace(-1, np.nan).mean(axis=1)\n\nmean_cols = [f\"tof_{i}_mean\" for i in range(1,6)]\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_child, df_adult],\n    [f\"Child (Subj: {df_child['subject'].iloc[0]}, Age: {df_child['age'].iloc[0]})\",\n     f\"Adult (Subj: {df_adult['subject'].iloc[0]}, Age: {df_adult['age'].iloc[0]})\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    for col in mean_cols:\n        ax.plot(times, df_seq[col], lw=1, label=col)\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"ToF Mean Distance\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Adult vs. Child: ToF Mean Distance\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:12:14.394733Z","iopub.execute_input":"2025-08-07T08:12:14.395053Z","iopub.status.idle":"2025-08-07T08:12:17.866635Z","shell.execute_reply.started":"2025-08-07T08:12:14.395030Z","shell.execute_reply":"2025-08-07T08:12:17.865506Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-","metadata":{}},{"cell_type":"markdown","source":"**1.Acceleration Magnitude**\n   -\n- Child: spike rất lớn ngay đầu (≈18 m/s²) rồi suy giảm; trong Gesture dao động quanh ~10, biên độ hơi gợn.\n\n- Adult: không có spike lớn; biên độ thấp và ổn định hơn (~9–10), dao động nhỏ trong Gesture.\n\n- Ý nghĩa: Trẻ có xu hướng bắt đầu gấp/giật hơn; người lớn vào nhịp mượt, lực đều.\n\n**2.Rotation Angle**\n-\n- Child: tăng mạnh trong Transition lên ~2.6 rad rồi giữ gần như “plateau” trong Gesture (biên độ nhỏ).\n\n- Adult: rơi từ ~2.9 → ~2.0 ngay đầu, sau đó giảm dần trong Gesture (~1.7–1.9).\n\n- Ý nghĩa: Người lớn có độ biến thiên góc lớn hơn (Δangle âm rõ rệt) khi chuyển sang thực hiện; trẻ giữ tư thế ổn định hơn sau khi vào Gesture.\n\n**3.Thermopile (5 kênh + mean)**\n-\n- Child: sau đoạn đầu hơi nhiễu, nhiệt tăng nhẹ rồi ổn định ~29–30°C trong Gesture.\n\n- Adult: khi vào Gesture, nhiệt mean giảm rõ (≈1–2°C) rồi đi ngang.\n\n- Ý nghĩa: Trẻ có xu hướng tiệm cận gần cảm biến (ấm lên), còn người lớn lùi/giữ xa hơn (mát đi) trong khi véo da cổ.\n\n**4.ToF Mean Distance**\n-\n- Child: khoảng cách giảm rất nhanh về rất thấp ở đầu rồi giữ thấp trong phần lớn Gesture; vài kênh dao động nhưng vẫn gần.\n\n- Adult: cũng hạ thấp khi vào Gesture nhưng tăng dần ở vài kênh (nhất là giữa-cuối Gesture), cho thấy dịch ra xa dần.\n\n- Ý nghĩa: Cùng một cử chỉ, trẻ giữ tay gần sensor hơn và nhất quán; người lớn tạo khoảng cách lớn hơn hoặc thay đổi khoảng cách theo thời gian.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Với Neck – pinch skin, khác biệt theo tuổi thể hiện ở:\n\n  * Động học vào nhịp: Child có jerk peak lớn; Adult mượt hơn.\n  \n  * Góc quay: Adult có Δangle lớn và xu thế giảm; Child “plateau”.\n\n  * Tiệm cận thiết bị: Child gần hơn (ToF thấp, Thermopile tăng); Adult xa hơn (ToF tăng dần, Thermopile giảm).\n\n- Những tín hiệu này gợi ý các feature hữu ích: jerk_peak@transition, Δrot_angle (Transition→Gesture), ΔThm_mean, ToF_min & slope trong Gesture.\n\n- Để mô hình hóa tốt hơn:\n\n  * Giữ adult_child làm meta-feature hoặc chuẩn hóa/huấn luyện theo nhóm tuổi.\n\n  * Tạo feature theo pha (Transition vs. Gesture) và theo xu hướng (slope, delta), không chỉ giá trị tức thời.\n\n  * Tránh lệch pha: cắt/bỏ vài frame đầu (spike) nếu cần tăng ổn định.","metadata":{}},{"cell_type":"markdown","source":"#### 📌 Đặc trưng đang xem xét:  **Sex (Female vs. Male)**\n\nTrong phần này, ta so sánh các tín hiệu cảm biến giữa nam và nữ đối với một cử chỉ cụ thể (ví dụ: “Forehead – scratch”). Ta tạo cùng lúc bốn biểu đồ: IMU acc_mag, IMU rot_angle, Thermopile, và ToF. Những khác biệt có thể xuất hiện do sự chênh lệch về sức mạnh cơ bắp trung bình, kích thước bàn tay hoặc phong cách thực hiện cử chỉ.\n\n**⏩ HCách phân tích & Hàm ý thực tiễn:**\n\n* **IMU – Acceleration:** Nếu nam giới thể hiện các đỉnh gia tốc cao hơn một cách nhất quán (do sức mạnh cơ bắp trung bình lớn hơn), mô hình có thể chuẩn hóa cường độ IMU hoặc học cách hiệu chỉnh theo giới tính.\n* **Rotation Angle:**Nam và nữ có thể có kiểu xoay cổ tay khác nhau; bộ phân loại không nên quá phụ thuộc vào một mẫu rot_angle nhất định mà bỏ qua yếu tố giới tính.\n* **Thermopile:** Nhiệt độ da cơ bản của nữ thường khác nhẹ so với nam, do đó các đường nhiệt có thể bắt đầu ở các giá trị khác nhau. Mô hình cần học cách tập trung vào sự thay đổi tương đối (relative changes) thay vì tuyệt đối, từ đó tăng độ ổn định khi dự đoán.\n* **ToF:** Hình dạng bàn tay của nam có thể tạo ra các cụm điểm ToF với kích thước khác biệt. Việc đưa giới tính vào mô hình giúp điều chỉnh ngưỡng khoảng cách và mô hình hóa không gian tiếp cận, giảm thiểu các lỗi phân loại khi đánh giá mức độ gần của tay đối với cơ thể.\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# ─── 1. Chọn biến nhóm cho ô này ─ ...\ngroup_col = \"sex\"\ngroup_map = {0: \"Female\", 1: \"Male\"}\n\n# ─── 2. Xây dựng tóm tắt cấp trình tự (nếu chưa tạo) ───────────────────────────────────────────────────\n# Sử dụng lại logic từ ô tổng quan trước đó, nhưng nhóm vào 'giới tính' thay vì 'thuận tay'.\n\nseq_stats = []\nfor seq_id, seq in train_df.groupby(\"sequence_id\"):\n    g = seq[group_col].iloc[0]\n    if pd.isna(g):\n        continue\n\n    # Phase masks\n    gest = seq[\"phase\"] == \"Gesture\"\n\n    # 1) IMU jerk\n    acc = np.sqrt(seq[\"acc_x\"]**2 + seq[\"acc_y\"]**2 + seq[\"acc_z\"]**2).values\n    max_jerk = np.abs(np.diff(acc)).max() if len(acc)>1 else np.nan\n\n    # 2) Phạm vi xoay trong cử chỉ\n    rot = 2*np.arccos(seq[\"rot_w\"].clip(-1,1))\n    rot_range = rot[gest].max() - rot[gest].min() if gest.any() else np.nan\n\n    # 3) Delta đỉnh nhiệt điện\n    thms = seq[[f\"thm_{i}\" for i in range(1,6)]].ffill().bfill().mean(axis=1)\n    baseline = thms[seq[\"phase\"]==\"Transition\"].median() if (seq[\"phase\"]==\"Transition\").any() else np.nan\n    thm_peak = thms[gest].max() - baseline if gest.any() else np.nan\n\n    # 4) Khoảng cách trung bình tối thiểu ToF\n    tcols = []\n    for i in range(1,6):\n        col = f\"tof_{i}_mean\"\n        if col not in seq:\n            pix = seq[[f\"tof_{i}_v{p}\" for p in range(64)]].replace(-1, np.nan)\n            seq[col] = pix.mean(axis=1)\n        tcols.append(seq[col])\n    tof_all = pd.concat(tcols, axis=1).mean(axis=1)\n    tof_min = tof_all.min()\n\n    seq_stats.append({\n        \"sequence_id\": seq_id,\n        group_col: g,\n        \"max_jerk\": max_jerk,\n        \"rot_range\": rot_range,\n        \"thm_peak_delta\": thm_peak,\n        \"tof_min_distance\": tof_min\n    })\n\nsummary = pd.DataFrame(seq_stats).dropna()\nsummary[\"Group\"] = summary[group_col].map(group_map)\n\n#─── 3. Vẽ biểu đồ phân phối được điều chỉnh ───────────────────────────────────────────────────────────────────────────\nsns.set_style(\"whitegrid\")\nfig, axes = plt.subplots(2, 2, figsize=(14, 10))\nplt.subplots_adjust(hspace=0.35, wspace=0.25)\n\nmetrics = [\n    (\"max_jerk\",         \"Max Jerk |Δacc| (m/s²)\"),\n    (\"rot_range\",        \"Rotation Range (rad)\"),\n    (\"thm_peak_delta\",   \"Thermopile ΔT Peak (°C)\"),\n    (\"tof_min_distance\", \"Min ToF Mean Dist.\")\n]\n\nfor ax, (col, lbl) in zip(axes.flatten(), metrics):\n    sns.violinplot(\n        data=summary,\n        x=\"Group\", y=col,\n        palette=\"Set2\",\n        inner=None,\n        ax=ax\n    )\n    sns.boxplot(\n        data=summary,\n        x=\"Group\", y=col,\n        width=0.2,\n        showcaps=True,\n        boxprops={'facecolor':'white'},\n        showfliers=False,\n        whiskerprops={'linewidth':2},\n        ax=ax\n    )\n    ax.set_title(lbl, fontsize=12)\n    ax.set_xlabel(\"\")\n    ax.set_ylabel(lbl, fontsize=10)\n\nfig.suptitle(\"Sex Comparison: Sequence‐Level Sensor Summaries (*Gesture Phase Only)\", fontsize=16)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:12:17.868098Z","iopub.execute_input":"2025-08-07T08:12:17.868455Z","iopub.status.idle":"2025-08-07T08:13:55.202517Z","shell.execute_reply.started":"2025-08-07T08:12:17.868431Z","shell.execute_reply":"2025-08-07T08:13:55.201329Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-\n**1.Max Jerk |Δacc|**\n-\n- Phân phối của nam và nữ chồng lấn gần như hoàn toàn; trung vị xấp xỉ ~4–5 m/s². Nữ có vẻ nhỉnh hơn rất nhẹ ở trung vị/upper quartile và cũng có một số outlier cao như nam.\n\n➡️ Khác biệt nhỏ; khó coi giới tính là yếu tố quyết định độ “giật” của cử chỉ.\n\n**2.Rotation Range (rad)**\n-\n- Trung vị hai nhóm gần ~0.25–0.35 rad; hình violin tương tự nhau. Đuôi trên của nam hơi dài hơn một chút.\n\n➡️ Không có sai khác rõ rệt về biên độ xoay trong pha Gesture.\n\n**3.Thermopile ΔT Peak (°C)**\n-\n-  Nữ có trung vị ΔT cao hơn nhẹ và phần đỉnh dày hơn (nhiều giá trị 3–6 °C). Nam xuất hiện nhiều giá trị âm hơn (giảm nhiệt).\n\n➡️ Gợi ý nữ có xu hướng đưa tay gần cảm biến hơn hoặc giữ lâu hơn; nam thường giữ xa/di chuyển nhanh hơn.\n\n**4.Min ToF Mean Distance**\n-\n- Trung vị hai nhóm tương đương (~70–90 đơn vị) với chồng lấn lớn. Nữ hơi dịch thấp (gần cảm biến hơn) nhưng chênh lệch nhỏ, cả hai đều có nhiều điểm rất gần (≈0).\n\n➡️ Khoảng cách tới cảm biến gần như giống nhau, nữ có xu hướng gần hơn đôi chút.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Giới tính không tạo khác biệt mạnh ở các đặc trưng động học chính (jerk, rotation).\n\n- Sự khác biệt đáng chú ý nhất là ở cảm biến gần–xa: nữ có ΔT cao hơn và ToF thấp hơn nhẹ, cho thấy hành vi tiếp cận cảm biến gần hơn một chút.\n\n- Với mô hình:\n\n  * Có thể giữ sex như meta‑feature nhưng không kỳ vọng sức phân tách lớn; quan trọng hơn là tuổi/chiều cao/chiều dài tay.\n\n  * Tạo feature theo khoảng cách/tiệm cận (ToF_min, ΔThm) và chuẩn hoá theo subject để giảm chênh lệch thói quen cử chỉ.\n\n  * Nếu muốn khẳng định, kiểm định Mann–Whitney U cho từng đặc trưng và/hoặc chạy một bộ phân loại chỉ dùng 4 feature này; AUC gần 0.5 sẽ xác nhận ảnh hưởng giới tính là yếu.","metadata":{}},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# ─── Tính toán thời lượng pha theo từng chuỗi để so sánh Giới tính ───────────────────────────────────────────────────────────\n# Giả sử train_df đã được hợp nhất với train_dem_df\n\n# Nhóm theo sequence_id và giới tính, đếm số khung hình trong mỗi pha\ndur_df_sex = (\n    train_df\n    .groupby(['sequence_id', 'sex'])['phase']\n    .value_counts()\n    .unstack(fill_value=0)\n    .reset_index()\n)\n\n# Chỉ giữ lại Chuyển tiếp & Cử chỉ\ndur_df_sex = dur_df_sex[['sequence_id', 'sex', 'Transition', 'Gesture']]\n\n# Tan chảy thành dạng dài\ndur_long_sex = dur_df_sex.melt(\n    id_vars=['sequence_id', 'sex'],\n    value_vars=['Transition', 'Gesture'],\n    var_name='phase',\n    value_name='duration_frames'\n)\n\n# Ánh xạ giới tính vào nhãn\ndur_long_sex['Group'] = dur_long_sex['sex'].map({0: 'Female', 1: 'Male'})\n\n# ─── Vẽ các hộp biểu đồ ngang cạnh nhau ─ ...\nsns.set_style(\"whitegrid\")\nfig, axes = plt.subplots(1, 2, figsize=(14, 6), sharey=True)\nplt.subplots_adjust(wspace=0.2)\n\nfor ax, group_label in zip(axes, ['Female', 'Male']):\n    subset = dur_long_sex[dur_long_sex['Group'] == group_label]\n    sns.boxplot(\n        data=subset,\n        x='duration_frames',\n        y='phase',\n        orient='h',\n        palette={'Transition':'lightgray', 'Gesture':'lightcoral'},\n        ax=ax\n    )\n    ax.set_title(f\"{group_label}\", fontsize=12)\n    ax.set_xlabel(\"Duration (frames)\", fontsize=10)\n    ax.set_ylabel(\"Phase\", fontsize=10)\n    ax.grid(True)\n\nfig.suptitle(\"Sex Comparison: Transition vs. Gesture Duration\", fontsize=14, y=1.02)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:13:55.203661Z","iopub.execute_input":"2025-08-07T08:13:55.204393Z","iopub.status.idle":"2025-08-07T08:13:55.812962Z","shell.execute_reply.started":"2025-08-07T08:13:55.204363Z","shell.execute_reply":"2025-08-07T08:13:55.812043Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-\n**1.Transition phase**\n-\n\n- Cả Male và Female: trung vị ~40 frames, phân bố rất tương đồng.\n\n- Có nhiều outlier dài (>300 frames), một số cực trị tới ~600–700 frames.\n\n- Đuôi phải của cả hai nhóm dài gần như nhau → cho thấy thời lượng Transition dài bất thường xuất hiện ở cả nam lẫn nữ.\n\n**2.Gesture phase**\n-\n\n- Trung vị thấp (~20 frames) và rất ổn định cho cả hai giới.\n\n- Độ phân tán nhỏ; một vài outlier ~70–90 frames, xuất hiện ở cả hai nhóm.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Giới tính không tạo khác biệt rõ rệt về thời lượng pha Transition hay Gesture.\n\n- Cả nam và nữ đều có tỷ lệ outlier Transition cao, có thể do chậm bắt đầu hoặc tạm dừng lâu trước khi thực hiện cử chỉ.","metadata":{}},{"cell_type":"code","source":"# ─── Tế bào C: So sánh giới tính của IMU, Rot, Thermopile và ToF ────────────────────────────────────────────────────────────────────────\n# (2) Chọn một cử chỉ để so sánh (thay đổi nếu cần)\ngesture_to_plot = \"Forehead - scratch\"\n\n# (3) Lọc chỉ các hàng có cử chỉ đó, chia theo giới tính (0=nữ, 1=nam)\ndf_gesture = train_df[train_df[\"gesture\"] == gesture_to_plot].copy()\nfemale_group = df_gesture[df_gesture[\"sex\"] == 0]\nmale_group   = df_gesture[df_gesture[\"sex\"] == 1]\n\nif (female_group[\"sequence_id\"].nunique() == 0) or (male_group[\"sequence_id\"].nunique() == 0):\n    raise ValueError(\"Not enough examples for each sex. Try a different gesture.\")\n\nfemale_seq = female_group[\"sequence_id\"].unique()[0]\nmale_seq   = male_group[\"sequence_id\"].unique()[0]\n\ndf_fem = train_df[train_df[\"sequence_id\"] == female_seq].sort_values(\"sequence_counter\")\ndf_male= train_df[train_df[\"sequence_id\"] == male_seq].sort_values(\"sequence_counter\")\n\n# Tính toán các tính năng IMU được suy ra\nfor df in (df_fem, df_male):\n    df[\"acc_mag\"] = np.sqrt(df[\"acc_x\"]**2 + df[\"acc_y\"]**2 + df[\"acc_z\"]**2)\n    df[\"rot_w_clipped\"] = df[\"rot_w\"].clip(-1, 1)\n    df[\"rot_angle\"] = 2 * np.arccos(df[\"rot_w_clipped\"])\n\n# Tái sử dụng chức năng đổ bóng\ndef shade_phases(ax, seq_df):\n    used = set()\n    for phase_label, color in [(\"Transition\", \"lightgray\"), (\"Gesture\", \"lightcoral\")]:\n        mask = seq_df[\"phase\"] == phase_label\n        if not mask.any():\n            continue\n        idxs = seq_df.index[mask]\n        diffs = np.where(np.diff(idxs) != 1)[0]\n        spans = []\n        start = idxs[0]\n        for b in diffs:\n            end = idxs[b]\n            spans.append((start, end))\n            start = idxs[b + 1]\n        spans.append((start, idxs[-1]))\n        for (s, e) in spans:\n            t0 = seq_df.loc[s, \"sequence_counter\"]\n            t1 = seq_df.loc[e, \"sequence_counter\"]\n            label = phase_label if phase_label not in used else None\n            ax.axvspan(t0, t1, color=color, alpha=0.3, label=label)\n            used.add(phase_label)\n\n# --------------------------------------------------------------------------------\n# C.1: IMU – Độ lớn gia tốc\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_fem, df_male],\n    [f\"Female (Subj: {df_fem['subject'].iloc[0]}, Sex=0)\",\n     f\"Male   (Subj: {df_male['subject'].iloc[0]}, Sex=1)\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"acc_mag\"],\n            color=\"tab:blue\", lw=1.5, label=\"|acc|(m/s²)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Acceleration Magnitude\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Sex: Acceleration Magnitude – Female vs. Male\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# C.2: IMU – Góc quay\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_fem, df_male],\n    [f\"Female (Subj: {df_fem['subject'].iloc[0]}, Sex=0)\",\n     f\"Male   (Subj: {df_male['subject'].iloc[0]}, Sex=1)\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"rot_angle\"],\n            color=\"tab:orange\", lw=1.5, label=\"rot_angle (rad)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Rotation Angle (rad)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Sex: Rotation Angle – Female vs. Male\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# C.3: Nhiệt điện – 5 kênh + Trung bình\n# --------------------------------------------------------------------------------\nfor df in (df_fem, df_male):\n    thm_cols = [f\"thm_{i}\" for i in range(1,6)]\n    df[\"thm_mean\"] = df[thm_cols].mean(axis=1)\n\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_fem, df_male],\n    [f\"Female (Subj: {df_fem['subject'].iloc[0]}, Sex=0)\",\n     f\"Male   (Subj: {df_male['subject'].iloc[0]}, Sex=1)\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    for col in [f\"thm_{i}\" for i in range(1,6)]:\n        ax.plot(times, df_seq[col], lw=1, label=col, alpha=0.7)\n    ax.plot(times, df_seq[\"thm_mean\"], color=\"black\", lw=2.5, label=\"thm_mean\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Thermopile (°C)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\", ncol=2)\n    ax.grid(True)\nplt.suptitle(\"Sex: Thermopile – Female vs. Male\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# C.4: ToF – Năm kênh khoảng cách trung bình\n# --------------------------------------------------------------------------------\nfor df in (df_fem, df_male):\n    for i_sensor in range(1,6):\n        pixel_cols = [f\"tof_{i_sensor}_v{pix}\" for pix in range(64)]\n        df[f\"tof_{i_sensor}_mean\"] = df[pixel_cols].replace(-1, np.nan).mean(axis=1)\n\nmean_cols = [f\"tof_{i}_mean\" for i in range(1,6)]\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_fem, df_male],\n    [f\"Female (Subj: {df_fem['subject'].iloc[0]}, Sex=0)\",\n     f\"Male   (Subj: {df_male['subject'].iloc[0]}, Sex=1)\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    for col in mean_cols:\n        ax.plot(times, df_seq[col], lw=1, label=col)\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"ToF Mean Distance\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Sex: ToF Mean Distance – Female vs. Male\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:13:55.814024Z","iopub.execute_input":"2025-08-07T08:13:55.814423Z","iopub.status.idle":"2025-08-07T08:13:59.134604Z","shell.execute_reply.started":"2025-08-07T08:13:55.814386Z","shell.execute_reply":"2025-08-07T08:13:59.133572Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-\n**1.Acceleration Magnitude**\n-\n- Female: ổn định quanh 10 m/s², chỉ có một spike nhỏ ở Transition; Gesture dao động nhẹ.\n\n- Male: spike mạnh (~18 m/s²) ở đầu Transition; trong Gesture biên độ dao động lớn hơn và tần suất thay đổi nhanh hơn.\n\n- Nhận xét: Nam có động tác mạnh hơn và biến thiên nhiều hơn trong Gesture.\n\n**2.Rotation Angle**\n-\n- Female: tăng từ ~1.45 rad lên ~2 rad trong Transition, plateau phần lớn Gesture, cuối Gesture tăng vọt (~2.6 rad).\n\n- Male: tăng mạnh từ ~1.9 rad lên ~2.9 rad ở Transition, plateau, sau đó giảm nhẹ ở cuối Gesture.\n\n- Nhận xét: Nam có biên độ xoay lớn hơn ngay từ Transition, nữ thay đổi nhiều hơn ở cuối Gesture.\n\n**3.Thermopile**\n-\n- Female: nhiệt độ trung bình tăng rõ từ ~25°C lên ~29°C trong Transition và giữ ổn định trong Gesture; một số kênh có dao động lớn.\n\n- Male: tăng từ ~24°C lên ~29°C nhanh chóng ở đầu Transition, giữ ổn định suốt Gesture, ít dao động hơn.\n\n- Nhận xét: Cả hai tiếp cận cảm biến nhiệt gần như nhau; nữ có nhiều biến động hơn ở từng kênh.\n\n**4.ToF Mean Distance**\n-\n- Female: các kênh giảm mạnh từ ~200–250 xuống ~80–100 units ở Transition, giữ tương đối ổn định; một số kênh dao động mạnh trong Gesture.\n\n- Male: giảm từ ~200 xuống ~70–100 units, ổn định hơn, ít dao động.\n\n- Nhận xét: Khoảng cách tiếp cận cảm biến tương tự, nhưng nữ có sự thay đổi giữa các kênh nhiều hơn.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Giới tính trong ví dụ này cho thấy một số khác biệt:\n\n  * Nam có gia tốc lớn hơn và dao động mạnh hơn trong Gesture.\n\n  * Nam bắt đầu Gesture với góc xoay lớn hơn và duy trì ổn định, nữ thay đổi nhiều hơn ở cuối.\n\n  * Tiếp cận cảm biến nhiệt và ToF tương tự, nhưng nữ có dao động kênh nhiều hơn.","metadata":{}},{"cell_type":"markdown","source":"#### 📌  Đặc trưng đang xem xét: **Chiều dài từ vai đến cổ tay**\n\nTrong phần này, dữ liệu được tách theo nhóm chiều dài tay (tay ngắn vs. tay dài) cho một cử chỉ cụ thể (ví dụ: “Eyelash – pull hair”). Ta trực quan hóa cùng lúc bốn tín hiệu cảm biến. Người có cánh tay dài hơn có thể tiếp cận khuôn mặt với góc độ và tốc độ khác, từ đó ảnh hưởng tới toàn bộ dấu hiệu từ cảm biến.\n\n**⏩ How to Interpret & Implications:**\n\n* **IMU:** Những người có tay dài thường có đồ thị acc_mag mượt hơn (ít thay đổi đột ngột hơn) vì cánh tay dài tạo ra gia tốc góc chậm hơn. Mô hình nên học cách chuẩn hóa theo chiều dài tay để tránh nhầm lẫn giữa độ mượt của chuyển động với việc không thực hiện cử chỉ mạnh.\n* **Rotation Angle:**  Người tay dài có thể có đỉnh rot_angle thấp hơn (do quỹ đạo lớn hơn nhưng tốc độ xoay chậm hơn). Bộ lọc thời gian của mô hình cần điều chỉnh cho phù hợp, tránh hiểu nhầm xoay chậm thành cử chỉ khác.\n* **Thermopile:** Tay dài có thể khiến các cảm biến nhiệt phát hiện nhiệt muộn hơn (do cổ tay phải di chuyển xa hơn mới tiếp cận được bề mặt cơ thể). Việc huấn luyện mô hình với cả hai nhóm chiều dài tay giúp tránh việc nhầm lẫn sự trễ này thành không có tiếp xúc.\n* **ToF:** Đối với người tay dài, khoảng cách ToF bắt đầu giảm ở giá trị xa hơn (tay di chuyển qua nhiều không gian hơn trước khi chạm vào mục tiêu). Mô hình cần học cách đặt ngưỡng khoảng cách phù hợp theo chiều dài tay để giảm lỗi bỏ sót khi tiếp xúc diễn ra ở xa hơn.","metadata":{}},{"cell_type":"markdown","source":"⏩ Plots for All Subjects and One Gesture","metadata":{}},{"cell_type":"markdown","source":"**Mục tiêu:** So sánh đặc trưng cảm biến giữa hai nhóm chiều dài cẳng tay (Short ≤ 52.0 cm, Long > 52.0 cm) trong pha Gesture của cử chỉ \"Eyelash - pull hair\".\n\n**Bước xử lý:**\n-\n\n- Lọc dữ liệu theo cử chỉ cần phân tích.\n\n- Xác định median chiều dài cẳng tay → phân nhóm Short và Long.\n\n- Tính 4 chỉ số cho mỗi chuỗi:\n\n  * Max Jerk |Δacc|: giá trị gia tốc thay đổi lớn nhất.\n\n  * Rotation Range: chênh lệch góc quay trong pha Gesture.\n\n  * Thermopile Peak ΔT: nhiệt độ cao nhất trừ baseline (pha Transition).\n\n  * Minimum ToF Mean Dist.: khoảng cách trung bình nhỏ nhất từ ToF.\n\n- Tạo DataFrame tóm tắt và vẽ violin plot + boxplot cho từng chỉ số.","metadata":{}},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# ─── Tóm tắt cấp độ chuỗi cho cẳng tay ngắn so với cẳng tay dài ───────────────────────────────────────────────────────────────────\n# Chúng tôi xác định 4 số liệu được điều chỉnh như trước, nhưng nhóm theo\n# 'ngắn' = cm từ vai đến cổ tay ≤ trung vị\n# 'dài' = cm từ vai đến cổ tay > trung vị\n\n# 1) Chọn cử chỉ (giống như vẽ ô)\ngesture_to_plot = \"Eyelash - pull hair\"\ndf_g = train_df[train_df[\"gesture\"] == gesture_to_plot].copy()\n\n# 2) Xác định chiều dài cẳng tay trung bình\nmedian_len = df_g[\"shoulder_to_wrist_cm\"].median()\n\n# 3) Tóm tắt xây dựng\nrecords = []\nfor seq_id, seq in train_df[train_df[\"gesture\"] == gesture_to_plot].groupby(\"sequence_id\"):\n    L = seq[\"shoulder_to_wrist_cm\"].iloc[0]\n    grp = \"Short\" if L <= median_len else \"Long\"\n    # IMU jerk\n    acc_mag = np.sqrt(seq[\"acc_x\"]**2 + seq[\"acc_y\"]**2 + seq[\"acc_z\"]**2)\n    jerks = np.abs(np.diff(acc_mag))\n    max_jerk = jerks.max() if len(jerks)>0 else np.nan\n    # Phạm vi quay trong Cử chỉ\n    rot = 2*np.arccos(seq[\"rot_w\"].clip(-1,1))\n    rot_rng = rot[seq[\"phase\"]==\"Gesture\"].max() - rot[seq[\"phase\"]==\"Gesture\"].min() \\\n              if (seq[\"phase\"]==\"Gesture\").any() else np.nan\n    # Delta đỉnh nhiệt điện\n    thm_cols = [f\"thm_{i}\" for i in range(1,6)]\n    thm_mean = seq[thm_cols].mean(axis=1)\n    base = thm_mean[seq[\"phase\"]==\"Transition\"].median()\n    peak_dt = thm_mean[seq[\"phase\"]==\"Gesture\"].max() - base \\\n              if (seq[\"phase\"]==\"Gesture\").any() else np.nan\n    # ToF min trung bình\n    tof_means = []\n    for i in range(1,6):\n        pix = seq[[f\"tof_{i}_v{p}\" for p in range(64)]].replace(-1,np.nan)\n        tof_means.append(pix.mean(axis=1))\n    tof_all = pd.concat(tof_means,axis=1).mean(axis=1)\n    min_tof = tof_all.min()\n    records.append({\n        \"Group\":         grp,\n        \"max_jerk\":      max_jerk,\n        \"rot_range\":     rot_rng,\n        \"thm_peak_dt\":   peak_dt,\n        \"tof_min_dist\":  min_tof\n    })\n\nsummary = pd.DataFrame(records).dropna()\n\n# ─── Vẽ violin+box được thiết kế riêng cho từng số liệu ─ ...\nsns.set_style(\"whitegrid\")\nfig, axes = plt.subplots(2, 2, figsize=(14,10))\nplt.subplots_adjust(hspace=0.4, wspace=0.3)\n\nmetrics = [\n    (\"max_jerk\",     \"Max Jerk |Δacc| (m/s²)\"),\n    (\"rot_range\",    \"Rotation Range in Gesture (rad)\"),\n    (\"thm_peak_dt\",  \"Thermopile Peak ΔT (°C)\"),\n    (\"tof_min_dist\", \"Minimum ToF Mean Dist.\")\n]\n\nfor ax, (col, label) in zip(axes.flatten(), metrics):\n    sns.violinplot(data=summary, x=\"Group\", y=col, palette=\"Set2\", ax=ax, inner=None)\n    sns.boxplot(data=summary, x=\"Group\", y=col,\n                width=0.2, showcaps=True, boxprops={'facecolor':'white'},\n                showfliers=False, whiskerprops={'linewidth':2}, ax=ax)\n    ax.set_title(label, fontsize=12)\n    ax.set_xlabel(\"\")\n    ax.set_ylabel(label, fontsize=10)\n\nfig.suptitle(f\"Forearm Length Comparison (≤{median_len:.1f}cm vs >{median_len:.1f}cm) (*Gesture Phase Only)\", fontsize=16)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:13:59.135786Z","iopub.execute_input":"2025-08-07T08:13:59.136088Z","iopub.status.idle":"2025-08-07T08:14:06.540616Z","shell.execute_reply.started":"2025-08-07T08:13:59.136048Z","shell.execute_reply":"2025-08-07T08:14:06.539557Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-\n**1.Max Jerk |Δacc|**\n-\n- Hai nhóm Short và Long có phân bố tương tự, median ~3 m/s².\n\n- Một số outlier cao tới >20 m/s² ở cả hai nhóm.\n\n- Không có khác biệt rõ ràng về biên độ gia tốc cực đại.\n\n**2.Rotation Range in Gesture**\n-\n- Short: median ~0.15 rad, có outlier lớn ~1.75 rad.\n\n- Long: median tương tự, nhưng ít outlier cực trị hơn.\n\n- Chênh lệch nhỏ, nhưng nhóm Short có xu hướng biến động lớn hơn.\n\n**3.Thermopile Peak ΔT**\n-\n- Median hai nhóm gần giống (~2–3°C).\n\n- Short có vài giá trị ΔT âm lớn (có thể do cảm biến đọc thấp hơn baseline), trong khi Long ổn định hơn.\n\n- Biểu đồ cho thấy phân bố nhiệt độ không khác biệt đáng kể.\n\n**4.Minimum ToF Mean Dist.**\n-\n- Cả hai nhóm có median ~80–90 units.\n\n- Long có phân bố rộng hơn một chút và vài giá trị gần 0 → nghĩa là tiếp cận rất sát cảm biến ở một số mẫu.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Chiều dài cẳng tay không tạo khác biệt rõ rệt ở 4 chỉ số phân tích, mặc dù:\n\n  * Nhóm Short có biến động góc xoay và ΔT lớn hơn.\n\n  * Nhóm Long có phạm vi khoảng cách ToF rộng hơn, một số tiếp cận cực sát.\n\n- Với mô hình ML, chiều dài cẳng tay có thể vẫn hữu ích như meta feature, nhưng không phải là yếu tố tách biệt mạnh cho riêng cử chỉ này.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# ─── Tính toán thời lượng pha cho mỗi chuỗi cho tất cả các chuỗi ──────────────────────────────────────────────────\n# Giả sử train_df đã có cột shoulder_to_wrist_cm được hợp nhất và cột 'pha'\n\n# Đánh dấu mỗi chuỗi là Cánh tay ngắn so với Cánh tay dài dựa trên chiều dài trung bình của vai_to_wrist_cm trên tất cả các chuỗi\nmed = train_df.groupby('sequence_id')['shoulder_to_wrist_cm'].first().median()\nseq_meta = (\n    train_df\n    .groupby('sequence_id')\n    .agg({'shoulder_to_wrist_cm':'first'})\n    .reset_index()\n)\nseq_meta['ArmGroup'] = np.where(seq_meta['shoulder_to_wrist_cm'] <= med,\n                                'Short Arm', 'Long Arm')\n\n# Đếm số khung hình trên mỗi pha trên mỗi chuỗi\ndur = (\n    train_df\n    .groupby(['sequence_id','phase'])['sequence_counter']\n    .count()\n    .unstack(fill_value=0)\n    .reset_index()\n)\n\n# Hợp nhất trong nhãn nhóm cánh tay\ndur = dur.merge(seq_meta[['sequence_id','ArmGroup']], on='sequence_id')\n\n# Tan chảy thành dạng dài\ndur_long = dur.melt(id_vars=['sequence_id','ArmGroup'],\n                    value_vars=['Transition','Gesture'],\n                    var_name='Phase', value_name='Duration')\n\n# ─── Vẽ biểu đồ hộp theo chiều ngang: Cánh tay ngắn so với cánh tay dài ─ ...\nsns.set_style(\"whitegrid\")\nfig, axes = plt.subplots(1, 2, figsize=(14,5), sharey=True)\nfor ax, grp in zip(axes, ['Short Arm','Long Arm']):\n    sub = dur_long[dur_long['ArmGroup']==grp]\n    sns.boxplot(x='Duration', y='Phase', data=sub,\n                palette=['lightgray','lightcoral'], ax=ax)\n    ax.set_title(f\"{grp} (≤ {med:.1f} cm)\", fontsize=12)\n    ax.set_xlabel(\"Duration (frames)\", fontsize=10)\n    ax.set_ylabel(\"Phase\", fontsize=10)\n    ax.grid(True)\n\nplt.suptitle(\"Phase Durations by Arm Length Group\", fontsize=14, y=1.05)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:14:06.541713Z","iopub.execute_input":"2025-08-07T08:14:06.542049Z","iopub.status.idle":"2025-08-07T08:14:07.205970Z","shell.execute_reply.started":"2025-08-07T08:14:06.542021Z","shell.execute_reply":"2025-08-07T08:14:07.205101Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ**\n-\n- Cả hai nhóm (Short Arm, Long Arm):\n\n  * Pha Transition có median khoảng 40–50 frames, phân bố rộng, nhiều outlier kéo dài đến hơn 600 frames.\n\n  * Pha Gesture có median ngắn hơn nhiều (khoảng 20–25 frames), phân bố hẹp, ít outlier hơn.\n\n- So sánh giữa hai nhóm:\n\n  * Median và phạm vi phân bố của cả hai pha gần như tương đương giữa nhóm cánh tay ngắn và dài.\n\n  * Outlier ở pha Transition xuất hiện ở cả hai nhóm, cho thấy không bị ảnh hưởng nhiều bởi chiều dài cánh tay.\n\n  * Pha Gesture khá ổn định, không có khác biệt rõ ràng về thời lượng giữa hai nhóm.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Chiều dài cánh tay không tạo ra khác biệt đáng kể về thời lượng của các pha Transition và Gesture.\n\n- Cả hai nhóm đều có Transition dài hơn Gesture đáng kể và phân bố Transition rộng hơn nhiều.","metadata":{}},{"cell_type":"markdown","source":"⏩ Biểu đồ cho các đối tượng được chọn ngẫu nhiên và một cử chỉ","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"# ─── Ô D: So sánh chiều dài từ vai đến cổ tay (ngắn so với dài) đối với IMU, Rot, Thermopile và ToF ───────────\n# (2) Chọn một cử chỉ để so sánh (thay đổi nếu cần)\ngesture_to_plot = \"Eyelash - pull hair\"\n\n# (3) Lọc theo cử chỉ đó và chia theo chiều dài từ vai đến cổ tay bằng cách sử dụng trung vị\ndf_gesture = train_df[train_df[\"gesture\"] == gesture_to_plot].copy()\nmedian_shoulder = df_gesture[\"shoulder_to_wrist_cm\"].median()\n\nshort_group = df_gesture[df_gesture[\"shoulder_to_wrist_cm\"] <= median_shoulder]\nlong_group  = df_gesture[df_gesture[\"shoulder_to_wrist_cm\"]  > median_shoulder]\n\nif (short_group[\"sequence_id\"].nunique() == 0) or (long_group[\"sequence_id\"].nunique() == 0):\n    raise ValueError(\"Not enough examples in one of the length‐groups. Try another gesture or adjust threshold.\")\n\nshort_seq = short_group[\"sequence_id\"].unique()[0]\nlong_seq  = long_group[\"sequence_id\"].unique()[0]\n\ndf_short = train_df[train_df[\"sequence_id\"] == short_seq].sort_values(\"sequence_counter\")\ndf_long  = train_df[train_df[\"sequence_id\"] == long_seq].sort_values(\"sequence_counter\")\n\n# Tính toán các tính năng có nguồn gốc từ IMU\nfor df in (df_short, df_long):\n    df[\"acc_mag\"] = np.sqrt(df[\"acc_x\"]**2 + df[\"acc_y\"]**2 + df[\"acc_z\"]**2)\n    df[\"rot_w_clipped\"] = df[\"rot_w\"].clip(-1, 1)\n    df[\"rot_angle\"] = 2 * np.arccos(df[\"rot_w_clipped\"])\n\n# Tái sử dụng chức năng đổ bóng\ndef shade_phases(ax, seq_df):\n    used = set()\n    for phase_label, color in [(\"Transition\", \"lightgray\"), (\"Gesture\", \"lightcoral\")]:\n        mask = seq_df[\"phase\"] == phase_label\n        if not mask.any():\n            continue\n        idxs = seq_df.index[mask]\n        diffs = np.where(np.diff(idxs) != 1)[0]\n        spans = []\n        start = idxs[0]\n        for b in diffs:\n            end = idxs[b]\n            spans.append((start, end))\n            start = idxs[b + 1]\n        spans.append((start, idxs[-1]))\n        for (s, e) in spans:\n            t0 = seq_df.loc[s, \"sequence_counter\"]\n            t1 = seq_df.loc[e, \"sequence_counter\"]\n            label = phase_label if phase_label not in used else None\n            ax.axvspan(t0, t1, color=color, alpha=0.3, label=label)\n            used.add(phase_label)\n\n# --------------------------------------------------------------------------------\n# D.1: IMU – Độ lớn gia tốc\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_short, df_long],\n    [f\"Short Arm (Shoulder‐Wrist ≤ {median_shoulder:.1f} cm)\",\n     f\"Long Arm (Shoulder‐Wrist > {median_shoulder:.1f} cm)\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"acc_mag\"],\n            color=\"tab:blue\", lw=1.5, label=\"|acc|(m/s²)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Acceleration Magnitude\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Arm Length: Acceleration Magnitude – Short vs. Long\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# D.2: IMU – Góc quay\n# --------------------------------------------------------------------------------\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_short, df_long],\n    [f\"Short Arm (Shoulder‐Wrist ≤ {median_shoulder:.1f} cm)\",\n     f\"Long Arm (Shoulder‐Wrist > {median_shoulder:.1f} cm)\"]\n):\n    ax.plot(df_seq[\"sequence_counter\"], df_seq[\"rot_angle\"],\n            color=\"tab:orange\", lw=1.5, label=\"rot_angle (rad)\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Rotation Angle (rad)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Arm Length: Rotation Angle – Short vs. Long\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# D.3: Nhiệt điện – 5 kênh + Trung bình\n# --------------------------------------------------------------------------------\nfor df in (df_short, df_long):\n    thm_cols = [f\"thm_{i}\" for i in range(1,6)]\n    df[\"thm_mean\"] = df[thm_cols].mean(axis=1)\n\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_short, df_long],\n    [f\"Short Arm (Shoulder‐Wrist ≤ {median_shoulder:.1f} cm)\",\n     f\"Long Arm (Shoulder‐Wrist > {median_shoulder:.1f} cm)\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    for col in [f\"thm_{i}\" for i in range(1,6)]:\n        ax.plot(times, df_seq[col], lw=1, label=col, alpha=0.7)\n    ax.plot(times, df_seq[\"thm_mean\"], color=\"black\", lw=2.5, label=\"thm_mean\")\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"Thermopile (°C)\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\", ncol=2)\n    ax.grid(True)\nplt.suptitle(\"Arm Length: Thermopile – Short vs. Long\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()\n\n# --------------------------------------------------------------------------------\n# D.4: ToF – Năm kênh khoảng cách trung bình\n# --------------------------------------------------------------------------------\nfor df in (df_short, df_long):\n    for i_sensor in range(1,6):\n        pixel_cols = [f\"tof_{i_sensor}_v{pix}\" for pix in range(64)]\n        df[f\"tof_{i_sensor}_mean\"] = df[pixel_cols].replace(-1, np.nan).mean(axis=1)\n\nmean_cols = [f\"tof_{i}_mean\" for i in range(1,6)]\nfig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)\nfor ax, df_seq, title in zip(\n    axes,\n    [df_short, df_long],\n    [f\"Short Arm (Shoulder‐Wrist ≤ {median_shoulder:.1f} cm)\",\n     f\"Long Arm (Shoulder‐Wrist > {median_shoulder:.1f} cm)\"]\n):\n    times = df_seq[\"sequence_counter\"]\n    for col in mean_cols:\n        ax.plot(times, df_seq[col], lw=1, label=col)\n    shade_phases(ax, df_seq)\n    ax.set_title(f\"{title}\\nGesture: {gesture_to_plot}\", fontsize=11)\n    ax.set_xlabel(\"Sequence Counter\", fontsize=9)\n    ax.set_ylabel(\"ToF Mean Distance\", fontsize=9)\n    ax.legend(loc=\"upper right\", fontsize=\"small\")\n    ax.grid(True)\nplt.suptitle(\"Arm Length: ToF Mean Distance – Short vs. Long\", fontsize=13)\nplt.tight_layout(rect=[0, 0, 1, 0.95])\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:14:07.206988Z","iopub.execute_input":"2025-08-07T08:14:07.207249Z","iopub.status.idle":"2025-08-07T08:14:10.323097Z","shell.execute_reply.started":"2025-08-07T08:14:07.207229Z","shell.execute_reply":"2025-08-07T08:14:10.322061Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ chi tiết**\n--","metadata":{}},{"cell_type":"markdown","source":"**Gia tốc (IMU):**\n-\n\n- Tay ngắn: Biến thiên gia tốc mạnh hơn ở giai đoạn chuyển tiếp, giảm ổn định hơn khi vào cử chỉ.\n\n- Tay dài: Gia tốc ổn định hơn, biên độ dao động thấp hơn, ít đột biến.\n\n**Góc quay (IMU):**\n-\n\n- Tay ngắn: Góc quay thay đổi rõ, đặc biệt tăng khi vào pha Gesture.\n\n- Tay dài: Góc quay thấp và thay đổi ít hơn, cho thấy chuyển động nhẹ nhàng hơn.\n\n**Thermopile:**\n-\n\n- Tay ngắn: Nhiệt độ tăng dần khi vào Gesture, nhưng biên độ dao động giữa các kênh cao.\n\n- Tay dài: Nhiệt độ ổn định hơn và tăng nhẹ, các kênh đồng nhất hơn.\n\n**ToF:**\n-\n\n- Tay ngắn: Khoảng cách thay đổi mạnh khi chuyển sang Gesture, dao động nhiều hơn.\n\n- Tay dài: Khoảng cách ổn định hơn, biến thiên ít, cho thấy chuyển động đều và kiểm soát tốt hơn.","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n\n- Người có tay dài thực hiện cử chỉ ổn định hơn: dao động gia tốc, góc quay, nhiệt độ và khoảng cách ít biến thiên.\n\n- Người có tay ngắn có xu hướng chuyển động nhanh hơn, dao động mạnh hơn khi chuyển pha, dẫn đến sự thay đổi rõ rệt trong dữ liệu cảm biến.\n\n- Điều này gợi ý chiều dài tay có thể ảnh hưởng đến mức độ ổn định và dạng tín hiệu trong các cảm biến, từ đó ảnh hưởng đến mô hình nhận diện cử chỉ.","metadata":{}},{"cell_type":"markdown","source":"### 3.3.3 Correlation analysis","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport pandas as pd\n\n# Kết hợp đào tạo và kiểm tra cho cả hai tập dữ liệu\ncombined_main = pd.concat([train_df, test_df], axis=0)\ncombined_demo = pd.concat([train_dem_df, test_dem_df], axis=0)\n\n# Đặt mục tiêu\ntarget_variable = 'gesture'\n\n# Xác định tiền tố cảm biến và danh sách loại trừ\nsensor_prefixes = ('acc_', 'rot_', 'thm_', 'tof_')\nexcluded_columns = [target_variable]\n\n# Xác định các biến số có thể sử dụng (không phải cảm biến, chỉ số)\ndef get_numerical_columns(df, exclude_prefixes, excluded_cols):\n    return [col for col in df.columns \n            if pd.api.types.is_numeric_dtype(df[col]) \n            and not col.startswith(exclude_prefixes)\n            and col not in excluded_cols]\n\nmain_vars = get_numerical_columns(combined_main, sensor_prefixes, excluded_columns)\ndemo_vars = get_numerical_columns(combined_demo, (), excluded_columns)\n\n# Kết hợp cả hai để có mối tương quan toàn cầu\ncombined_data = pd.concat([\n    combined_main[main_vars].reset_index(drop=True),\n    combined_demo[demo_vars].reset_index(drop=True)\n], axis=1)\n\n# Tính toán tương quan và che dấu tam giác trên\ncorr_all = combined_data.corr()\nmask_all = np.triu(np.ones_like(corr_all, dtype=bool))\n\n# Plot\nplt.figure(figsize=(18, 10))\nax = sns.heatmap(\n    corr_all, mask=mask_all, cmap='viridis', annot=True, \n    square=False, linewidths=.5, annot_kws={\"size\": 12}\n)\nplt.title('Correlation Heatmap — Combined (Demographic + Main) Data', fontsize=16)\n\n# Rotate x-axis tick labels\nax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right', fontsize=11)\nax.set_yticklabels(ax.get_yticklabels(), fontsize=11)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:14:10.324294Z","iopub.execute_input":"2025-08-07T08:14:10.324643Z","iopub.status.idle":"2025-08-07T08:14:12.110542Z","shell.execute_reply.started":"2025-08-07T08:14:10.324612Z","shell.execute_reply":"2025-08-07T08:14:12.109471Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích biểu đồ tương quan**\n-","metadata":{}},{"cell_type":"markdown","source":"**🟨 Các mối tương quan mạnh đáng chú ý:**\n-\n- **Tuổi (age) vs. adult_child:**\n\n  * Hệ số tương quan 0.83 → cực kỳ mạnh.\n\n  * Giải thích: adult_child là biến nhị phân phản ánh việc một người có phải trẻ em không, nên tất yếu sẽ liên hệ chặt chẽ với tuổi.\n\n- **shoulder_to_wrist_cm vs. elbow_to_wrist_cm:**\n\n  * Tương quan 0.67 → rất cao, phản ánh tính tỷ lệ giải phẫu cơ thể.\n\n- **height_cm vs. shoulder_to_wrist_cm:**\n\n  * Tương quan 0.75, cho thấy người cao hơn có xu hướng tay dài hơn.\n\n- **height_cm vs. age:**\n\n  * Tương quan ~0.53: trẻ em tăng chiều cao theo tuổi.","metadata":{}},{"cell_type":"markdown","source":"**🟪 Các mối tương quan yếu hoặc không đáng kể:**\n-\n\n  * handedness gần như không tương quan với bất kỳ biến nào (tối đa ~0.1).\n  * sex có tương quan vừa phải với height_cm (0.43) và age (0.28).\n  * sequence_counter không liên quan nhiều đến các đặc trưng còn lại (tất cả < |0.2|).","metadata":{}},{"cell_type":"markdown","source":"**Kết luận**\n-\n- Các đặc trưng nhân trắc học (chiều cao, tay, tuổi) có mối liên hệ nội tại chặt chẽ và phù hợp với thực tế sinh học.\n\n- handedness không liên quan nhiều đến các thông số khác, điều này cho thấy handedness là một yếu tố độc lập — rất hữu ích để giữ lại như một biến meta riêng biệt trong mô hình.\n\n- Không phát hiện multicollinearity cực đoan, ngoại trừ mối quan hệ cao giữa shoulder_to_wrist_cm và elbow_to_wrist_cm → nên cẩn trọng khi đưa cả hai vào mô hình tuyến tính.","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder\n\n# Sao chép và mã hóa mục tiêu\ntrain_corr_df = train_df.copy()\ntrain_corr_df[target_variable] = LabelEncoder().fit_transform(train_corr_df[target_variable])\n\n# Trích xuất các đặc điểm số hợp lệ chỉ từ tàu hỏa\ntrain_main_vars = get_numerical_columns(train_corr_df, sensor_prefixes, excluded_columns)\n\n# Bao gồm cả các đặc điểm nhân khẩu học\ntrain_demo_vars = get_numerical_columns(train_dem_df, (), excluded_columns)\n\n# Kết hợp cả hai\ntrain_all_corr = pd.concat([train_corr_df[train_main_vars], train_dem_df[train_demo_vars],\n                            train_corr_df[[target_variable]]], axis=1)\n\n# Chỉ tương quan với mục tiêu\ncorr_target_only = train_all_corr.corr()[[target_variable]].T\n\n# Plot\nplt.figure(figsize=(12, 3))\nsns.heatmap(corr_target_only, cmap='viridis', annot=True, linewidths=0.5, cbar=False, annot_kws={\"size\": 10})\nplt.title(\"Correlation with Target (gesture) — Train Data\", fontsize=13)\nplt.xticks(rotation=45, ha='right')\nplt.yticks(rotation=0)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-07T08:14:12.111627Z","iopub.execute_input":"2025-08-07T08:14:12.111963Z","iopub.status.idle":"2025-08-07T08:14:14.857091Z","shell.execute_reply.started":"2025-08-07T08:14:12.111933Z","shell.execute_reply":"2025-08-07T08:14:14.856109Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Phân tích kết quả**\n-\n- Tất cả hệ số đều rất nhỏ: |r| ≲ 0.14 (cao nhất ~–0.14 ở elbow_to_wrist_cm, ~–0.13 ở shoulder_to_wrist_cm, ~0.08 ở handedness, ~0.07 ở sequence_counter).\n\n- Điều này cho thấy không có quan hệ tuyến tính mạnh giữa các biến nhân khẩu học/hình thái và nhãn cử chỉ.\n\n**Lưu ý:**\n-\nvì gesture là biến phân loại đa lớp, tương quan Pearson với giá trị được gán nhãn số tùy ý dễ gây hiểu nhầm — dấu và độ lớn r phụ thuộc cách đánh số lớp, nên không diễn giải “r dương/âm” như nhân quả.\n\n- Biểu đồ còn có các cột lặp (ví dụ age, sex xuất hiện hai lần): khả năng bạn đã ghép thêm train_dem_df trong khi các cột này đã có sẵn trong train_df. Nếu index không trùng, có thể sinh NaN ngầm.","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}