{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":53666,"databundleVersionId":6589269,"sourceType":"competition"},{"sourceId":6507308,"sourceType":"datasetVersion","datasetId":3706667},{"sourceId":7005405,"sourceType":"datasetVersion","datasetId":3856023},{"sourceId":151583600,"sourceType":"kernelVersion"}],"dockerImageVersionId":30558,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -q /kaggle/input/xgboost-2-0-0-whl/xgboost-2.0.0-py3-none-manylinux2014_x86_64.whl","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:18:46.490027Z","iopub.execute_input":"2023-11-26T23:18:46.490527Z","iopub.status.idle":"2023-11-26T23:19:03.565883Z","shell.execute_reply.started":"2023-11-26T23:18:46.490478Z","shell.execute_reply":"2023-11-26T23:19:03.5642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Question and Problem Definition\n\nDefined on Kaggle as such: The goal of this competition is to detect sleep onset and wake. You will develop a model trained on wrist-worn accelerometer data in order to determine a person's sleep state.\n\n**Evaluation**\n\nEvaluated on average precision of detected events, averaged over timestamps error tolerance thresholds, averaged over event classes\n\nThe expected output should have a confidence score attached to each event. [How to calculate is listed here](https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/overview)","metadata":{}},{"cell_type":"code","source":"# data analysis and wrangling\nimport pandas as pd\nimport numpy as np\nimport random as rnd\nimport datetime\nfrom itertools import groupby\nfrom sklearn.model_selection import train_test_split\nfrom pandas.api.types import is_datetime64_ns_dtype\nfrom IPython.display import display_html, clear_output, Markdown;\n\n# visualization\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\n# machine learning\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.svm import SVC, LinearSVC\nfrom sklearn.ensemble import RandomForestClassifier\nfrom sklearn.neighbors import KNeighborsClassifier\nfrom sklearn.naive_bayes import GaussianNB\nfrom sklearn.linear_model import Perceptron\nfrom sklearn.linear_model import SGDClassifier\nfrom sklearn.tree import DecisionTreeClassifier\n\nfrom sklearn.model_selection import StratifiedGroupKFold, GroupKFold\nfrom sklearn.ensemble import GradientBoostingClassifier\nfrom xgboost import XGBRegressor, XGBClassifier\nfrom lightgbm import LGBMRegressor, LGBMClassifier\nfrom lightgbm import log_evaluation, early_stopping, record_evaluation\nfrom catboost import CatBoostRegressor, CatBoostClassifier, Pool\n\nfrom imblearn.under_sampling import RandomUnderSampler\nfrom joblib import Parallel, delayed\nimport gc\nimport plotly.express as px\n\nfrom os import system, getpid, walk;\nfrom psutil import Process;\nfrom colorama import Fore, Style, init\n\n\n# Remove warnings\npd.options.mode.chained_assignment = None  # default='warn'\nimport warnings\nwarnings.filterwarnings(\"ignore\")\nfrom metric import score ","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:19:03.568942Z","iopub.execute_input":"2023-11-26T23:19:03.56936Z","iopub.status.idle":"2023-11-26T23:19:08.631346Z","shell.execute_reply.started":"2023-11-26T23:19:03.569325Z","shell.execute_reply":"2023-11-26T23:19:08.630283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Acquire Data","metadata":{}},{"cell_type":"code","source":"#train data\ntrain_series = pd.read_parquet('/kaggle/input/child-mind-institute-detect-sleep-states/train_series.parquet', engine='pyarrow')\ntrain_events = pd.read_csv('/kaggle/input/child-mind-institute-detect-sleep-states/train_events.csv')\n\n#test data\ntest_series = pd.read_parquet('/kaggle/input/child-mind-institute-detect-sleep-states/test_series.parquet', engine='pyarrow')","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:19:08.633143Z","iopub.execute_input":"2023-11-26T23:19:08.634016Z","iopub.status.idle":"2023-11-26T23:20:27.597383Z","shell.execute_reply.started":"2023-11-26T23:19:08.63387Z","shell.execute_reply":"2023-11-26T23:20:27.595852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"combine = [train_series, train_events, test_series]","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.600267Z","iopub.execute_input":"2023-11-26T23:20:27.600783Z","iopub.status.idle":"2023-11-26T23:20:27.606125Z","shell.execute_reply.started":"2023-11-26T23:20:27.600745Z","shell.execute_reply":"2023-11-26T23:20:27.604891Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Analyze the describing data\n\nThe training features and outcomes are already split. The features are stored in the train series dataset while the outcomes are in the train events.\n\n**Which features are avalilable?**","metadata":{}},{"cell_type":"code","source":"print(train_series.columns.values)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.608088Z","iopub.execute_input":"2023-11-26T23:20:27.608672Z","iopub.status.idle":"2023-11-26T23:20:27.620258Z","shell.execute_reply.started":"2023-11-26T23:20:27.608626Z","shell.execute_reply":"2023-11-26T23:20:27.619301Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**What is avaliable for the outcome?**","metadata":{}},{"cell_type":"code","source":"print(train_events.columns.values)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.621691Z","iopub.execute_input":"2023-11-26T23:20:27.622398Z","iopub.status.idle":"2023-11-26T23:20:27.632113Z","shell.execute_reply.started":"2023-11-26T23:20:27.62236Z","shell.execute_reply":"2023-11-26T23:20:27.63075Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will have to group the data by night. \n\n**Which features are categorical?**\n\n- Categorical: Event. \n\n**Which features are numerical?**\n\n- series_id, step, timestamp, anglez, enmo, night\n\nThe only categorical feature is what we are predicting","metadata":{}},{"cell_type":"code","source":"train_series.head()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.634022Z","iopub.execute_input":"2023-11-26T23:20:27.634449Z","iopub.status.idle":"2023-11-26T23:20:27.663949Z","shell.execute_reply.started":"2023-11-26T23:20:27.634418Z","shell.execute_reply":"2023-11-26T23:20:27.662594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_events.head()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.667296Z","iopub.execute_input":"2023-11-26T23:20:27.668451Z","iopub.status.idle":"2023-11-26T23:20:27.685021Z","shell.execute_reply.started":"2023-11-26T23:20:27.668407Z","shell.execute_reply":"2023-11-26T23:20:27.683831Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Which features may contain errors?**\n\nThere may be some points where the watch was removed. Data is still collected so it may seem suspiciously still. We have to be able to differentiate that from sleep","metadata":{}},{"cell_type":"code","source":"#train_series.info()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.686377Z","iopub.execute_input":"2023-11-26T23:20:27.686877Z","iopub.status.idle":"2023-11-26T23:20:27.69611Z","shell.execute_reply.started":"2023-11-26T23:20:27.686834Z","shell.execute_reply":"2023-11-26T23:20:27.694636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#train_events.info()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.701368Z","iopub.execute_input":"2023-11-26T23:20:27.701931Z","iopub.status.idle":"2023-11-26T23:20:27.710499Z","shell.execute_reply.started":"2023-11-26T23:20:27.701891Z","shell.execute_reply":"2023-11-26T23:20:27.709209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#test_series.info()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.712026Z","iopub.execute_input":"2023-11-26T23:20:27.712424Z","iopub.status.idle":"2023-11-26T23:20:27.724214Z","shell.execute_reply.started":"2023-11-26T23:20:27.71239Z","shell.execute_reply":"2023-11-26T23:20:27.722654Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**What is the distribution of numerical feature values across the samples?**\n\n","metadata":{}},{"cell_type":"code","source":"#train_series.describe()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.726495Z","iopub.execute_input":"2023-11-26T23:20:27.727571Z","iopub.status.idle":"2023-11-26T23:20:27.735553Z","shell.execute_reply.started":"2023-11-26T23:20:27.727516Z","shell.execute_reply":"2023-11-26T23:20:27.734058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#train_events.describe()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.737509Z","iopub.execute_input":"2023-11-26T23:20:27.738318Z","iopub.status.idle":"2023-11-26T23:20:27.747235Z","shell.execute_reply.started":"2023-11-26T23:20:27.738271Z","shell.execute_reply":"2023-11-26T23:20:27.746138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#test_series.describe()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.748688Z","iopub.execute_input":"2023-11-26T23:20:27.74935Z","iopub.status.idle":"2023-11-26T23:20:27.758072Z","shell.execute_reply.started":"2023-11-26T23:20:27.749306Z","shell.execute_reply":"2023-11-26T23:20:27.75714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see what would be the average step for both onset and wake to try to estimate when sleep is most prominate. ","metadata":{}},{"cell_type":"code","source":"#train_events.groupby('event').describe().unstack(1)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.759141Z","iopub.execute_input":"2023-11-26T23:20:27.760127Z","iopub.status.idle":"2023-11-26T23:20:27.770597Z","shell.execute_reply.started":"2023-11-26T23:20:27.760091Z","shell.execute_reply":"2023-11-26T23:20:27.769409Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**We want to go based on series, so how many series are there?**\n\nThere are 277 unique series in the training data but we are only testing on 3","metadata":{}},{"cell_type":"code","source":"#print(train_events[\"series_id\"].nunique())\n#print(train_series[\"series_id\"].nunique())\n\n#test_series[\"series_id\"].unique()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.772Z","iopub.execute_input":"2023-11-26T23:20:27.772387Z","iopub.status.idle":"2023-11-26T23:20:27.783051Z","shell.execute_reply.started":"2023-11-26T23:20:27.772353Z","shell.execute_reply":"2023-11-26T23:20:27.782134Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Assumptions based on data analysis\n\nI have arrived at the following assumptions based on the analysis I have done so far.\n\n**Correlating.**\n\nWe want to know how well does each feature correlate with Sleep Onset/Wake. We want to do this early in our project and match these quick correlations with modelled correlations later in the project.\n\n**Completing.**\n\n1. We may want to complete Step feature as it is definitely correlated to sleep event.\n2. We may want to complete the Timestamp feature as it may also correlates with step and event.\n\n**Correcting.**\n\n1. \n\n**Creating.**\n\n1. We may want to create time/step bands to indicate time of day.\n2. We may want to create an anglez band.\n3. We may want to create an enmo band.\n4. We may also want to create a new category based on hour, month, and time.\n5. We may want to encode onset and wake and add two new features : sleep and awake. \n6. We want to create a night category based on the number of nights in the series\n\n**Classifying.**\n\nWe may also add to our assumptions based on the problem description noted earlier.\n\n1. ID which time hours someone is most likely to be sleeping, so we can id a time frame before and after when someone is mostly likely experiencing onset and sleep wake.\n\nThe following events can be seen in the data: onset, wake, awake, sleep, None\n- None be no activitity at all (ie no watch being worn)","metadata":{}},{"cell_type":"markdown","source":"## Handling Empty Values and Encoding\n\nBefore we analyze further by pivoting, lets fill in any empty values and encode events.\n\nWe are going to be looking at the test series because it is a smaller set to process. There are millions of rows in teh training set.","metadata":{}},{"cell_type":"code","source":"#Turning the date into a datetime object\ntrain_events['date'] = train_events['timestamp'].str.split('T', expand=True)[0]\ntrain_events['time'] = train_events['timestamp'].str.split('T', expand=True)[1].str.split('-', expand=True)[0]\ntrain_events['timestamp'] = pd.to_datetime(train_events['date']+' '+train_events['time'])\ntrain_events['hour'] = train_events['timestamp'].dt.hour","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:27.784266Z","iopub.execute_input":"2023-11-26T23:20:27.784925Z","iopub.status.idle":"2023-11-26T23:20:28.043615Z","shell.execute_reply.started":"2023-11-26T23:20:27.784889Z","shell.execute_reply":"2023-11-26T23:20:28.042249Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_events.head()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.045237Z","iopub.execute_input":"2023-11-26T23:20:28.045671Z","iopub.status.idle":"2023-11-26T23:20:28.067384Z","shell.execute_reply.started":"2023-11-26T23:20:28.045633Z","shell.execute_reply":"2023-11-26T23:20:28.065961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Analyze by pivoting features\n\nWe can only do this if we do not have any empty values. Currently, there is empty step and timestamps.\n\nIf we want to see a correlation between sleep onset and wake and time, we need to fill in time and step fully.\n\n#### Visualizing Onset and Wakeup time distribution","metadata":{}},{"cell_type":"code","source":"# Set figure size\nplt.figure(figsize=(12, 6))\n\n# Seperate the data by event type\nevent_onset = train_events[train_events['event'] == \"onset\"]\nevent_wakeup = train_events[train_events['event'] == \"wakeup\"]\n\n# Plot onset event distribution\nsns.histplot(event_onset['hour'].dropna(), kde=False, \n             bins=24, color = 'blue', discrete=True)\n\n# Plot wakeup event distribution\nsns.histplot(event_wakeup['hour'].dropna(), kde=False, \n             bins=24, color = 'red', discrete=True)\n\n# Add labels\nplt.title('Event Distribution')\nplt.xlabel('Hour')\nplt.ylabel('Count')\nplt.legend(['Onset', 'Wakeup'])\n\n# Show plot\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.069169Z","iopub.execute_input":"2023-11-26T23:20:28.069807Z","iopub.status.idle":"2023-11-26T23:20:28.679509Z","shell.execute_reply.started":"2023-11-26T23:20:28.06977Z","shell.execute_reply":"2023-11-26T23:20:28.67683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Visualize if Month effects sleep times?\n\nFollowing common logic, with daylight shifts through the year, later months may have a sooner sleep time. Lets see if month should be included in the training data.","metadata":{}},{"cell_type":"code","source":"# Create the month column\ntrain_events['month'] = train_events['timestamp'].dt.month\n\n\n# A Function to plot the distribution of each event by hour for the provided month\ndef plot_event_distribution_by_month(month):\n    # Set figure size\n    plt.figure(figsize=(12, 6))\n\n    # Seperate the data by event type\n    event_onset = train_events[(train_events['event'] == \"onset\") & (train_events['month'] == month)]\n    event_wakeup = train_events[(train_events['event'] == \"wakeup\") & (train_events['month'] == month)]\n\n    # Plot onset event distribution\n    sns.histplot(event_onset['hour'].dropna(), kde=False, \n                 bins=24, color = 'blue', discrete=True)\n\n    # Plot wakeup event distribution\n    sns.histplot(event_wakeup['hour'].dropna(), kde=False, \n                 bins=24, color = 'red', discrete=True)\n\n    # Add labels\n    plt.title('Event Distribution for Month: ' + str(month))\n    plt.xlabel('Hour')\n    plt.ylabel('Count')\n    plt.legend(['Onset', 'Wakeup'])\n\n    # Show plot\n    plt.show()\n\ntrain_events.head()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.682604Z","iopub.execute_input":"2023-11-26T23:20:28.684175Z","iopub.status.idle":"2023-11-26T23:20:28.71515Z","shell.execute_reply.started":"2023-11-26T23:20:28.684122Z","shell.execute_reply":"2023-11-26T23:20:28.713906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#plot_event_distribution_by_month(1)\n#plot_event_distribution_by_month(3)\n#plot_event_distribution_by_month(6)\n#plot_event_distribution_by_month(8)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.719122Z","iopub.execute_input":"2023-11-26T23:20:28.719599Z","iopub.status.idle":"2023-11-26T23:20:28.725272Z","shell.execute_reply.started":"2023-11-26T23:20:28.719557Z","shell.execute_reply":"2023-11-26T23:20:28.724117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see that most people wakeup between 6-8am and go to sleep between 8-11pm.\n\nWe should remember that we are also give accelerometer data (enmo and anglez) and show also try to focus on that.\n\n#### Visualizing enmo and angles with onset and wakeup change points\nThere is 100+ series IDs so we are going to focus on 10\n\nThe ENMO and Anglez seems to have a different baseline for each child, but they still follow an overall pattern of highs being periods of wake and lows being sleep.\n\nExtended lows seem to indicated no activitiy (aka watch off)","metadata":{}},{"cell_type":"code","source":"def plot_series_with_events(series_id, train_series, train_events):\n    # Filter the DataFrame based on the series_id\n    sample_serie = train_series[train_series['series_id'] == series_id]\n    \n    # Convert timestamp to Pandas datetime object\n    sample_serie['date'] = sample_serie['timestamp'].str.split('T', expand=True)[0]\n    sample_serie['time'] = sample_serie['timestamp'].str.split('T', expand=True)[1].str.split('-', expand=True)[0]\n    sample_serie['timestamp'] = pd.to_datetime(sample_serie['date'] + ' ' + sample_serie['time'])\n    \n    # Filter event data based on the series_id\n    sample_events = train_events[train_events['series_id'] == series_id]\n    sample_onset = sample_events.loc[sample_events['event'] == 'onset', 'timestamp'].dropna()\n    sample_wakeup = sample_events.loc[sample_events['event'] == 'wakeup', 'timestamp'].dropna()\n    \n    # Helper function to plot data and events\n    def plot_data_and_events(data, ylabel):\n        plt.figure(figsize=(20, 3))\n        plt.plot(sample_serie['timestamp'], sample_serie[data], label=data, linewidth=1)\n        \n        for onset in sample_onset:\n            plt.axvline(x=onset, color='r', linestyle='--', label='onset')\n            \n        for wakeup in sample_wakeup:\n            plt.axvline(x=wakeup, color='g', linestyle='--', label='wakeup')\n            \n        handles, labels = plt.gca().get_legend_handles_labels()\n        new_labels, new_handles = [], []\n        for handle, label in zip(handles, labels):\n            if label not in new_labels:\n                new_handles.append(handle)\n                new_labels.append(label)\n        \n        plt.legend(new_handles, new_labels)\n        plt.xlabel('Timestamp')\n        plt.ylabel(ylabel)\n        plt.title(f'{ylabel} over Time with Event Flags - '+series_id)\n        plt.show()\n    \n    # Plot enmo and anglez\n    plot_data_and_events('enmo', 'ENMO Value')\n    plot_data_and_events('anglez', 'anglez Value')","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.726978Z","iopub.execute_input":"2023-11-26T23:20:28.72793Z","iopub.status.idle":"2023-11-26T23:20:28.74084Z","shell.execute_reply.started":"2023-11-26T23:20:28.727821Z","shell.execute_reply":"2023-11-26T23:20:28.739595Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#for series_id in train_events['series_id'].unique()[:10]:\n #   plot_series_with_events(series_id, train_series, train_events)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.742164Z","iopub.execute_input":"2023-11-26T23:20:28.742775Z","iopub.status.idle":"2023-11-26T23:20:28.757828Z","shell.execute_reply.started":"2023-11-26T23:20:28.742738Z","shell.execute_reply":"2023-11-26T23:20:28.756573Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can bin the ENMO and Anglez and for each bin, see if there is a correlation of range for onset and wake.\n\nSimilarly, for each hour in the day, we can see if there is a correlation for when onset and wake occurs.\n\n\n**NEED TO DO**\n- Combine Anglez and ENMO with event type data\n- ID Step and time correlation\n- Add Awake and Asleep to event types\n- Show if binned values influence distribution","metadata":{}},{"cell_type":"code","source":"# Create a hist graph of binned enmo values showing the distribution of each event\n# Need to add the event types to the train_series DataFrame\n\ntrain_events.head()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.75978Z","iopub.execute_input":"2023-11-26T23:20:28.76068Z","iopub.status.idle":"2023-11-26T23:20:28.788654Z","shell.execute_reply.started":"2023-11-26T23:20:28.760633Z","shell.execute_reply":"2023-11-26T23:20:28.787755Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Wrangle Data\n\n### Dropping features\nFor the series that do not have any timestamp or step data in the event, we are going to drop it from our sets.\n\nAfter splitting into timestamp into date, month and hour, we can drop timestamp\n\nFor the non onset or wakeup, we are going to classify them as other.","metadata":{}},{"cell_type":"code","source":"# For the score function variables\ncolumn_names = {\n    'series_id_column_name': 'series_id',\n    'time_column_name': 'step',\n    'event_column_name': 'event',\n    'score_column_name': 'score',\n}\n\ntolerances = {\n    'onset': [12, 36, 60, 90, 120, 150, 180, 240, 300, 360], \n    'wakeup': [12, 36, 60, 90, 120, 150, 180, 240, 300, 360]\n}\n\ndef PrintColor(text:str, color = Fore.BLUE, style = Style.BRIGHT):\n    \"Prints color outputs using colorama using a text F-string\";\n    print(style + color + text + Style.RESET_ALL); \n\ndef GetMemUsage():\n    \"\"\"\n    This function defines the memory usage across the kernel. \n    Source-\n    https://stackoverflow.com/questions/61366458/how-to-find-memory-usage-of-kaggle-notebook\n    \"\"\";\n    \n    pid = getpid();\n    py = Process(pid);\n    memory_use = py.memory_info()[0] / 2. ** 30;\n    return f\"RAM memory GB usage = {memory_use :.4}\";","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.790338Z","iopub.execute_input":"2023-11-26T23:20:28.791104Z","iopub.status.idle":"2023-11-26T23:20:28.799703Z","shell.execute_reply.started":"2023-11-26T23:20:28.791066Z","shell.execute_reply":"2023-11-26T23:20:28.798226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def feat_eng(df):\n    \n    df['series_id'] = df['series_id'].astype('category')\n    df['timestamp'] = pd.to_datetime(df['timestamp']).apply(lambda t: t.tz_localize(None))\n    df['hour'] = df[\"timestamp\"].dt.hour\n    #df[\"dayofweek\"] = df[\"timestamp\"].dt.dayofweek\n    df['month'] = df[\"timestamp\"].dt.month\n    df[\"spring\"] = df[\"month\"].apply(lambda x: 1 if x in [3,4,5] else 0)\n    df[\"summer\"] = df[\"month\"].apply(lambda x: 1 if x in [6,7,8] else 0)\n    df[\"autumn\"] = df[\"month\"].apply(lambda x: 1 if x in [9,10,11] else 0)\n    df[\"winter\"] = df[\"month\"].apply(lambda x: 1 if x in [12,1,2] else 0)\n    df = df.drop(columns = ['month'])\n    \n    \n    df.sort_values(['timestamp'], inplace=True)\n    df.set_index('timestamp', inplace=True)\n    \n    df['lids'] = np.maximum(0., df['enmo'] - 0.02)\n    df['lids'] = df['lids'].rolling(f'{120*5}s', center=True, min_periods=1).agg('sum')\n    df['lids'] = 100 / (df['lids'] + 1)\n    df['lids'] = df['lids'].rolling(f'{360*5}s', center=True, min_periods=1).agg('mean').astype(np.float32)\n    \n    df[\"enmo\"] = (df[\"enmo\"]*1000).astype(np.int16)\n    df[\"anglez\"] = df[\"anglez\"].astype(np.int16)\n    df[\"anglezdiffabs\"] = df[\"anglez\"].diff().abs().astype(np.float32)\n    \n    for col in ['enmo', 'anglez', 'anglezdiffabs']:\n        \n        # periods in seconds        \n        periods = [60,360,720,3600] \n        \n        for n in periods:\n            \n            rol_args = {'window':f'{n+5}s', 'min_periods':10, 'center':True}\n            \n            for agg in ['median', 'mean', 'max', 'min', 'var']:\n                df[f'{col}_{agg}_{n}'] = df[col].rolling(**rol_args).agg(agg).astype(np.float32).values\n                gc.collect()\n            \n            if n == max(periods):\n                df[f'{col}_mad_{n}'] = (df[col] - df[f'{col}_median_{n}']).abs().rolling(**rol_args).median().astype(np.float32)\n            \n            df[f'{col}_amplit_{n}'] = df[f'{col}_max_{n}']-df[f'{col}_min_{n}']\n            df[f'{col}_amplit_{n}_min'] = df[f'{col}_amplit_{n}'].rolling(**rol_args).min().astype(np.float32).values\n            \n#             if col in ['enmo', 'anglez']:\n            df[f'{col}_diff_{n}_max'] = df[f'{col}_max_{n}'].diff().abs().rolling(**rol_args).max().astype(np.float32)\n            df[f'{col}_diff_{n}_mean'] = df[f'{col}_max_{n}'].diff().abs().rolling(**rol_args).mean().astype(np.float32)\n\n    \n            gc.collect()\n    \n    df.reset_index(inplace=True)\n    df.dropna(inplace=True)\n\n    return df","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.802361Z","iopub.execute_input":"2023-11-26T23:20:28.802926Z","iopub.status.idle":"2023-11-26T23:20:28.823261Z","shell.execute_reply.started":"2023-11-26T23:20:28.802879Z","shell.execute_reply":"2023-11-26T23:20:28.822056Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"file = '/kaggle/input/zzzs-lightweight-training-dataset-target/Zzzs_train_multi.parquet'\n\ndef feat_eng_by_id(idx):\n    \n    from warnings import simplefilter \n    simplefilter(action=\"ignore\", category=pd.errors.PerformanceWarning)\n    \n    df  = pd.read_parquet(file, filters=[('series_id','=',idx)])\n    df['awake'] = df['awake'].astype(np.int8)\n    df = feat_eng(df)\n    \n    return df","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.825264Z","iopub.execute_input":"2023-11-26T23:20:28.826159Z","iopub.status.idle":"2023-11-26T23:20:28.841925Z","shell.execute_reply.started":"2023-11-26T23:20:28.82611Z","shell.execute_reply":"2023-11-26T23:20:28.840879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DEV = False\nTRUNCATION_FACTOR = 60\n\nseries_id  = pd.read_parquet(file, columns=['series_id'])\nseries_id = series_id.series_id.unique()\n\nprint(len(series_id))\n\nif DEV:\n    series_id = series_id[::10]\n    \nweird_series = ['31011ade7c0a', 'a596ad0b82aa']\n\nseries_id = [s for s in series_id if s not in weird_series]","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:28.846737Z","iopub.execute_input":"2023-11-26T23:20:28.847425Z","iopub.status.idle":"2023-11-26T23:20:31.313352Z","shell.execute_reply.started":"2023-11-26T23:20:28.847387Z","shell.execute_reply":"2023-11-26T23:20:31.312108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\ntrain = Parallel(n_jobs=6)(delayed(feat_eng_by_id)(i) for i in series_id)\ntrain = pd.concat(train, ignore_index=True)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:20:31.314957Z","iopub.execute_input":"2023-11-26T23:20:31.31532Z","iopub.status.idle":"2023-11-26T23:25:53.769853Z","shell.execute_reply.started":"2023-11-26T23:20:31.315291Z","shell.execute_reply":"2023-11-26T23:25:53.768235Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X, y = train.drop(columns=['awake']), train['awake']\ngroups = train[\"series_id\"].values\n\ngc.collect()\n\nif not DEV:\n    del train\n    gc.collect()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:25:53.771424Z","iopub.execute_input":"2023-11-26T23:25:53.771828Z","iopub.status.idle":"2023-11-26T23:26:00.814437Z","shell.execute_reply.started":"2023-11-26T23:25:53.771797Z","shell.execute_reply":"2023-11-26T23:26:00.81226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class EnsembleAvgProba():\n    \n    def __init__(self, classifiers):\n        \n        self.classifiers = classifiers\n    \n    def fit(self,X,y):\n        \n        for classifier in self.classifiers:                \n            classifier.fit(X, y)\n            gc.collect()\n     \n    def predict_proba(self, X):\n        \n        probs = []\n        \n        for m in self.classifiers:\n            probs.append(m.predict_proba(X))\n        \n        probabilities = np.stack(probs)\n        p = np.mean(probabilities, axis=0)\n        \n        return p \n    \n    def predict(self, X):\n        \n        probs = []\n        \n        for m in self.classifiers:\n            probs.append(m.predict(X))\n        \n        probabilities = np.stack(probs)\n        p = np.mean(probabilities, axis=0)\n        \n        return p.round()","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:26:00.816577Z","iopub.execute_input":"2023-11-26T23:26:00.817174Z","iopub.status.idle":"2023-11-26T23:26:00.827406Z","shell.execute_reply.started":"2023-11-26T23:26:00.817123Z","shell.execute_reply":"2023-11-26T23:26:00.825676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lgb_params1 = {    \n    'boosting_type':'gbdt',\n    'num_leaves':31,\n    'max_depth':6,\n    'learning_rate':0.03,\n    'n_estimators':850,\n    'subsample_for_bin':200000,\n    'min_child_weight':0.001,\n    'min_child_samples':20,\n    'subsample':0.9,\n#     'colsample_bytree':0.7,\n    'reg_alpha':0.05,\n    'reg_lambda':0.05,\n             }","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:26:00.829779Z","iopub.execute_input":"2023-11-26T23:26:00.830323Z","iopub.status.idle":"2023-11-26T23:26:00.840383Z","shell.execute_reply.started":"2023-11-26T23:26:00.830284Z","shell.execute_reply":"2023-11-26T23:26:00.83941Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5 Fold GroupKFold CV","metadata":{}},{"cell_type":"code","source":"def get_events(idx, classifier, file='test_series.parquet') :\n    \n    test  = pd.read_parquet(f'/kaggle/input/child-mind-institute-detect-sleep-states/{file}',\n                    filters=[('series_id','=',idx)])\n    test = feat_eng(test)\n    X_test = test.drop(columns=drop_cols)\n    test = test[drop_cols]\n\n    preds, probs = classifier.predict(X_test), classifier.predict_proba(X_test)[:, 1]\n    \n    test['prediction'] = preds\n    test['prediction'] = test['prediction'].rolling(360+1, center=True).median()\n    test['probability'] = probs\n    \n    test = test[test['prediction']!=2]\n    \n    test.loc[test['prediction']==0, 'probability'] = 1-test.loc[test['prediction']==0, 'probability']\n    test['score'] = test['probability'].rolling(60*12*5, center=True, min_periods=10).mean().bfill().ffill()\n\n    \n    test['pred_diff'] = test['prediction'].diff()\n    \n    test['event'] = test['pred_diff'].replace({1:'wakeup', -1:'onset', 0:np.nan})\n    \n    test_wakeup = test[test['event']=='wakeup'].groupby(test['timestamp'].dt.date).agg('first')\n    test_onset = test[test['event']=='onset'].groupby(test['timestamp'].dt.date).agg('last')\n    test = pd.concat([test_wakeup, test_onset], ignore_index=True).sort_values('timestamp')\n\n    return test\n\n\ndef get_validation_events(idx, classifier, test) :\n    \n    drop_cols = ['series_id', 'step', 'timestamp']\n    X_test = test.drop(columns=drop_cols)\n    test = test[drop_cols]\n    \n    #PrintColor(\"X_test\")\n    #display(X_test)\n    \n    #print()\n    \n    #PrintColor(\"test\")\n    #display(test)\n\n    preds, probs = classifier.predict(X_test), classifier.predict_proba(X_test)[:, 1]\n    \n    test['prediction'] = preds\n    test['prediction'] = test['prediction'].rolling(360+1, center=True).median()\n    test['probability'] = probs\n    \n    test = test[test['prediction']!=2]\n    \n    test.loc[test['prediction']==0, 'probability'] = 1-test.loc[test['prediction']==0, 'probability']\n    test['score'] = test['probability'].rolling(60*12*5, center=True, min_periods=10).mean().bfill().ffill()\n\n    \n    test['pred_diff'] = test['prediction'].diff()\n    \n    test['event'] = test['pred_diff'].replace({1:'wakeup', -1:'onset', 0:np.nan})\n    \n    test_wakeup = test[test['event']=='wakeup'].groupby(test['timestamp'].dt.date).agg('first')\n    test_onset = test[test['event']=='onset'].groupby(test['timestamp'].dt.date).agg('last')\n    test = pd.concat([test_wakeup, test_onset], ignore_index=True).sort_values('timestamp')\n\n    return test","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:26:00.842546Z","iopub.execute_input":"2023-11-26T23:26:00.843888Z","iopub.status.idle":"2023-11-26T23:26:00.862077Z","shell.execute_reply.started":"2023-11-26T23:26:00.843827Z","shell.execute_reply":"2023-11-26T23:26:00.861074Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_events2 = pd.read_csv('/kaggle/input/child-mind-institute-detect-sleep-states/train_events.csv')\ntrain_events2['timestamp'] = pd.to_datetime(train_events2['timestamp']).apply(lambda t: t.tz_localize(None))","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:26:00.863558Z","iopub.execute_input":"2023-11-26T23:26:00.864551Z","iopub.status.idle":"2023-11-26T23:26:01.044583Z","shell.execute_reply.started":"2023-11-26T23:26:00.864506Z","shell.execute_reply":"2023-11-26T23:26:01.043324Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if DEV:\n    SPLITS = 2\nelse:\n    SPLITS = 5\n    \nsgkf = StratifiedGroupKFold(n_splits=SPLITS)   \ncols_sub = ['series_id','step','event','score']\nCV_scores = 0\n\nfor i, (dev_ind, val_ind) in enumerate(sgkf.split(X, y, groups)):\n    \n    dev_ind = dev_ind[::TRUNCATION_FACTOR]\n    \n    model = EnsembleAvgProba(classifiers=[\n                    #LGBMClassifier(random_state=42, **lgb_params1),\n                    #GradientBoostingClassifier(n_estimators=100,max_depth=5,min_samples_leaf=300,random_state=42),\n                    RandomForestClassifier(n_estimators=500, min_samples_leaf=300, random_state=42, n_jobs=-1),\n                    #XGBClassifier(n_estimators=520,objective=\"binary:logistic\", learning_rate=0.02, max_depth=7, random_state=42)    \n    ])\n    \n    PrintColor(f\"---> Fold {i}:\", color=Fore.BLUE)\n    dev_groups, val_groups = groups[dev_ind], groups[val_ind]\n    X_train = X.iloc[dev_ind].drop(columns = ['series_id', 'step', 'timestamp'])\n    y_train = y.iloc[dev_ind]\n    print(f\"Train Indexes: {dev_ind}\")\n    print(f\"Val Indexes: {val_ind}\")\n    print(f\"Shape: {X_train.shape}\")\n    model.fit(X_train, y_train)\n    \n    series_id = list(set(val_groups))\n    print(f\"Train Groups: {set(dev_groups)}\")\n    print(f\"Val Groups: {set(val_groups)}\")\n    \n    assert len(set(dev_groups) & set(val_groups)) == 0\n\n    val_events = []\n\n    for idx in series_id: \n        \n        test = get_validation_events(idx, model, X[X.series_id == idx])\n        val_events.append(test[cols_sub])\n \n    val = pd.concat(val_events, ignore_index=True).reset_index(names='row_id')\n    val_solution = train_events2[train_events2['series_id'].isin(series_id)]\n    \n    print()\n    PrintColor(\"Your Output\", color=Fore.RED)\n    display(val[val.series_id == series_id[0]])\n    print()\n    PrintColor(\"Correct Output\", color=Fore.RED)\n    display(val_solution[val_solution.series_id == series_id[0]])\n    \n    fold_CV = score(val_solution, val, tolerances, **column_names)\n    \n    PrintColor(f\"Fold {i} CV Score | {fold_CV}\")\n    CV_scores += fold_CV\n    \n    print()\n    print()\n    \nPrintColor(f\"Mean CV Score | {CV_scores / SPLITS}\", color = Fore.GREEN)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T23:26:01.046921Z","iopub.execute_input":"2023-11-26T23:26:01.04732Z","iopub.status.idle":"2023-11-27T00:01:53.25076Z","shell.execute_reply.started":"2023-11-26T23:26:01.047289Z","shell.execute_reply":"2023-11-27T00:01:53.249289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# If a series ID in train_events has null values for steps and timestamp for every data point the series_id needs to be removed from train_events and train_series\n# Create a list of series_id that need to be removed\nseries_id_to_remove = []\nfor series_id in train_events['series_id'].unique():\n    if train_events[train_events['series_id'] == series_id]['steps'].isnull().all():\n        series_id_to_remove.append(series_id)\n\n# Remove the series_id from train_events\ntrain_events = train_events[~train_events['series_id'].isin(series_id_to_remove)]\n\n# Remove the series_id from train_series\ntrain_series = train_series[~train_series['series_id'].isin(series_id_to_remove)]\n","metadata":{"execution":{"iopub.status.busy":"2023-11-27T00:01:53.252571Z","iopub.execute_input":"2023-11-27T00:01:53.253657Z","iopub.status.idle":"2023-11-27T00:01:54.930143Z","shell.execute_reply.started":"2023-11-27T00:01:53.253617Z","shell.execute_reply":"2023-11-27T00:01:54.928248Z"},"trusted":true},"execution_count":null,"outputs":[]}]}