{"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":"none","dataSources":[{"sourceId":82611,"databundleVersionId":9553358,"sourceType":"competition"}],"dockerImageVersionId":30786,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## <p style=\"padding:15px; background-color:#b48dda; font-family:arial; font-weight:bold; color:white; font-size:100%; letter-spacing: 2px; text-align:left; border-radius: 10px 10px; margin-bottom: -30px;\">Setup</p>","metadata":{}},{"cell_type":"code","source":"import os\n\nif not os.path.isdir('src'):\n    !git init .\n    !git remote add origin https://github.com/Manuel-Materazzo/Blood-Glucose-Prediction.git\n\nprint('Pulling...')\n!git pull origin master -q\n\nprint('Installing dependencies...')\n!pip install -r /kaggle/working/src/requirements.txt -q\n\nimport sklearn\nprint('The scikit-learn version is {}.'.format(sklearn.__version__))\n\nimport re\nimport torch\nimport pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom pandas import DataFrame\nfrom sklearn.impute import SimpleImputer, KNNImputer\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.preprocessing import OneHotEncoder, OrdinalEncoder\nfrom sklearn.base import BaseEstimator, TransformerMixin\nfrom pandas.api.types import is_datetime64_any_dtype as is_datetime\n\nfrom src.pipelines.dt_pipeline import DTPipeline\nfrom src.pipelines.empty_dt_pipeline import EmptyDTPipeline\nfrom src.preprocessors.empty_data_preprocessor import EmptyDataPreprocessor\n\nfrom src.enums.accuracy_metric import AccuracyMetric\nfrom src.enums.optimization_direction import OptimizationDirection\n\nfrom src.models.catboost_regressor import CatBoostRegressorWrapper\nfrom src.models.hgb_regressor import HGBRegressorWrapper\nfrom src.models.lgbm_regressor import LGBMRegressorWrapper\nfrom src.models.xgb_regressor import XGBRegressorWrapper\nfrom src.models.tabnet_regressor import TabNetRegressorWrapper\n\nfrom src.models.catboost_classifier import CatBoostClassifierWrapper\nfrom src.models.hgb_classifier import HGBClassifierWrapper\nfrom src.models.lgbm_classifier import LGBMClassifierWrapper\nfrom src.models.xgb_classifier import XGBClassifierWrapper\nfrom src.models.tabnet_classifier import TabNetClassifierWrapper\n\nfrom src.trainers.simple_trainer import SimpleTrainer\nfrom src.trainers.accurate_cross_trainer import AccurateCrossTrainer\nfrom src.trainers.cached_accurate_cross_trainer import CachedAccurateCrossTrainer\n\nfrom src.hyperparameter_optimizers.custom_grid_optimizer import CustomGridOptimizer\nfrom src.hyperparameter_optimizers.default_grid_optimizer import DefaultGridOptimizer\nfrom src.hyperparameter_optimizers.hyperopt_bayesian_optimizer import HyperoptBayesianOptimizer\nfrom src.hyperparameter_optimizers.optuna_optimizer import OptunaOptimizer\n\nfrom src.ensembles.weighted_ensemble import WeightedEnsemble\nfrom src.ensembles.stacked_ensemble import StackedEnsemble\n\npd.set_option('display.max_columns', None)\n\ndef load_data():\n    # Load the data\n    file_path = '/kaggle/input/brist1d/train.csv'\n    data = pd.read_csv(file_path, index_col='id')\n    \n    # Remove rows with missing target, separate target from predictors\n    data.dropna(axis=0, subset=['bg+1:00'], inplace=True)\n    y = data['bg+1:00']\n    X = data.drop(['bg+1:00'], axis=1)\n\n    # rename target column\n    data['target'] = data['bg+1:00']\n    data = data.drop(['bg+1:00'], axis=1)\n\n    # standardize column names\n    data = data.rename(columns = lambda x:re.sub('[^A-Za-z0-9_]+', '_', x))\n    X = X.rename(columns = lambda x:re.sub('[^A-Za-z0-9_]+', '_', x))\n    return X, y, data\n\nprint('Loading data...')\nX, y, raw_data = load_data()\nprint('Done ✅')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:13:27.556463Z","iopub.execute_input":"2025-01-05T17:13:27.556825Z","iopub.status.idle":"2025-01-05T17:15:17.598052Z","shell.execute_reply.started":"2025-01-05T17:13:27.556792Z","shell.execute_reply":"2025-01-05T17:15:17.596768Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## <p style=\"padding:15px; background-color:#b48dda; font-family:arial; font-weight:bold; color:white; font-size:100%; letter-spacing: 2px; text-align:left; border-radius: 10px 10px; margin-bottom: -30px; margin-top: 50px;\">Data Exploration</p>","metadata":{}},{"cell_type":"code","source":"raw_data.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:17.601322Z","iopub.execute_input":"2025-01-05T17:15:17.601851Z","iopub.status.idle":"2025-01-05T17:15:17.920464Z","shell.execute_reply.started":"2025-01-05T17:15:17.601799Z","shell.execute_reply":"2025-01-05T17:15:17.918983Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n    \n#### Initial observations\n* The dataset is composed of some metrics collected by a smartwarch.\n* Aside from identification fields, columns are named after a metric-timedelta convention, where time delta is a hour:minute value of time.\n* Data is collected every 10 to 20 minutes.\n* For each metric, there is a time delta column every 5 minutes for 6 hours.\n* It seems that only 1 out of 3 delta columns is valorized. This alignes with the supposed 15 minutes data collection policy. **This means that probably patients collect data asynchronously and we'll have to do backward and forward filling**.","metadata":{}},{"cell_type":"code","source":"# individual mesurement types\ncolumn_types = []\nfor column in raw_data.columns:\n    column_type = column.split(\"_\")[0]\n    column_types.append(column_type)\n\ndeduplicated_column_types = list(dict.fromkeys(column_types))\nprint(deduplicated_column_types)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:17.922008Z","iopub.execute_input":"2025-01-05T17:15:17.922364Z","iopub.status.idle":"2025-01-05T17:15:17.929138Z","shell.execute_reply.started":"2025-01-05T17:15:17.922331Z","shell.execute_reply":"2025-01-05T17:15:17.928047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# all patients with data count\nraw_data.p_num.value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:17.931212Z","iopub.execute_input":"2025-01-05T17:15:17.931696Z","iopub.status.idle":"2025-01-05T17:15:17.974855Z","shell.execute_reply.started":"2025-01-05T17:15:17.931644Z","shell.execute_reply":"2025-01-05T17:15:17.973503Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n\nThe dataset enumerates patients from p1 to p12, p-07 to p-09 are missing. We have 9 of the 24 patients data in the training set.\n\nSome patients have less data, did they wear smartwatches for a different amount of time?","metadata":{}},{"cell_type":"code","source":"# survey times for each patient\nraw_data.groupby(['p_num']).time.nunique()\n\nraw_data['hour'] = pd.to_datetime(raw_data.time).dt.hour\nplt.figure(figsize=(8, 8))\nraw_data['hour'].value_counts().sort_index().plot(kind='pie', autopct='%1.1f%%')\nplt.title('Distribution of Hour - Training (Pie Chart)')\nplt.ylabel('')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:17.976480Z","iopub.execute_input":"2025-01-05T17:15:17.976951Z","iopub.status.idle":"2025-01-05T17:15:18.487970Z","shell.execute_reply.started":"2025-01-05T17:15:17.976902Z","shell.execute_reply":"2025-01-05T17:15:18.486741Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n\nPatients have the same amount of surveys, one every 5 minutes for the whole 24 hours. The difference in data amounts to the wearing time in days.","metadata":{}},{"cell_type":"code","source":"# Difference from the most and the least measured hours between patients (hour survey delta)\n\n# cast the time column to datetime and extract the hour on a \"hour\" column\nraw_data['hour'] = pd.to_datetime(raw_data.time).dt.hour\n\n# group by patient and hour, to get all time counts\ntime_counts = raw_data.groupby(['p_num', 'hour']).p_num.count()\n\n# get maximum and minimum number of surveys (hour) of each patient\npatient_minmax = time_counts.groupby(['p_num']).aggregate(['max', 'min'])\n\n# calculate diff\npatient_minmax['diff'] = patient_minmax['max'] - patient_minmax['min']\n\n# sort\npatient_minmax = patient_minmax.sort_values(by='diff', ascending=False)\n\nsns.barplot(y=patient_minmax['diff'], x=patient_minmax.index)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:18.489358Z","iopub.execute_input":"2025-01-05T17:15:18.489761Z","iopub.status.idle":"2025-01-05T17:15:18.870886Z","shell.execute_reply.started":"2025-01-05T17:15:18.489727Z","shell.execute_reply":"2025-01-05T17:15:18.869632Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n\nPatients with more data have increased missing surveys.\n\nWhy patient 5 has high delta with low data numerosity?","metadata":{}},{"cell_type":"code","source":"# Hour distribution of patient 5 surveys (has high hour survey delta, but low datapoint numerosity)\n\n# filter by patient 1\np05_X = raw_data.loc[raw_data.p_num == 'p05']\n# get all time counts\ntime_counts = p05_X.time.value_counts()\n\ntime_counts_df = pd.DataFrame(time_counts)\n\n# cast the time column to datetime and extract the hour on a \"hour\" column\ntime_counts_df['hour'] = pd.to_datetime(time_counts_df.index).hour\n\n# group by hour\ntime_counts_df_grouped = time_counts_df.groupby('hour').sum()\n\n# sort\ntime_counts_df_grouped = time_counts_df_grouped.sort_values(by='count', ascending=False)\n\nsns.barplot(y=time_counts_df_grouped['count'], x=time_counts_df_grouped.index )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:18.875622Z","iopub.execute_input":"2025-01-05T17:15:18.876050Z","iopub.status.idle":"2025-01-05T17:15:19.378706Z","shell.execute_reply.started":"2025-01-05T17:15:18.876013Z","shell.execute_reply":"2025-01-05T17:15:19.377431Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n\nThere is, more or less, the same amount of data for each hour. Patient 5 probably wore the smartwatch wrong on some nights.","metadata":{}},{"cell_type":"code","source":"# check if patients have consistent missing columns\npatients = ['p01', 'p02', 'p03', 'p04', 'p05', 'p06', 'p10', 'p11', 'p12']\nfor patient_id in patients:\n    patient_X = raw_data.loc[raw_data.p_num == patient_id]\n    cols_with_missing = len([col for col in raw_data.columns if raw_data[col].isnull().any])\n    print(\"{} columns with missing data: {}\".format(patient_id, cols_with_missing))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:19.380556Z","iopub.execute_input":"2025-01-05T17:15:19.381057Z","iopub.status.idle":"2025-01-05T17:15:23.945657Z","shell.execute_reply.started":"2025-01-05T17:15:19.381021Z","shell.execute_reply":"2025-01-05T17:15:23.944212Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n\nProvided that we have data points every 5 minutes, and on each column there are missing data, we can conclude that data collection times are not consistent on the same patient.","metadata":{}},{"cell_type":"code","source":"# count duplicated rows\nprint(f\"There are {sum(raw_data.duplicated())} duplicated rows in the train data frame.\")\n\n# count rows\nrow_count = raw_data.shape[0]\n\n# get a serie with missing value columns and count\nfeatures_with_missing_values = [col for col in raw_data.columns if raw_data[col].isnull().any]\nmissing_values_counts = raw_data[features_with_missing_values].isnull().sum()\n\n# group by metric\nmissing_counts_grouped_by_metrics = {}\nfor key, value in missing_values_counts.items():\n     metric = key.split(\"_\")[0]\n     missing_counts_grouped_by_metrics[metric] = (missing_counts_grouped_by_metrics.get(metric) or 0) + value\n\n# convert to key-value list\nkeys = list(missing_counts_grouped_by_metrics.keys())\nvals = list(missing_counts_grouped_by_metrics.values())\n\n# convert values in percentage\nvals = [(i * 100 / (row_count * 72)) for i in vals]\n\n# set the plot max to 100, for accurate percentage representations\nplt.ylim(0, 100)\nsns.barplot(x=keys, y=vals)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:23.947199Z","iopub.execute_input":"2025-01-05T17:15:23.947675Z","iopub.status.idle":"2025-01-05T17:15:28.155682Z","shell.execute_reply.started":"2025-01-05T17:15:23.947628Z","shell.execute_reply":"2025-01-05T17:15:28.154468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# check for time patterns on carbs surveys\ncarbs_X = raw_data.loc[raw_data['carbs_0_00'].notnull()]\ncarbs_X['hour'] = pd.to_datetime(carbs_X['time'].values).hour\ncarbs_X = carbs_X[['p_num','carbs_0_00', 'hour']]\n\ngrouped = carbs_X.groupby(['p_num', 'hour']).size().reset_index(name='count')\n\nplt.figure(figsize=(12, 6))\nsns.barplot(x='hour', y='count', hue='p_num', data=grouped, ci=None)\n\nplt.xlabel('Hour')\nplt.ylabel('Count')\nplt.title('Count of Carbs Measurements per Hour and p_num')\nplt.legend(title='p_num')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:28.157426Z","iopub.execute_input":"2025-01-05T17:15:28.157783Z","iopub.status.idle":"2025-01-05T17:15:29.040084Z","shell.execute_reply.started":"2025-01-05T17:15:28.157750Z","shell.execute_reply":"2025-01-05T17:15:29.038878Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n\ncarbs and activity have a great deal of empty data, but the distribution of the data we have is on the whole day.","metadata":{}},{"cell_type":"code","source":"# Aggregate data by patient and plot the distribution\ndf_patient_avg = raw_data.groupby('p_num')['target'].mean().reset_index()\n\nplt.figure(figsize=(12, 5))\nsns.violinplot(data=df_patient_avg, x='p_num', y='target')\nplt.title('Average bg+1:00 by Patient')\nplt.grid(True)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:29.041860Z","iopub.execute_input":"2025-01-05T17:15:29.042341Z","iopub.status.idle":"2025-01-05T17:15:29.597994Z","shell.execute_reply.started":"2025-01-05T17:15:29.042295Z","shell.execute_reply":"2025-01-05T17:15:29.596837Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## <p style=\"padding:15px; background-color:#b48dda; font-family:arial; font-weight:bold; color:white; font-size:100%; letter-spacing: 2px; text-align:left; border-radius: 10px 10px; margin-bottom: -60px\">Feature Engineering</p>\n\n<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; background-color: #f6f5f5; text-align:left\"> \n\nFeature importance analysis has shown that the prediction is influences only by data from the past 45 mins / 1 hour.<br>\nWe can seize this opportunity to increase our dataset size by 6 times by changing the problem statement.","metadata":{}},{"cell_type":"code","source":"time_series = {\n    '0': ['_0_55', '_0_50', '_0_45', '_0_40', '_0_35', '_0_30', '_0_25', '_0_20', '_0_15', '_0_10', '_0_05', '_0_00'],\n    '1': ['_1_55', '_1_50', '_1_45', '_1_40', '_1_35', '_1_30', '_1_25', '_1_20', '_1_15', '_1_10', '_1_05', '_1_00'],\n    '2': ['_2_55', '_2_50', '_2_45', '_2_40', '_2_35', '_2_30', '_2_25', '_2_20', '_2_15', '_2_10', '_2_05', '_2_00'],\n    '3': ['_3_55', '_3_50', '_3_45', '_3_40', '_3_35', '_3_30', '_3_25', '_3_20', '_3_15', '_3_10', '_3_05', '_3_00'],\n    '4': ['_4_55', '_4_50', '_4_45', '_4_40', '_4_35', '_4_30', '_4_25', '_4_20', '_4_15', '_4_10', '_4_05', '_4_00'],\n    '5': ['_5_55', '_5_50', '_5_45', '_5_40', '_5_35', '_5_30', '_5_25', '_5_20', '_5_15', '_5_10', '_5_05', '_5_00']\n}\n\ndef extract_sub_row(row, time_serie_suffixes, target_column, time_delta):\n    \n    # transform the row into a dictionary\n    row_dict = row.to_dict()\n    \n    # create subrow object\n    sub_row = {}\n    \n    # add patient number\n    sub_row['p_num'] = row['p_num']\n    \n    # set the real time, by subtracting the delta from the time column\n    sub_row['time'] = pd.to_datetime(row['time'], format='%H:%M:%S') - pd.Timedelta(hours=time_delta)\n\n    # set the new target\n    sub_row['target'] = row[target_column]\n    \n    # add columns containing the provided time series suffix list\n    for key, value in row_dict.items():\n        \n        # check if the column contains one of the time series suffixes\n        for index, suffix in enumerate(time_serie_suffixes):\n            \n            if suffix in key:\n                # get metric name from key(bg_X_XX)\n                metric_name = key.split('_')[0]\n                \n                # generate new column name\n                new_column_name = f'{metric_name}_{str(index)}'\n                \n                # add column to the row\n                sub_row[new_column_name] = value\n                \n    return sub_row\n            \ndef split_data(df):\n    \"\"\"\n    Split a row with 6 hours of data into 6 rows with 1 hour of data\n    :param df:\n    :return:\n    \"\"\"    \n    rows = []\n    \n    # for each row in the dataset\n    for idx, row in df.iterrows():\n        # extract 6 one hour rows\n        sub_row_0 = extract_sub_row(row, time_series['0'], 'target', 0)\n        rows.append(sub_row_0)\n        for i in range(1,6):\n            target_column = f'bg_{str(i - 1)}_00'\n            sub_row = extract_sub_row(row, time_series[str(i)], target_column, i)\n            rows.append(sub_row)\n            \n    return pd.DataFrame(rows).sort_values(by=['p_num', 'time']).reset_index(drop=True)\n\ndef drop_prediction_columns(df):\n    \"\"\"\n    Drops all the columns with data older than one hour\n    :param df:\n    :return:\n    \"\"\"\n    cols_to_drop = []\n    for column in df.columns:\n        # rename first hour data columns\n        for idx, suffix in enumerate(time_series['0']):\n            if suffix in column:\n                # get metric name from key(bg_X_XX)\n                metric_name = column.split('_')[0]\n                # generate new column name\n                new_column_name = f'{metric_name}_{str(idx)}'\n                # add new column\n                df[new_column_name] = df[column]\n                # remove old column\n                cols_to_drop.append(column)\n                continue\n            \n        for i in range(1,6):\n            if any(time_serie_suffix in column for time_serie_suffix in time_series[str(i)]):\n                cols_to_drop.append(column)\n    df.drop(cols_to_drop,axis=1,inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:29.599698Z","iopub.execute_input":"2025-01-05T17:15:29.600061Z","iopub.status.idle":"2025-01-05T17:15:29.616509Z","shell.execute_reply.started":"2025-01-05T17:15:29.600028Z","shell.execute_reply":"2025-01-05T17:15:29.615291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mesas_cols=['bg', 'insulin','steps','hr','cals']\ndivisible_cols=['bg','hr']\n\ndef create_features(df):\n    # Generate mean, std and zscore of latest value\n    for col in mesas_cols:\n        # calculate mean\n        df[f'mean_patient_{col}_11'] = df.groupby(['p_num'])[f\"{col}_11\"].transform('mean')\n        # calculate standard deviation\n        df[f'std_patient_{col}_11'] = df.groupby(['p_num'])[f\"{col}_11\"].transform('std')\n        # z-score normalization\n        df[f'norm_patient_{col}_11'] = (df[f'{col}_11'] - df[f'mean_patient_{col}_11']) / df[f'std_patient_{col}_11']\n        \n\n    # Pairwise Feature Interactions on 3 latest values\n    for i in range(len(mesas_cols)):\n        for j in range(i + 1, len(mesas_cols)):\n            for k in [1,2,3]:\n                time_index = 12 - k\n                # bg_11_plus_hr_11 = bg_11 + hr_11\n                df[f\"{mesas_cols[i]}_{time_index}_plus_{mesas_cols[j]}_{time_index}\"] = df[f\"{mesas_cols[i]}_{time_index}\"] + df[f\"{mesas_cols[j]}_{time_index}\"]\n                # bg_11_minus_hr_11 = bg_11 - hr_11\n                df[f\"{mesas_cols[i]}_{time_index}_minus_{mesas_cols[j]}_{time_index}\"] = df[f\"{mesas_cols[i]}_{time_index}\"] - df[f\"{mesas_cols[j]}_{time_index}\"]\n                # bg_11_multiplied_hr_11 = bg_11 * hr_11\n                df[f\"{mesas_cols[i]}_{time_index}_multiplied_{mesas_cols[j]}_{time_index}\"] = df[f\"{mesas_cols[i]}_{time_index}\"] * df[f\"{mesas_cols[j]}_{time_index}\"]\n                # bg_11_divided_hr_11 = bg_11 / hr_11 + 1e-15\n                df[f\"{mesas_cols[i]}_{time_index}_divided_{mesas_cols[j]}_{time_index}\"] = df[f\"{mesas_cols[i]}_{time_index}\"] / (df[f\"{mesas_cols[j]}_{time_index}\"] + 1e-15)\n                \n            # bg_11_minus_hr_11_minus_bg_10_minus_hr_10 = bg_11_minus_hr_11 - bg_10_minus_hr_10\n            df[f\"{mesas_cols[i]}_11_minus_{mesas_cols[j]}_11_minus_{mesas_cols[i]}_10_minus_{mesas_cols[j]}_10\"]=\\\n            df[f\"{mesas_cols[i]}_11_minus_{mesas_cols[j]}_11\"] - df[f\"{mesas_cols[i]}_10_minus_{mesas_cols[j]}_10\"]\n    \n    # compute differences and combinations of all values\n    for col in mesas_cols:\n        for i in range(1,12):\n            # bg_11_minus_bg_10 = bg_11 - bg_10\n            df[f\"{col}_{i}_minus_{col}_{i-1}\"]=df[f\"{col}_{i}\"] - df[f\"{col}_{i-1}\"]\n            \n            # bg_11_plus_bg_10 = bg_11 + bg_10\n            df[f'{col}_{i}_plus_{col}_{i-1}'] = df[f'{col}_{i}'] + df[f'{col}_{i-1}']\n        \n            # bg_11_multiplied_bg_10 = bg_11 * bg_10\n            df[f'{col}_{i}_multiplied_{col}_{i-1}'] = df[f'{col}_{i}'] * df[f'{col}_{i-1}']\n            \n    # compute division only on worthy columns\n    for col in divisible_cols:\n        for i in range(1,12): \n            # bg_11_divided_bg_10 = bg_11 / bg_10\n            df[f'{col}_{i}_divided_{col}_{i-1}'] = df[f'{col}_{i}'] / df[f'{col}_{i-1}']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:29.617822Z","iopub.execute_input":"2025-01-05T17:15:29.618137Z","iopub.status.idle":"2025-01-05T17:15:29.634774Z","shell.execute_reply.started":"2025-01-05T17:15:29.618109Z","shell.execute_reply":"2025-01-05T17:15:29.633486Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"activity_dictionary = {'None': 0, 'Yoga': 1, 'Walking': 2, 'Walk': 2, 'Dancing': 3, 'Zumba': 4,\n                       'Strength training': 5,\n                       'Weights': 6, 'Aerobic Workout': 7, 'Workout': 8, 'HIIT': 9, 'Run': 10, 'Running': 10,\n                       'Bike': 11, 'Outdoor Bike': 11, 'Stairclimber': 12, 'Spinning': 13, 'Swim': 14,\n                       'Swimming': 14,\n                       'Tennis': 15, 'Indoor climbing': 16, 'Hike': 17, 'Sport': 18}\n\n# Create a dictionary that maps each patient to a number\npartecipants_dictionary = {'p01': 1, 'p02': 2, 'p03': 3, 'p04': 4, 'p05': 5, 'p06': 6, 'p07': 7, 'p08': 8, 'p09': 9,\n                           'p10': 10, 'p11': 11, 'p12': 12, 'p13': 13, 'p14': 14, 'p15': 15, 'p16': 16, 'p17': 17,\n                           'p18': 18, 'p19': 19, 'p20': 20, 'p21': 21, 'p22': 22, 'p23': 23, 'p24': 24}\n\ndef drop_features(X):\n    # drop carbs\n    X.drop([c for c in X.columns if 'carbs' in c],axis=1,inplace=True)\n\n\ndef transform_time(X):\n    # extract a hour and minute series\n    if not is_datetime(X['time']):\n        X['time'] = pd.to_datetime(X['time'], format='%H:%M:%S')\n    X[\"hour\"] = X['time'].dt.hour\n    X[\"minute\"] = X['time'].dt.minute\n\n    # create half day sin cos wave\n    X['sin_halfday'] = np.sin(2 * np.pi * X['hour'] / 12)\n    X['cos_halfday'] = np.cos(2 * np.pi * X['hour'] / 12)\n    # create a hour sin and cos wave\n    X['hour_sin'] = np.sin(2 * np.pi * X[\"hour\"] / 24)\n    X['hour_cos'] = np.cos(2 * np.pi * X[\"hour\"] / 24)\n    # create a minute sin and cos wave\n    X['minute_sin'] = np.sin(2 * np.pi * X[\"minute\"] / 60)\n    X['minute_cos'] = np.cos(2 * np.pi * X[\"minute\"] / 60)\n\n    # remove time\n    X.drop(['time'], axis=1, inplace=True)\n\n\ndef encode_and_0fill(X):\n    steps_columns = []\n    activity_columns = []\n    carbs_columns = []\n    cals_columns = []\n\n    for column in X.columns:\n        # if the column is a metric with a survey time\n        if 'steps_' in column:\n            steps_columns.append(column)\n        if 'activity_' in column:\n            activity_columns.append(column)\n        if 'carbs_' in column:\n            carbs_columns.append(column)\n        if 'cals_' in column:\n            cals_columns.append(column)\n\n    X[activity_columns] = X[activity_columns].fillna('None')  # assume no activity when empty\n\n    # apply static encoding for activities\n    for col in activity_columns:\n        # Assign values based on the dictionary and set -1 for empty values\n        X[col] = X[col].apply(lambda x: activity_dictionary.get(x, -1) if x != '' else -1)\n\n    # apply static encoding for patients\n    # Assign values based on the dictionary and set -1 for empty values\n    X['p_num'] = X['p_num'].apply(lambda x: partecipants_dictionary.get(x, -1) if x != '' else -1)\n\n    X[steps_columns] = X[steps_columns].fillna(0)  # assume 0 steps when empty\n    X[carbs_columns] = X[carbs_columns].fillna(0)  # assume 0 carbohydrate intake when empty\n    X[cals_columns] = X[cals_columns].fillna(0)  # assume 0 calories burned when empty\n\n\ndef fill_time_series(X):\n    # create a dictionary of metrics and their corresponding columns\n    metric_columns = {}\n    for column in X.columns:\n        if '_' in column:\n            # get the metric name\n            metric_name = column.split(\"_\")[0]\n            # get the array of existing columns with the same metrics (or create it) and add the current column\n            metric_columns_names = metric_columns.get(metric_name) or []\n            metric_columns_names.append(column)\n            # save the new array on the dictionary\n            metric_columns[metric_name] = metric_columns_names\n\n    # iterate metrics\n    for key, value in metric_columns.items():\n        # Sort the metric columns in descending order\n        columns = sorted(value, key=lambda x: '_'.join(x.split('_')[1:]), reverse=True)\n\n        # Fill missing values using backfill (and forward fill if the latest values are empty)\n        X[columns] = X[columns].bfill()\n        X[columns] = X[columns].ffill()\n\ndef delete_strong_correlated_columns(X):\n    # reduce  overfitting, strong correlations between nearest timestep features\n    X.drop([f'bg_{i}'for i in range(12) if i % 2 != 0], axis=1, inplace=True)\n\ndef preprocess_data(X):\n    #drop_features(X)\n    encode_and_0fill(X)\n    fill_time_series(X)\n    drop_prediction_columns(X)\n    #create_features(X)\n    transform_time(X)\n    #delete_strong_correlated_columns(X)\n\ndef preprocess_train_data(X):\n    #drop_features(X)\n    encode_and_0fill(X)\n    fill_time_series(X)\n    X = split_data(X)\n    #create_features(X)\n    transform_time(X)\n    #delete_strong_correlated_columns(X)\n    return X","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:29.636687Z","iopub.execute_input":"2025-01-05T17:15:29.637105Z","iopub.status.idle":"2025-01-05T17:15:29.660198Z","shell.execute_reply.started":"2025-01-05T17:15:29.637070Z","shell.execute_reply":"2025-01-05T17:15:29.658859Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X['target'] = y\nX = preprocess_train_data(X)\nX.to_csv('preprocessed_train.csv')\ny = X['target']\nX = X.drop(['target'], axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:15:29.661704Z","iopub.execute_input":"2025-01-05T17:15:29.662079Z","iopub.status.idle":"2025-01-05T17:38:49.102556Z","shell.execute_reply.started":"2025-01-05T17:15:29.662044Z","shell.execute_reply":"2025-01-05T17:38:49.100020Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('total columns: ', len(X.columns.values))\n\nfeatures_with_missing_values = [col for col in X.columns if X[col].isnull().any()]\n\nprint('number of columns with missing values: ', len(features_with_missing_values))\nprint(features_with_missing_values)\nprint('--- Percentage of Missing Values ---')\nprint(100*X[features_with_missing_values].isnull().sum() / X.shape[0])\n\ncategorical_cols = [cname for cname in X.columns if X[cname].dtype == \"object\"]\nprint('number of categorical columns: ', len(categorical_cols))\nprint(categorical_cols)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:38:49.105199Z","iopub.execute_input":"2025-01-05T17:38:49.105636Z","iopub.status.idle":"2025-01-05T17:38:49.206838Z","shell.execute_reply.started":"2025-01-05T17:38:49.105598Z","shell.execute_reply":"2025-01-05T17:38:49.205286Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"border-radius:12px; border:#b48dda solid; padding: 15px; text-align:left\">\n\nDataframe successfully cleaned up, no need for further pipelines.","metadata":{}},{"cell_type":"markdown","source":"## <p style=\"padding:15px; background-color:#b48dda; font-family:arial; font-weight:bold; color:white; font-size:100%; letter-spacing: 2px; text-align:left; border-radius: 10px 10px; margin-bottom: -30px; margin-top: 50px;\">Train model</p>","metadata":{}},{"cell_type":"code","source":"# instantiate data pipeline\nfrom src.pipelines.empty_dt_pipeline import EmptyDTPipeline\npipeline = EmptyDTPipeline(X)\n\n# pick a model, and a trainer\nmodel_type = XGBRegressorWrapper()\ntrainer = AccurateCrossTrainer(pipeline, model_type, metric=AccuracyMetric.RMSE)\n\n# optimizing parameters worsens performance for this dataset\noptimized_params = {\n            'objective': 'reg:squarederror',\n            'learning_rate': 0.03,\n            'n_jobs': -1,\n}\n\n# Train model\n_, boost_rounds, _ = trainer.validate_model(X, y, log_level=0, params=optimized_params)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T17:38:49.208141Z","iopub.execute_input":"2025-01-05T17:38:49.208479Z","iopub.status.idle":"2025-01-05T18:21:01.694041Z","shell.execute_reply.started":"2025-01-05T17:38:49.208436Z","shell.execute_reply":"2025-01-05T18:21:01.691840Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainer.show_feature_importance(X)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T18:21:01.696860Z","iopub.execute_input":"2025-01-05T18:21:01.698215Z","iopub.status.idle":"2025-01-05T18:21:02.285848Z","shell.execute_reply.started":"2025-01-05T18:21:01.698169Z","shell.execute_reply":"2025-01-05T18:21:02.284645Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainer.show_loss()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T18:21:02.287198Z","iopub.execute_input":"2025-01-05T18:21:02.287562Z","iopub.status.idle":"2025-01-05T18:21:02.598124Z","shell.execute_reply.started":"2025-01-05T18:21:02.287530Z","shell.execute_reply":"2025-01-05T18:21:02.596824Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## <p style=\"padding:15px; background-color:#b48dda; font-family:arial; font-weight:bold; color:white; font-size:100%; letter-spacing: 2px; text-align:left; border-radius: 10px 10px; margin-bottom: -30px; margin-top: 50px;\">Train complete model on all data</p>","metadata":{}},{"cell_type":"code","source":"# fit complete_model on all data from the training data\ncomplete_model = trainer.train_model(X, y, iterations=boost_rounds, params=optimized_params)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T18:21:02.599776Z","iopub.execute_input":"2025-01-05T18:21:02.600126Z","iopub.status.idle":"2025-01-05T18:24:23.747192Z","shell.execute_reply.started":"2025-01-05T18:21:02.600093Z","shell.execute_reply":"2025-01-05T18:24:23.745897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# pickle components, to experiment later\nimport shutil\nfrom src.pipelines.dt_pipeline import save_data_model\nfrom src.trainers.trainer import save_model\n\n# save model file for current dataset on target directory\nprint(\"Saving data model...\")\nsave_data_model(X)\n\n# save trained pipeline on target directory\nprint(\"Saving pipeline...\")\npipeline.save_pipeline()\n\n# save model on target directory\nprint(\"Saving fitted model...\")\nsave_model(complete_model)\n\nshutil.copytree('../target', 'target')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T19:04:51.316167Z","iopub.execute_input":"2025-01-05T19:04:51.316648Z","iopub.status.idle":"2025-01-05T19:04:51.412707Z","shell.execute_reply.started":"2025-01-05T19:04:51.316610Z","shell.execute_reply":"2025-01-05T19:04:51.411330Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## <p style=\"padding:15px; background-color:#b48dda; font-family:arial; font-weight:bold; color:white; font-size:100%; letter-spacing: 2px; text-align:left; border-radius: 10px 10px; margin-bottom: -30px; margin-top: 50px;\">Make predictions on the competition data, and generate submission</p>","metadata":{}},{"cell_type":"code","source":"# Load the data\ntest_data_path = '/kaggle/input/brist1d/test.csv'\ntest_data = pd.read_csv(test_data_path, index_col='id')\ntest_data = test_data.rename(columns = lambda x:re.sub('[^A-Za-z0-9_]+', '_', x))\npreprocess_data(test_data)\n\n# make predictions which we will submit. \ntest_preds = complete_model.predict(test_data)\n\n# Save predictions in the format used for competition scoring\noutput = pd.DataFrame({'id': test_data.index,\n                       'bg+1:00': test_preds})\noutput.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-05T18:24:23.813442Z","iopub.execute_input":"2025-01-05T18:24:23.813849Z","iopub.status.idle":"2025-01-05T18:24:24.719590Z","shell.execute_reply.started":"2025-01-05T18:24:23.813814Z","shell.execute_reply":"2025-01-05T18:24:24.718456Z"}},"outputs":[],"execution_count":null}]}