{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":82611,"databundleVersionId":9553358,"sourceType":"competition"}],"dockerImageVersionId":30787,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# BrisT1D Blood Glucose Prediction","metadata":{}},{"cell_type":"markdown","source":"Predicting blood glucose fluctuations is crucial for managing type 1 diabetes.\n\nDeveloping effective algorithms for this can alleviate some of the challenges faced by individuals with the condition.\r\n\r\nGoal: To forecast blood glucose levels one hour ahead using the previous six hours of participasnt data.","metadata":{}},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport seaborn as sns\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:06.101717Z","iopub.execute_input":"2024-11-29T21:08:06.102346Z","iopub.status.idle":"2024-11-29T21:08:06.108975Z","shell.execute_reply.started":"2024-11-29T21:08:06.102310Z","shell.execute_reply":"2024-11-29T21:08:06.107980Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# plotting\nimport matplotlib.pyplot as plt\nimport plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\n\n# models\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.preprocessing import MinMaxScaler\nfrom sklearn.ensemble import StackingRegressor\nfrom sklearn.linear_model import Ridge\nfrom sklearn.model_selection import cross_val_score\nfrom sklearn.metrics import mean_squared_error\nimport lightgbm as lgb\n!pip install catboost\nfrom catboost import CatBoostRegressor\n\n!pip install --quiet optuna\nimport optuna","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:06.110612Z","iopub.execute_input":"2024-11-29T21:08:06.110917Z","iopub.status.idle":"2024-11-29T21:08:22.083676Z","shell.execute_reply.started":"2024-11-29T21:08:06.110884Z","shell.execute_reply":"2024-11-29T21:08:22.082605Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Functions","metadata":{}},{"cell_type":"code","source":"### Display Graphs for Null Values\n\ndef visualize_null_values(df):\n\n    # Calculate the percentage of null values per feature\n    null_percentage = df.isnull().mean() * 100\n\n    # Create categories based on null percentage ranges\n    bins = [0, 25, 50, 75, 100]\n    labels = ['0-25%', '25-50%', '50-75%', '75-100%']\n    category_counts = pd.cut(null_percentage, bins=bins, labels=labels).value_counts().sort_index()\n\n    # Create a figure with two subplots, one for the bar chart and one for the pie chart\n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))\n\n    # Plot 1: Bar graph showing number of features in each null percentage category\n    bar = ax1.bar(category_counts.index, category_counts.values, color='skyblue')\n\n    # Add numbers on top of each bar\n    for i, value in enumerate(category_counts.values):\n        ax1.text(i, value + 0.1, str(value), ha='center', fontsize=12)\n\n    ax1.set_title('Features by Percentage of Null Values', fontsize=14)\n    ax1.set_xlabel('Percentage of Null Values', fontsize=12)\n    ax1.set_ylabel('Number of Features', fontsize=12)\n\n    # Plot 2: Pie chart showing features with null values and without null values\n    features_with_nulls = (null_percentage > 0).sum()\n    features_without_nulls = (null_percentage == 0).sum()\n\n    labels = ['Features with Null Values', 'Features without Null Values']\n    sizes = [features_with_nulls, features_without_nulls]\n    colors = ['#ff9999','#66b3ff']\n\n    ax2.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)\n    ax2.set_title('Features with and without Null Values', fontsize=14)\n\n    plt.tight_layout()\n    plt.show()\n\n### Display Count Plot of Most Common Activity at Each hour\n\ndef most_common_activity_per_hour(df):\n    fig, axes = plt.subplots(2, 3, figsize=(15, 10))\n    fig.suptitle('Most Common Activity at Each Hour', fontsize=16)\n    colors = ['blue', 'cyan', 'pink', 'purple', 'red', 'orange']\n\n    for i in range(6):\n        row, col_idx = divmod(i, 3)\n\n        top_10_activities = df[f'activity-{i}:00'].value_counts().nlargest(10).index\n        filtered_df = df[df[f'activity-{i}:00'].isin(top_10_activities)]\n\n        sns.countplot(filtered_df, x=f'activity-{i}:00', ax=axes[row, col_idx], color=colors[i])\n        axes[row, col_idx].set_xticklabels(axes[row, col_idx].get_xticklabels(), rotation=45)\n        axes[row, col_idx].set_title(f'Activity at time {i}:00')\n        axes[row, col_idx].set_xlabel('Activity')\n        axes[row, col_idx].set_ylabel('Count')\n\n    plt.tight_layout(rect=[0, 0.03, 1, 0.95])\n    plt.show()\n\n### Display Boxplots of Blood Glucose Levels for Each Hour and Each Activities\n\ndef boxplot_bg_per_hour(h):\n    plt.figure(figsize=(12, 6))\n    sns.boxplot(x=f\"activity-{h}:00\", y=f\"bg-{h}:00\", data=train_df)\n    plt.xticks(rotation=45)\n    plt.title(f\"Impact of Activities on Blood Glucose Levels at hour {h}:00\")\n    plt.xlabel('Activity')\n    plt.ylabel('Blood Glucose (mmol/L)')\n    plt.show()\n\n### Train Model and Predict\n\ndef train_and_predict(model, X_train, y_train, X_test, y_test):\n    # train the model\n    model.fit(X_train, y_train)\n\n    # predict and evaluate\n    train_pred = model.predict(X_train)\n    test_pred = model.predict(X_test)\n\n    train_rmse = np.sqrt(mean_squared_error(y_train, train_pred))\n    test_rmse = np.sqrt(mean_squared_error(y_test, test_pred))\n\n    return train_pred, test_pred, train_rmse, test_rmse","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:22.085664Z","iopub.execute_input":"2024-11-29T21:08:22.086012Z","iopub.status.idle":"2024-11-29T21:08:22.101945Z","shell.execute_reply.started":"2024-11-29T21:08:22.085957Z","shell.execute_reply":"2024-11-29T21:08:22.101159Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Processing Data","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/brist1d/train.csv')\ntest_df = pd.read_csv('/kaggle/input/brist1d/test.csv')\nsample_submission_df = pd.read_csv('/kaggle/input/brist1d/sample_submission.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:22.103741Z","iopub.execute_input":"2024-11-29T21:08:22.103965Z","iopub.status.idle":"2024-11-29T21:08:30.178693Z","shell.execute_reply.started":"2024-11-29T21:08:22.103942Z","shell.execute_reply":"2024-11-29T21:08:30.177750Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:30.179814Z","iopub.execute_input":"2024-11-29T21:08:30.180138Z","iopub.status.idle":"2024-11-29T21:08:30.273237Z","shell.execute_reply.started":"2024-11-29T21:08:30.180109Z","shell.execute_reply":"2024-11-29T21:08:30.272298Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:30.274320Z","iopub.execute_input":"2024-11-29T21:08:30.274614Z","iopub.status.idle":"2024-11-29T21:08:30.298375Z","shell.execute_reply.started":"2024-11-29T21:08:30.274586Z","shell.execute_reply":"2024-11-29T21:08:30.297623Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.describe().transpose()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:30.299253Z","iopub.execute_input":"2024-11-29T21:08:30.299496Z","iopub.status.idle":"2024-11-29T21:08:33.334774Z","shell.execute_reply.started":"2024-11-29T21:08:30.299471Z","shell.execute_reply":"2024-11-29T21:08:33.333872Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.info(show_counts=True, verbose=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:33.335910Z","iopub.execute_input":"2024-11-29T21:08:33.336228Z","iopub.status.idle":"2024-11-29T21:08:33.805427Z","shell.execute_reply.started":"2024-11-29T21:08:33.336200Z","shell.execute_reply":"2024-11-29T21:08:33.804482Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Changing Participant Number To Be an Integer","metadata":{}},{"cell_type":"code","source":"train_df['p_num']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:33.808266Z","iopub.execute_input":"2024-11-29T21:08:33.808541Z","iopub.status.idle":"2024-11-29T21:08:33.814966Z","shell.execute_reply.started":"2024-11-29T21:08:33.808513Z","shell.execute_reply":"2024-11-29T21:08:33.814151Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df['p_num']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:33.816035Z","iopub.execute_input":"2024-11-29T21:08:33.816293Z","iopub.status.idle":"2024-11-29T21:08:33.828940Z","shell.execute_reply.started":"2024-11-29T21:08:33.816268Z","shell.execute_reply":"2024-11-29T21:08:33.828045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Change p_num to be integer numbers\ntrain_df['p_num'] = train_df['p_num'].str.extract('(\\d+)').astype(int)\ntest_df['p_num'] = test_df['p_num'].str.extract('(\\d+)').astype(int)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:33.829933Z","iopub.execute_input":"2024-11-29T21:08:33.830230Z","iopub.status.idle":"2024-11-29T21:08:34.244059Z","shell.execute_reply.started":"2024-11-29T21:08:33.830204Z","shell.execute_reply":"2024-11-29T21:08:34.243027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df['p_num']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:34.245222Z","iopub.execute_input":"2024-11-29T21:08:34.245581Z","iopub.status.idle":"2024-11-29T21:08:34.252541Z","shell.execute_reply.started":"2024-11-29T21:08:34.245532Z","shell.execute_reply":"2024-11-29T21:08:34.251679Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df['p_num']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:34.253691Z","iopub.execute_input":"2024-11-29T21:08:34.253922Z","iopub.status.idle":"2024-11-29T21:08:34.263897Z","shell.execute_reply.started":"2024-11-29T21:08:34.253899Z","shell.execute_reply":"2024-11-29T21:08:34.263204Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Encode Activity Columns","metadata":{}},{"cell_type":"code","source":"activity_cols = [col for col in train_df.columns if col.startswith('activity')]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:34.264909Z","iopub.execute_input":"2024-11-29T21:08:34.265275Z","iopub.status.idle":"2024-11-29T21:08:34.274741Z","shell.execute_reply.started":"2024-11-29T21:08:34.265238Z","shell.execute_reply":"2024-11-29T21:08:34.274052Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"After viewing the activities.txt file, I noticed that there are 2 similar activities: \"Walk\" and \"Walking\". \nWe'll change every occurance of \"Walk\" to \"Walking\".","metadata":{}},{"cell_type":"code","source":"# change every occurance of \"Walk\" to \"Walking\" in the activity columns\nfor col in activity_cols:\n    train_df[col] = train_df[col].apply(lambda x: 'Walking' if x == 'Walk' else x)\n    test_df[col] = test_df[col].apply(lambda x: 'Walking' if x == 'Walk' else x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:34.275783Z","iopub.execute_input":"2024-11-29T21:08:34.276196Z","iopub.status.idle":"2024-11-29T21:08:36.282347Z","shell.execute_reply.started":"2024-11-29T21:08:34.276158Z","shell.execute_reply":"2024-11-29T21:08:36.281625Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"activities = ['No Activity', 'Indoor climbing','Run','Strength training','Swim','Bike','Dancing','Stairclimber','Spinning','Walking','HIIT','Outdoor Bike','Aerobic Workout','Tennis','Workout','Hike','Zumba','Sport','Yoga','Swimming','Weights','Running']\nactivites_encode = {activity: i for i, activity in enumerate(activities)}\nactivites_encode","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:36.283395Z","iopub.execute_input":"2024-11-29T21:08:36.283745Z","iopub.status.idle":"2024-11-29T21:08:36.290326Z","shell.execute_reply.started":"2024-11-29T21:08:36.283708Z","shell.execute_reply":"2024-11-29T21:08:36.289424Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Activities Analysis","metadata":{}},{"cell_type":"code","source":"# Most common activity every 5 minutes\ntrain_df[activity_cols].mode().iloc[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:36.291456Z","iopub.execute_input":"2024-11-29T21:08:36.291791Z","iopub.status.idle":"2024-11-29T21:08:36.658808Z","shell.execute_reply.started":"2024-11-29T21:08:36.291754Z","shell.execute_reply":"2024-11-29T21:08:36.657933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df[activity_cols].mode().iloc[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:36.659729Z","iopub.execute_input":"2024-11-29T21:08:36.660015Z","iopub.status.idle":"2024-11-29T21:08:36.693937Z","shell.execute_reply.started":"2024-11-29T21:08:36.659962Z","shell.execute_reply":"2024-11-29T21:08:36.692962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"most_common_activity_per_hour(train_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:36.695369Z","iopub.execute_input":"2024-11-29T21:08:36.695737Z","iopub.status.idle":"2024-11-29T21:08:38.552950Z","shell.execute_reply.started":"2024-11-29T21:08:36.695698Z","shell.execute_reply":"2024-11-29T21:08:38.552176Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"most_common_activity_per_hour(test_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:38.554262Z","iopub.execute_input":"2024-11-29T21:08:38.554633Z","iopub.status.idle":"2024-11-29T21:08:40.026984Z","shell.execute_reply.started":"2024-11-29T21:08:38.554588Z","shell.execute_reply":"2024-11-29T21:08:40.026227Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Imputation","metadata":{}},{"cell_type":"code","source":"visualize_null_values(train_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:40.028252Z","iopub.execute_input":"2024-11-29T21:08:40.028619Z","iopub.status.idle":"2024-11-29T21:08:40.838105Z","shell.execute_reply.started":"2024-11-29T21:08:40.028579Z","shell.execute_reply":"2024-11-29T21:08:40.837201Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualize_null_values(test_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:40.839205Z","iopub.execute_input":"2024-11-29T21:08:40.839484Z","iopub.status.idle":"2024-11-29T21:08:41.297414Z","shell.execute_reply.started":"2024-11-29T21:08:40.839456Z","shell.execute_reply":"2024-11-29T21:08:41.296560Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We'll asume that missing values in the activity columns mean no activity, so the method of imputation for the activity columns\n\nis filling them with \"No Activity\".","metadata":{}},{"cell_type":"code","source":"# Apply mapping\nfor col in activity_cols:\n    train_df[col] = train_df[col].apply(lambda x: activites_encode[x] if x in activites_encode else activites_encode['No Activity'])\n    test_df[col] = test_df[col].apply(lambda x: activites_encode[x] if x in activites_encode else activites_encode['No Activity'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:41.298699Z","iopub.execute_input":"2024-11-29T21:08:41.299460Z","iopub.status.idle":"2024-11-29T21:08:45.739680Z","shell.execute_reply.started":"2024-11-29T21:08:41.299416Z","shell.execute_reply":"2024-11-29T21:08:45.738962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Activity column example of unique values\ntrain_df[\"activity-5:55\"].unique()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:45.743169Z","iopub.execute_input":"2024-11-29T21:08:45.743417Z","iopub.status.idle":"2024-11-29T21:08:45.749692Z","shell.execute_reply.started":"2024-11-29T21:08:45.743393Z","shell.execute_reply":"2024-11-29T21:08:45.748985Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The chosen method for imputation for the numeric features is forward and backward fill.\n\nThis will ensure data continuity by using the previous valid value in a column or row (forward fill),\n\nwhile backward fill propagates them using the next valid value.","metadata":{}},{"cell_type":"code","source":"# Impute numeric columns - forward and backward fill\nnumeric_columns_train = train_df.select_dtypes(include=['float64', 'int64']).columns\nnumeric_columns_test = test_df.select_dtypes(include=['float64', 'int64']).columns\n\ntrain_df[numeric_columns_train] = train_df[numeric_columns_train].ffill(axis=1)\ntrain_df[numeric_columns_train] = train_df[numeric_columns_train].bfill(axis=1)\ntest_df[numeric_columns_test] = test_df[numeric_columns_test].ffill(axis=1)\ntest_df[numeric_columns_test] = test_df[numeric_columns_test].bfill(axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:45.750750Z","iopub.execute_input":"2024-11-29T21:08:45.751067Z","iopub.status.idle":"2024-11-29T21:08:49.503111Z","shell.execute_reply.started":"2024-11-29T21:08:45.751033Z","shell.execute_reply":"2024-11-29T21:08:49.502383Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\nMissing values after forward and backward fill:\")\nprint(train_df[numeric_columns_train].isnull().sum())\nprint(test_df[numeric_columns_test].isnull().sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:49.504404Z","iopub.execute_input":"2024-11-29T21:08:49.505088Z","iopub.status.idle":"2024-11-29T21:08:50.467316Z","shell.execute_reply.started":"2024-11-29T21:08:49.505046Z","shell.execute_reply":"2024-11-29T21:08:50.466403Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Exploratory Data Analysis","metadata":{}},{"cell_type":"markdown","source":"### Distribution of Blood Glucose Levels per Hour","metadata":{}},{"cell_type":"code","source":"bg_columns = [f'bg-{hour}:00' for hour in range(5, -1, -1)]  # Creates bg-5:00, bg-4:00, ..., bg-0:00\n\nfig, axes = plt.subplots(2, 3, figsize=(15, 10))\nfig.suptitle(f'Distribution of Blood Glucose Levels at Each Hour', fontsize=16)\n\n# Iterate over each column and plot the histogram in the corresponding subplot\nfor i, col in enumerate(bg_columns):\n    row, col_idx = divmod(i, 3)\n    sns.histplot(train_df[col], kde=True, ax=axes[row, col_idx])\n    axes[row, col_idx].set_title(f'Blood Glucose Levels {col}')\n    axes[row, col_idx].set_xlabel('Blood Glucose Levels (mmol/L)')\n    axes[row, col_idx].set_ylabel('Frequency')\n\nplt.tight_layout(rect=[0, 0.03, 1, 0.95])\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:50.468519Z","iopub.execute_input":"2024-11-29T21:08:50.468898Z","iopub.status.idle":"2024-11-29T21:08:57.869202Z","shell.execute_reply.started":"2024-11-29T21:08:50.468857Z","shell.execute_reply":"2024-11-29T21:08:57.868321Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We can see that the distribution of blood glucose levels at each hour is very similar, with very minor differences, meaning they are stable.\n\nThis can also suggest that these columns may not be adding unique information to the model, and a subset of them could capture the overall\n\ntrend without all six hours of lagged values.","metadata":{}},{"cell_type":"code","source":"sns.set_style('darkgrid')\nfig, ax = plt.subplots(nrows=1, ncols=1, figsize=(10, 5))\n\nsns.histplot(data=train_df, x=\"bg-0:00\", kde=True, ax=ax, label=\"bg-0:00\")\nsns.histplot(data=train_df, x=\"bg+1:00\", kde=True, ax=ax, label=\"bg+1:00\")\nax.set_title(\"Blood Glucose Levels\", fontsize=16)\nax.set_ylabel('Blood Glucose Levels (mmol/L)')\nax.set_xlabel(\"Frequency\")\nax.legend(loc=\"upper right\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:57.870172Z","iopub.execute_input":"2024-11-29T21:08:57.870417Z","iopub.status.idle":"2024-11-29T21:08:59.906554Z","shell.execute_reply.started":"2024-11-29T21:08:57.870392Z","shell.execute_reply":"2024-11-29T21:08:59.905694Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Activity Influence on Blood Glucose","metadata":{}},{"cell_type":"code","source":"boxplot_bg_per_hour(0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:08:59.907582Z","iopub.execute_input":"2024-11-29T21:08:59.907858Z","iopub.status.idle":"2024-11-29T21:09:00.364319Z","shell.execute_reply.started":"2024-11-29T21:08:59.907829Z","shell.execute_reply":"2024-11-29T21:09:00.363372Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"boxplot_bg_per_hour(1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:00.365348Z","iopub.execute_input":"2024-11-29T21:09:00.365586Z","iopub.status.idle":"2024-11-29T21:09:00.806594Z","shell.execute_reply.started":"2024-11-29T21:09:00.365561Z","shell.execute_reply":"2024-11-29T21:09:00.805700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"boxplot_bg_per_hour(2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:00.807711Z","iopub.execute_input":"2024-11-29T21:09:00.807985Z","iopub.status.idle":"2024-11-29T21:09:01.434648Z","shell.execute_reply.started":"2024-11-29T21:09:00.807957Z","shell.execute_reply":"2024-11-29T21:09:01.433954Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"boxplot_bg_per_hour(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:01.435590Z","iopub.execute_input":"2024-11-29T21:09:01.435831Z","iopub.status.idle":"2024-11-29T21:09:01.870490Z","shell.execute_reply.started":"2024-11-29T21:09:01.435806Z","shell.execute_reply":"2024-11-29T21:09:01.869602Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"boxplot_bg_per_hour(4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:01.871694Z","iopub.execute_input":"2024-11-29T21:09:01.872385Z","iopub.status.idle":"2024-11-29T21:09:02.387576Z","shell.execute_reply.started":"2024-11-29T21:09:01.872353Z","shell.execute_reply":"2024-11-29T21:09:02.386575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"boxplot_bg_per_hour(5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:02.388628Z","iopub.execute_input":"2024-11-29T21:09:02.388882Z","iopub.status.idle":"2024-11-29T21:09:02.836863Z","shell.execute_reply.started":"2024-11-29T21:09:02.388857Z","shell.execute_reply":"2024-11-29T21:09:02.836060Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Conclusion","metadata":{}},{"cell_type":"markdown","source":"From the distribution of blood glucose levles per hour plots we can see that the distribution are quite similar, so we can choose not to use the data with 5 minute gaps, but 1 hour gaps.\n\nMoreover, we can see many high levels of blood glucose in activity 0, which is 'No Activity'. This activity was used to fill null values, and we can see that it makes sence:\n\nblood glucose levels can rise due to no exercise. In addision, participants might eat during that time which can also increase blood glucose levels.","metadata":{}},{"cell_type":"markdown","source":"## Feature Selection","metadata":{}},{"cell_type":"markdown","source":"For the time based data (bg, insulin, carbs, hr, steps, cals and activity), we'll choose a new gap of 30 minutes, to reduce redundancy.","metadata":{}},{"cell_type":"code","source":"# changing special characters because some models can't handle them (such as LightGBM)\ntrain_df.columns = train_df.columns.str.replace(\":\", \"-\")\ntest_df.columns = test_df.columns.str.replace(\":\", \"-\")\n\ntrain_df.columns = train_df.columns.str.replace(\"_\", \"-\")\ntest_df.columns = test_df.columns.str.replace(\"_\", \"-\")\n\nsample_submission_df.columns = sample_submission_df.columns.str.replace(\":\", \"-\")\nsample_submission_df.columns = sample_submission_df.columns.str.replace(\"_\", \"-\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:02.838101Z","iopub.execute_input":"2024-11-29T21:09:02.838487Z","iopub.status.idle":"2024-11-29T21:09:02.846486Z","shell.execute_reply.started":"2024-11-29T21:09:02.838446Z","shell.execute_reply":"2024-11-29T21:09:02.845478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# take 30 minutes gap for each column\nbg_columns = [f'bg-{hour}-{minute:02d}' for hour in range(5, -1, -1) for minute in range(0, 60, 30)]\nhr_columns = [f'hr-{hour}-{minute:02d}' for hour in range(5, -1, -1) for minute in range(0, 60, 30)]\ninsulin_columns = [f'insulin-{hour}-{minute:02d}' for hour in range(5, -1, -1) for minute in range(0, 60, 30)]\ncarbs_columns = [f'carbs-{hour}-{minute:02d}' for hour in range(5, -1, -1) for minute in range(0, 60, 30)]\nsteps_columns = [f'steps-{hour}-{minute:02d}' for hour in range(5, -1, -1) for minute in range(0, 60, 30)]\ncals_columns = [f'cals-{hour}-{minute:02d}' for hour in range(5, -1, -1) for minute in range(0, 60, 30)]\nactivity_columns = [f'activity-{hour}-{minute:02d}' for hour in range(5, -1, -1) for minute in range(0, 60, 30)]\n\ntrain_df = train_df[[\"id\", \"p-num\", \"time\"] + bg_columns + insulin_columns + carbs_columns + hr_columns + steps_columns + cals_columns + activity_columns + [\"bg+1-00\"]]\ntest_df = test_df[[\"id\", \"p-num\", \"time\"] + bg_columns + insulin_columns + carbs_columns + hr_columns + steps_columns + cals_columns + activity_columns]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:02.847452Z","iopub.execute_input":"2024-11-29T21:09:02.847747Z","iopub.status.idle":"2024-11-29T21:09:02.923619Z","shell.execute_reply.started":"2024-11-29T21:09:02.847720Z","shell.execute_reply":"2024-11-29T21:09:02.922741Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Feature Engineering","metadata":{}},{"cell_type":"markdown","source":"### Time column","metadata":{}},{"cell_type":"markdown","source":"Extracting hour, minute, and second as separate features. Also, since time is periodic (e.g., 23:59 is close to 00:00), directly using hour as a numerical feature might misrepresent relationships, so we'll represent it as a cyclic feature using sine and cosine transformations.\r\n\r\nThe new features are:\r\n\r\n`hour_sin`, `hour_cos`, `minute_sin`, `minute cos`, `second_sin`, `second_cos`","metadata":{}},{"cell_type":"code","source":"# extract hour, minute and second\ntrain_df['hour'] = pd.to_datetime(train_df['time'], format='%H:%M:%S').dt.hour\ntrain_df['minute'] = pd.to_datetime(train_df['time'], format='%H:%M:%S').dt.minute\ntrain_df['second'] = pd.to_datetime(train_df['time'], format='%H:%M:%S').dt.second\n\ntest_df['hour'] = pd.to_datetime(test_df['time'], format='%H:%M:%S').dt.hour\ntest_df['minute'] = pd.to_datetime(test_df['time'], format='%H:%M:%S').dt.minute\ntest_df['second'] = pd.to_datetime(test_df['time'], format='%H:%M:%S').dt.second\n\n# add cyclic features\ntrain_df['hour-sin'] = np.sin(2 * np.pi * train_df['hour'] / 24)\ntrain_df['hour-cos'] = np.cos(2 * np.pi * train_df['hour'] / 24)\ntrain_df['minute-sin'] = np.sin(2 * np.pi * train_df['minute'] / 60)\ntrain_df['minute-cos'] = np.cos(2 * np.pi * train_df['minute'] / 60)\ntrain_df['second-sin'] = np.sin(2 * np.pi * train_df['second'] / 60)\ntrain_df['second-cos'] = np.cos(2 * np.pi * train_df['second'] / 60)\n\ntest_df['hour-sin'] = np.sin(2 * np.pi * test_df['hour'] / 24)\ntest_df['hour-cos'] = np.cos(2 * np.pi * test_df['hour'] / 24)\ntest_df['minute-sin'] = np.sin(2 * np.pi * test_df['minute'] / 60)\ntest_df['minute-cos'] = np.cos(2 * np.pi * test_df['minute'] / 60)\ntest_df['second-sin'] = np.sin(2 * np.pi * test_df['second'] / 60)\ntest_df['second-cos'] = np.cos(2 * np.pi * test_df['second'] / 60)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:02.924637Z","iopub.execute_input":"2024-11-29T21:09:02.924901Z","iopub.status.idle":"2024-11-29T21:09:03.047407Z","shell.execute_reply.started":"2024-11-29T21:09:02.924875Z","shell.execute_reply":"2024-11-29T21:09:03.046454Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# delete hour column\ntrain_df.drop(columns=['hour', 'minute', 'second', 'time'], inplace=True)\ntest_df.drop(columns=['hour', 'minute', 'second', 'time'], inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:03.048451Z","iopub.execute_input":"2024-11-29T21:09:03.048722Z","iopub.status.idle":"2024-11-29T21:09:03.196755Z","shell.execute_reply.started":"2024-11-29T21:09:03.048696Z","shell.execute_reply":"2024-11-29T21:09:03.195850Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model and Prediction","metadata":{}},{"cell_type":"code","source":"time_columns = [\"hour-sin\", \"hour-cos\", \"minute-sin\", \"minute-cos\", \"second-sin\", \"second-cos\"]\nfeature_columns = bg_columns + insulin_columns + carbs_columns + hr_columns + steps_columns + cals_columns + activity_columns + time_columns\nparticipant_column = ['p-num']\ntarget_column = ['bg+1-00']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:03.197783Z","iopub.execute_input":"2024-11-29T21:09:03.198055Z","iopub.status.idle":"2024-11-29T21:09:03.202508Z","shell.execute_reply.started":"2024-11-29T21:09:03.198020Z","shell.execute_reply":"2024-11-29T21:09:03.201648Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train, X_test, y_train, y_test = train_df[feature_columns], test_df[feature_columns], train_df[target_column], sample_submission_df[target_column]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:03.203638Z","iopub.execute_input":"2024-11-29T21:09:03.204227Z","iopub.status.idle":"2024-11-29T21:09:03.266143Z","shell.execute_reply.started":"2024-11-29T21:09:03.204187Z","shell.execute_reply":"2024-11-29T21:09:03.265543Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Cat Boost Model","metadata":{}},{"cell_type":"code","source":"catboost_model = CatBoostRegressor()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:03.267103Z","iopub.execute_input":"2024-11-29T21:09:03.267426Z","iopub.status.idle":"2024-11-29T21:09:03.272759Z","shell.execute_reply.started":"2024-11-29T21:09:03.267388Z","shell.execute_reply":"2024-11-29T21:09:03.271933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_pred, test_pred, train_rmse, test_rmse = train_and_predict(catboost_model, X_train, y_train, X_test, y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:03.273917Z","iopub.execute_input":"2024-11-29T21:09:03.274289Z","iopub.status.idle":"2024-11-29T21:09:38.559212Z","shell.execute_reply.started":"2024-11-29T21:09:03.274252Z","shell.execute_reply":"2024-11-29T21:09:38.558293Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Train RMSE (CatBoost): {train_rmse:.4f}\")\nprint(f\"Test RMSE (CatBoost): {test_rmse:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:09:38.560399Z","iopub.execute_input":"2024-11-29T21:09:38.560687Z","iopub.status.idle":"2024-11-29T21:09:38.565417Z","shell.execute_reply.started":"2024-11-29T21:09:38.560658Z","shell.execute_reply":"2024-11-29T21:09:38.564554Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Hyperparameter Optimization","metadata":{}},{"cell_type":"code","source":"def objective(trial):\n    # Suggest hyperparameters for CatBoost\n    params = {\n        \"colsample_bylevel\": trial.suggest_float(\"colsample_bylevel\", 0.01, 0.1),\n        \"depth\": trial.suggest_int(\"depth\", 1, 12),\n        \"boosting_type\": trial.suggest_categorical(\"boosting_type\", [\"Ordered\", \"Plain\"]),\n        \"bootstrap_type\": trial.suggest_categorical(\n            \"bootstrap_type\", [\"Bayesian\", \"Bernoulli\", \"MVS\"]\n        )\n    }\n\n    if params[\"bootstrap_type\"] == \"Bayesian\":\n        params[\"bagging_temperature\"] = trial.suggest_float(\"bagging_temperature\", 0, 10)\n    elif params[\"bootstrap_type\"] == \"Bernoulli\":\n        params[\"subsample\"] = trial.suggest_float(\"subsample\", 0.1, 1)\n\n    # Create and train CatBoost model\n    model = CatBoostRegressor(**params)\n    model.fit(X_train, y_train, eval_set=(X_test, y_test), early_stopping_rounds=50, verbose=0)\n\n    # Predict on validation set and calculate RMSE\n    y_pred = model.predict(X_test)\n    rmse = np.sqrt(mean_squared_error(y_test, y_pred))\n    return rmse","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:18:26.284797Z","iopub.execute_input":"2024-11-29T21:18:26.285180Z","iopub.status.idle":"2024-11-29T21:18:26.291612Z","shell.execute_reply.started":"2024-11-29T21:18:26.285146Z","shell.execute_reply":"2024-11-29T21:18:26.290650Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create an Optuna study\nstudy = optuna.create_study(direction=\"minimize\")  # Minimize RMSE\nstudy.optimize(objective, n_trials=50)  # Run optimization for 50 trials\n\n# Best hyperparameters and score\nprint(\"Best RMSE:\", study.best_value)\nprint(\"Best hyperparameters:\", study.best_params)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:18:28.822196Z","iopub.execute_input":"2024-11-29T21:18:28.822556Z","iopub.status.idle":"2024-11-29T21:25:11.312578Z","shell.execute_reply.started":"2024-11-29T21:18:28.822522Z","shell.execute_reply":"2024-11-29T21:25:11.311737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"opt_catboost_model = CatBoostRegressor(**study.best_params)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:25:40.536211Z","iopub.execute_input":"2024-11-29T21:25:40.536558Z","iopub.status.idle":"2024-11-29T21:25:40.541174Z","shell.execute_reply.started":"2024-11-29T21:25:40.536526Z","shell.execute_reply":"2024-11-29T21:25:40.540191Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_pred, test_pred, train_rmse, test_rmse = train_and_predict(opt_catboost_model, X_train, y_train, X_test, y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:25:53.058666Z","iopub.execute_input":"2024-11-29T21:25:53.059488Z","iopub.status.idle":"2024-11-29T21:26:04.910164Z","shell.execute_reply.started":"2024-11-29T21:25:53.059452Z","shell.execute_reply":"2024-11-29T21:26:04.909184Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Train RMSE (CatBoost): {train_rmse:.4f}\")\nprint(f\"Test RMSE (CatBoost): {test_rmse:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:26:09.567636Z","iopub.execute_input":"2024-11-29T21:26:09.568197Z","iopub.status.idle":"2024-11-29T21:26:09.572683Z","shell.execute_reply.started":"2024-11-29T21:26:09.568163Z","shell.execute_reply":"2024-11-29T21:26:09.571781Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_submission_df.rename(columns={'bg+1-00': 'bg+1:00'}, inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:46:21.516774Z","iopub.execute_input":"2024-11-29T21:46:21.517461Z","iopub.status.idle":"2024-11-29T21:46:21.521956Z","shell.execute_reply.started":"2024-11-29T21:46:21.517424Z","shell.execute_reply":"2024-11-29T21:46:21.521119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_submission_df['bg+1:00']=test_pred\nsample_submission_df.to_csv('submission.csv', index=False)\nsample_submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-29T21:46:42.110325Z","iopub.execute_input":"2024-11-29T21:46:42.110683Z","iopub.status.idle":"2024-11-29T21:46:42.134782Z","shell.execute_reply.started":"2024-11-29T21:46:42.110649Z","shell.execute_reply":"2024-11-29T21:46:42.133831Z"}},"outputs":[],"execution_count":null}]}