{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<h1 style=\"font-family:verdana;\"> <center>Daily Unsupervised learning of Time Series</center> </h1>\n \n***","metadata":{}},{"cell_type":"markdown","source":"## Introduction","metadata":{}},{"cell_type":"markdown","source":"<p style=\"font-size:15px; font-family:verdana; line-height: 1.7em\">On this notebook I will try to use several unsupervised methods of learning to find some patterns or other useful information which can help to predict FoG-events. \n    <br>\n    <u>I will use and describe next methods</u>:\n <ol>\n     <li>Rolling mean</li>\n     <li>Decomposition of time series</li>\n     <li>Segmentation, shapelets searching, patterns searching and other methods via <code>stumpy</code> \n         </ol>\n         <p style=\"font-size:15px; font-family:verdana; line-height: 1.7em\">\n    Hope this one will useful and helpful to you.\n    <br>\n    Enjoy! </p>","metadata":{}},{"cell_type":"markdown","source":"\n<div class=\"alert alert-block alert-info\" style=\"font-size:14px; font-family:verdana; line-height: 1.7em;\">\n    📌 &nbsp; Warning! This notebook is created by newbie  for education purposes only\n</div>","metadata":{}},{"cell_type":"markdown","source":"## Import & functions","metadata":{"execution":{"iopub.status.busy":"2023-05-19T13:21:24.839797Z","iopub.execute_input":"2023-05-19T13:21:24.840206Z","iopub.status.idle":"2023-05-19T13:21:24.861924Z","shell.execute_reply.started":"2023-05-19T13:21:24.840178Z","shell.execute_reply":"2023-05-19T13:21:24.861067Z"}}},{"cell_type":"code","source":"#import\nimport math\nimport numpy as np\nimport pandas as pd \nimport matplotlib as mpl\nimport matplotlib.pyplot as plt\nfrom matplotlib.pyplot import figure\nfrom matplotlib.patches import Rectangle\nimport matplotlib.dates as mdates\nimport seaborn as sns\nfrom pathlib import Path\nfrom scipy import stats\nimport os\nimport datetime\nimport stumpy","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-06-05T09:30:36.089144Z","iopub.execute_input":"2023-06-05T09:30:36.089591Z","iopub.status.idle":"2023-06-05T09:30:39.216306Z","shell.execute_reply.started":"2023-06-05T09:30:36.089540Z","shell.execute_reply":"2023-06-05T09:30:39.214995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#constants\nHZ = 100 #timesteps per second in daily\ng = 9.81 #unit of accelerometers in daily","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.219155Z","iopub.execute_input":"2023-06-05T09:30:39.219542Z","iopub.status.idle":"2023-06-05T09:30:39.224627Z","shell.execute_reply.started":"2023-06-05T09:30:39.219509Z","shell.execute_reply":"2023-06-05T09:30:39.223483Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#function to read .parquet files from root folder\n#it creates a dictionary with next type - {filname.parquet:dataframe}\n#begin and end can make a slice from csv_list\ndef read_parquet(parquet_list,root,begin = 0, end = None):\n    df_dict = {}\n    if end is None:\n        end = len(parquet_list)\n    for event in range(begin,end):\n        parq = parquet_list[event]\n        data = pd.read_parquet(root+parq)\n        df_dict[parq] = data       \n    return df_dict","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-06-05T09:30:39.226446Z","iopub.execute_input":"2023-06-05T09:30:39.226940Z","iopub.status.idle":"2023-06-05T09:30:39.247014Z","shell.execute_reply.started":"2023-06-05T09:30:39.226899Z","shell.execute_reply":"2023-06-05T09:30:39.245880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#data info\ndef data_info(data, title):\n    print(f'Short info of {title} dataframe' + '\\n')\n    display('Size',data.shape)\n    print('Info')\n    display(data.info())\n    print('Describe')\n    display(data.describe())\n    print('Head')\n    display(data.head())\n    print('NaNs in dataframe')\n    display(data.isna().sum())\n    print('Duplicates in dataframe')\n    display(data.duplicated().sum())\n    ","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.248841Z","iopub.execute_input":"2023-06-05T09:30:39.250064Z","iopub.status.idle":"2023-06-05T09:30:39.263093Z","shell.execute_reply.started":"2023-06-05T09:30:39.250029Z","shell.execute_reply":"2023-06-05T09:30:39.261784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Info","metadata":{}},{"cell_type":"markdown","source":"<p style=\"font-size:16px; font-family:verdana; line-height: 1.7em\">\nThe data series include three datasets, collected under distinct circumstances:\n\n- The tDCS FOG (tdcsfog) dataset, comprising data series collected in the lab, as subjects completed a FOG-provoking protocol.\n- The DeFOG (defog) dataset, comprising data series collected in the subject's home, as subjects completed a FOG-provoking protocol\n- The Daily Living (daily) dataset, comprising one week of continuous 24/7 recordings from sixty-five subjects. Forty-five subjects exhibit FOG symptoms and also have series in the defog dataset, while the other twenty subjects do not exhibit FOG symptoms and do not have series elsewhere in the data. \n    <br>\n   <p style=\"font-size:16px; font-family:verdana; line-height: 1.7em\"> I will concentrate on <code>daily</code> dataset only\n</p><br>","metadata":{}},{"cell_type":"markdown","source":"## Loading metadata\n***","metadata":{}},{"cell_type":"markdown","source":"<p style=\"font-size:15px; font-family:verdana; line-height: 1.7em\">Here we will get a short view on our subjects and other metadata </p><br>","metadata":{}},{"cell_type":"code","source":"#import metadata\nsubjects = pd.read_csv('/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/subjects.csv')\nevents = pd.read_csv('/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/events.csv')\ntasks = pd.read_csv('/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/tasks.csv')\ndaily_metadata = pd.read_csv('/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/daily_metadata.csv')\ndefog_metadata = pd.read_csv('/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/defog_metadata.csv')","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.266431Z","iopub.execute_input":"2023-06-05T09:30:39.267077Z","iopub.status.idle":"2023-06-05T09:30:39.339080Z","shell.execute_reply.started":"2023-06-05T09:30:39.267043Z","shell.execute_reply":"2023-06-05T09:30:39.337839Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#daily metadata\ndata_info(daily_metadata,' Daily Metadata')","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.340684Z","iopub.execute_input":"2023-06-05T09:30:39.341026Z","iopub.status.idle":"2023-06-05T09:30:39.429836Z","shell.execute_reply.started":"2023-06-05T09:30:39.340999Z","shell.execute_reply":"2023-06-05T09:30:39.428515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## daily and defog subjects\n***","metadata":{}},{"cell_type":"code","source":"#daily and defog subjects\nd_d_subjects = subjects.loc[subjects['Visit'].isna() == False ]\nd_d_subjects.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.431664Z","iopub.execute_input":"2023-06-05T09:30:39.432282Z","iopub.status.idle":"2023-06-05T09:30:39.455345Z","shell.execute_reply.started":"2023-06-05T09:30:39.432233Z","shell.execute_reply":"2023-06-05T09:30:39.454159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_info(d_d_subjects, 'Daily and DeFOG Subjects')","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.457323Z","iopub.execute_input":"2023-06-05T09:30:39.457897Z","iopub.status.idle":"2023-06-05T09:30:39.530094Z","shell.execute_reply.started":"2023-06-05T09:30:39.457855Z","shell.execute_reply":"2023-06-05T09:30:39.529014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Time Series Analysis\n***","metadata":{}},{"cell_type":"code","source":"#set a root directory to scan daily data from \nroot_dir = '/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/unlabeled/'\ndaily_list =  os.listdir(path=root_dir)\nprint(f'daily folder contains {len(daily_list)} daily records')\n#dictionary with file sizes\nsize_dict = {}\nfor file in daily_list:\n    file_stats = os.stat(root_dir + file)\n    size_dict[file] = int(file_stats.st_size / (1024 * 1024))\n    print(f'{file} size is {file_stats.st_size / (1024 * 1024):.2f} MB')","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.531703Z","iopub.execute_input":"2023-06-05T09:30:39.532886Z","iopub.status.idle":"2023-06-05T09:30:39.551395Z","shell.execute_reply.started":"2023-06-05T09:30:39.532843Z","shell.execute_reply":"2023-06-05T09:30:39.550251Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:1.5px;\">\n    <p>\n        My suggestion is that huge files are from <b>daily</b> series and smaller one are from <b>defog</b>\n        <br>\n        Let's check it out\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#values of file sizes\nvalues = []\nfor value in size_dict.values():\n    values.append(value)\n#search for huge files\n#as we suggest that huge files has defog series too\nn = 0\nfor value in values:\n    if value > 500:  #this value i try to find \n        n+=1\nprint('Number of huge files in daily:', n)       ","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.552821Z","iopub.execute_input":"2023-06-05T09:30:39.553247Z","iopub.status.idle":"2023-06-05T09:30:39.561313Z","shell.execute_reply.started":"2023-06-05T09:30:39.553207Z","shell.execute_reply":"2023-06-05T09:30:39.560077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:1.5px;\">\n    <p>\n        Well, maybe i was right and all files with size above <i>500 MB</i> has they time series at <b>defog</b>\n        <br>\n        Anyway, we will take one file with huge size and one file with smaller one \n    </p>\n</div>\n","metadata":{}},{"cell_type":"code","source":"#reading .parquet files from folder into a dict\n#we can choose a slice's begin and end points\ndaily_dict = read_parquet(daily_list,root_dir,62,64)","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:30:39.562831Z","iopub.execute_input":"2023-06-05T09:30:39.563147Z","iopub.status.idle":"2023-06-05T09:31:07.351049Z","shell.execute_reply.started":"2023-06-05T09:30:39.563120Z","shell.execute_reply":"2023-06-05T09:31:07.350039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#dict with .parquet files\ndaily_dict","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:31:07.352436Z","iopub.execute_input":"2023-06-05T09:31:07.352823Z","iopub.status.idle":"2023-06-05T09:31:07.369276Z","shell.execute_reply.started":"2023-06-05T09:31:07.352792Z","shell.execute_reply":"2023-06-05T09:31:07.368450Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#our first subject to analyse\nsubject_one = daily_dict['276630050d.parquet']\nsubject_one.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:31:07.370638Z","iopub.execute_input":"2023-06-05T09:31:07.371133Z","iopub.status.idle":"2023-06-05T09:31:07.382247Z","shell.execute_reply.started":"2023-06-05T09:31:07.371104Z","shell.execute_reply":"2023-06-05T09:31:07.381063Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#convert Time column into Time in sec\nsubject_one['Time'] = subject_one['Time'].div(HZ)","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-06-05T09:31:07.387914Z","iopub.execute_input":"2023-06-05T09:31:07.388471Z","iopub.status.idle":"2023-06-05T09:31:07.970955Z","shell.execute_reply.started":"2023-06-05T09:31:07.388432Z","shell.execute_reply":"2023-06-05T09:31:07.969647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"subject_one.head(101)","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:31:07.972325Z","iopub.execute_input":"2023-06-05T09:31:07.972725Z","iopub.status.idle":"2023-06-05T09:31:07.991661Z","shell.execute_reply.started":"2023-06-05T09:31:07.972690Z","shell.execute_reply":"2023-06-05T09:31:07.990322Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:1.5px;\">\n    <p>\n        To save your time I will research results of processing only in one accelerometer\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#drop other acc\nsubject_one_accv = subject_one.drop(['AccML','AccAP'],axis = 1)","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:31:07.993241Z","iopub.execute_input":"2023-06-05T09:31:07.993696Z","iopub.status.idle":"2023-06-05T09:31:08.721921Z","shell.execute_reply.started":"2023-06-05T09:31:07.993664Z","shell.execute_reply":"2023-06-05T09:31:08.720750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"subject_one_accv.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:31:08.723719Z","iopub.execute_input":"2023-06-05T09:31:08.724197Z","iopub.status.idle":"2023-06-05T09:31:08.735825Z","shell.execute_reply.started":"2023-06-05T09:31:08.724154Z","shell.execute_reply":"2023-06-05T09:31:08.735014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#simple plot\nsubject_one_accv.plot(\n    y = 'AccV',\n    x = 'Time',\n    title = 'AccV',\n    fontsize = 14,\n    figsize = (24,8),\n    grid = True,\n    legend = True,\n    color = 'salmon'\n)\nplt.xlabel('Time in s.', fontsize = 14)\nplt.ylabel('Accelerations in g units',fontsize = 14);","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:31:08.736879Z","iopub.execute_input":"2023-06-05T09:31:08.737702Z","iopub.status.idle":"2023-06-05T09:32:19.982916Z","shell.execute_reply.started":"2023-06-05T09:31:08.737668Z","shell.execute_reply":"2023-06-05T09:32:19.981181Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        Here we can see a raw data plot of AccV values. Next I will try to make this plot better and find a valuable info in it\n        <br>\n        Also we can note the <i>X-axis</i> units - it's a seconds for now, but we can get time stamp of record begining in metadata to create a time axis with hours and minutes. Save this idea for now\n        Next step is plotting rolling mean to see a main trend in this series\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"## Time Manipulations","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        First we transform a time in metadata to <code>datetime64</code>\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#setting object column to datetime of pandas\ndaily_metadata['Beginning of recording [00:00-23:59]'] = \\\npd.to_datetime(daily_metadata['Beginning of recording [00:00-23:59]'],\n               format='%H:%M')","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:32:19.985855Z","iopub.execute_input":"2023-06-05T09:32:19.986514Z","iopub.status.idle":"2023-06-05T09:32:20.001590Z","shell.execute_reply.started":"2023-06-05T09:32:19.986465Z","shell.execute_reply":"2023-06-05T09:32:20.000488Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#check\ndaily_metadata.dtypes","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:32:20.003072Z","iopub.execute_input":"2023-06-05T09:32:20.003770Z","iopub.status.idle":"2023-06-05T09:32:20.020354Z","shell.execute_reply.started":"2023-06-05T09:32:20.003737Z","shell.execute_reply":"2023-06-05T09:32:20.019088Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        Next we'll find our subject in this data and get his timestamp of begining\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#timestamp for subject\ndaily_metadata.loc[daily_metadata['Id'] == '276630050d']['Beginning of recording [00:00-23:59]']\n","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:32:20.022102Z","iopub.execute_input":"2023-06-05T09:32:20.022473Z","iopub.status.idle":"2023-06-05T09:32:20.045614Z","shell.execute_reply.started":"2023-06-05T09:32:20.022442Z","shell.execute_reply":"2023-06-05T09:32:20.044772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        Our subject has started his recording session in 12:00. Now we can add a time in seconds to his start time and get a new axis units\n        <br>\n        Of course this date is fake, don't watch on this please\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#we create an index from start date with correct step in seconds\nsubject_one_accv['Time'] = pd.date_range(start='01-01-2019t12:00', periods=len(subject_one_accv), freq=\"1s\")","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:32:20.047030Z","iopub.execute_input":"2023-06-05T09:32:20.047636Z","iopub.status.idle":"2023-06-05T09:32:20.563913Z","shell.execute_reply.started":"2023-06-05T09:32:20.047606Z","shell.execute_reply":"2023-06-05T09:32:20.562896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#check\nsubject_one_accv.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:32:20.565140Z","iopub.execute_input":"2023-06-05T09:32:20.565472Z","iopub.status.idle":"2023-06-05T09:32:20.576984Z","shell.execute_reply.started":"2023-06-05T09:32:20.565445Z","shell.execute_reply":"2023-06-05T09:32:20.575829Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        And now we can get a index in <code>datetime64</code>\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#check\nsubject_one_accv.set_index('Time', inplace = True)\n#check 2\nsubject_one_accv.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:32:20.578928Z","iopub.execute_input":"2023-06-05T09:32:20.579843Z","iopub.status.idle":"2023-06-05T09:32:20.596109Z","shell.execute_reply.started":"2023-06-05T09:32:20.579799Z","shell.execute_reply":"2023-06-05T09:32:20.595161Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        That's it, now we can turn to the main part of the show\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# Rolling Mean\n***","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n       <b>Rolling mean</b> is a mean of a certain number of previous periods in a time series\n        <br>\n        It helps us to see the main line of trend in series or to slightly smoothing the peaks\n        <br>\n        Even such a simple method as rolling mean can be useful in prediction of FoG-events\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n       <u>Note:</u> <i>Here I will create a slice from such a big dataframe, but on local machine you can skip it</i>\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#slice from data\nsubject_slice_accv = subject_one_accv[:round(len(subject_one_accv)/200)] # reduce the size in 200 times\nsubject_slice_accv.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:32:20.597605Z","iopub.execute_input":"2023-06-05T09:32:20.598071Z","iopub.status.idle":"2023-06-05T09:32:20.616259Z","shell.execute_reply.started":"2023-06-05T09:32:20.598040Z","shell.execute_reply":"2023-06-05T09:32:20.615081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#plot with rolling mean\n#timedelta to rolling mean size aka window size\ndelta = pd.Timedelta(200, \"s\")\n#here window means a time interval for mean calculating\n#I will use only a half of time series beacuse of memory errors\nplt.figure(figsize = (24,7))\nplt.plot(\n    subject_slice_accv,\n    label = \"AccV\",\n    color = 'steelblue'\n)\n# #to save some calculation resources\n# #you can always chage this value\n# plt.xlim(0,3000)\nplt.xlabel('Datetime', fontsize = 14)\nplt.ylabel('Accelerations in g',fontsize = 14)\nplt.plot(\n    subject_slice_accv.rolling(window = delta).mean(),\n    label = \"Rolling mean in 100 seconds\",\n    color = 'orange'\n)\nplt.legend(title = '', loc = 'upper right', fontsize = 14)\nplt.ylim(0,-2); #scaling the plot","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:33:16.920766Z","iopub.execute_input":"2023-06-05T09:33:16.921192Z","iopub.status.idle":"2023-06-05T09:33:17.752739Z","shell.execute_reply.started":"2023-06-05T09:33:16.921158Z","shell.execute_reply":"2023-06-05T09:33:17.751355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        So rolling mean give us some more useful view on our series, but there a scale problem: this plot is too small to understand\n        <br>\n        Also the window size gives a great impact to plotting results, so this value should be tuned with accuracy\n        <br>\n        <u>Note:</u> rolling mean cutting all the peaks in series\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# Decomposition","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        <b><u>Decomposition</u></b> - simpliest method to get the main patterns in time series\n</div>","metadata":{}},{"cell_type":"code","source":"#decomposition importing\nfrom statsmodels.tsa.seasonal import seasonal_decompose\nfrom pylab import rcParams\n\n#figsize of results\nrcParams['figure.figsize'] = 16, 12\ndecompose = seasonal_decompose(subject_slice_accv,period = 60*60*24) \n\n#make some color magic\nfig, axes = plt.subplots(4, 1, constrained_layout = True)\nfig.suptitle('AccV Decomposition')\ndecompose.observed.plot(ax=axes[0], legend=False, color='r')\naxes[0].set_ylabel('Observed AccV')\ndecompose.trend.plot(ax=axes[1], legend=False, color='g')\naxes[1].set_ylabel('Trend')\ndecompose.seasonal.plot(ax=axes[2], legend=False)\naxes[2].set_ylabel('Seasonal')\ndecompose.resid.plot(ax=axes[3], legend=False, color='k')\naxes[3].set_ylabel('Residual');\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-06-05T09:32:21.390743Z","iopub.execute_input":"2023-06-05T09:32:21.391049Z","iopub.status.idle":"2023-06-05T09:32:41.890810Z","shell.execute_reply.started":"2023-06-05T09:32:21.391023Z","shell.execute_reply":"2023-06-05T09:32:41.889491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        <u>Here we can see nexts results of decompostion:</u>\n        <ul>\n            <li><b>Observer</b> - original time series</li>\n            <li><b>Trend</b> - long-term change in series's level</li>\n            <li><b>Seasonal</b> - cyclical changes in the level of a series with a constant period</li>\n            <li><b>Residual</b> - unpredictable random change of the series</li>\n    </ul>\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"# Stumpy methods","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n     STUMPY is a powerful and scalable Python library that efficiently computes something called the matrix profile - <br>\n        a vector that stores the z-normalized Euclidean distance between any subsequence within a time series and its nearest neighbor.\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        <u>The most interesting possibilities of <code>stumpy</code> for FoG-events prediction are</u>:\n            <ol>\n                <li>Minimus of series</li>\n                <li>Shapelet Discovery</li>\n                <li>Semantic Segmentation</li>\n                <li>Chains Calculation</li>\n    </ol>\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#preparing\nsubject_slice_stumpy = subject_slice_accv[:round(len(subject_one_accv)/2000)] # creating another slice to save calculation time","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:34:26.264892Z","iopub.execute_input":"2023-06-05T09:34:26.265321Z","iopub.status.idle":"2023-06-05T09:34:26.271883Z","shell.execute_reply.started":"2023-06-05T09:34:26.265290Z","shell.execute_reply":"2023-06-05T09:34:26.270480Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#setting start parameters\nm = 400 #window size, tune it for experiments\nmp = stumpy.stump(subject_slice_stumpy.squeeze(), m)\nmp_df = pd.DataFrame(mp, columns=['profile', 'profile index', 'left profile index', 'right profile index'])","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:34:27.722100Z","iopub.execute_input":"2023-06-05T09:34:27.722485Z","iopub.status.idle":"2023-06-05T09:34:51.321740Z","shell.execute_reply.started":"2023-06-05T09:34:27.722456Z","shell.execute_reply":"2023-06-05T09:34:51.320550Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#check\nmp_df","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:35:01.818738Z","iopub.execute_input":"2023-06-05T09:35:01.819595Z","iopub.status.idle":"2023-06-05T09:35:01.834285Z","shell.execute_reply.started":"2023-06-05T09:35:01.819544Z","shell.execute_reply":"2023-06-05T09:35:01.833189Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Global minimums","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        We can do many interesting things with <code>stumpy</code>, first of all we will find <u>best_motif</u>:\n            <br>\n            it is the one where the time series is the smallest (or reach the minimum in other words)\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#finding best motif\nbest_motif = mp_df[mp_df['profile'] == mp_df['profile'].min()]\nbest_motif","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:35:08.011872Z","iopub.execute_input":"2023-06-05T09:35:08.012298Z","iopub.status.idle":"2023-06-05T09:35:08.034405Z","shell.execute_reply.started":"2023-06-05T09:35:08.012269Z","shell.execute_reply":"2023-06-05T09:35:08.033480Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#fixing value of rows to avoid future mistakes\n#the reason is alghoritm of stumpy, you can use links in the end of notebook to learn more\nsubject_slice_stumpy.copy().drop(subject_slice_stumpy.tail(3).index, inplace = True)","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:35:14.338543Z","iopub.execute_input":"2023-06-05T09:35:14.339111Z","iopub.status.idle":"2023-06-05T09:35:14.349791Z","shell.execute_reply.started":"2023-06-05T09:35:14.339065Z","shell.execute_reply":"2023-06-05T09:35:14.348597Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#plotting best motifs\nfig, ax = plt.subplots(2, figsize=(16,8))\ng1 = sns.lineplot(\n    y = subject_slice_stumpy['AccV'].squeeze(),\n    x = subject_slice_stumpy.reset_index(drop = True).index,\n    ax = ax[0])\ng2 = sns.lineplot(\n    data = mp_df['profile'],\n    color = 'violet',\n    ax = ax[1]);\nfor i in best_motif.index.to_list():\n#     g1.axvline(x = i, color=\"green\")\n#     g2.axvline(x = i, color=\"green\")\n    rect = Rectangle((i, -4), 200, 400, facecolor = \"lightgrey\")\n    g1.add_patch(rect)","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:37:12.752933Z","iopub.execute_input":"2023-06-05T09:37:12.753324Z","iopub.status.idle":"2023-06-05T09:37:13.869024Z","shell.execute_reply.started":"2023-06-05T09:37:12.753295Z","shell.execute_reply":"2023-06-05T09:37:13.867765Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        All this gray rectangles are global minimums in our series, possibly it's 'calm' values of accelerometer or another valuable feature\n        <br>\n        My suggestions is that window size can make this picture cleaner\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"## Semantic segmentation","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        The semantic segmentation goal is find a <code>k</code> areas in whole time series\n        <br>\n       Let's take a look\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#setting the params\nL = m\nregimes = 4\ncac, regime_locations = stumpy.fluss(mp[:, 1], L = L, n_regimes = regimes, excl_factor = 1)","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:37:53.180361Z","iopub.execute_input":"2023-06-05T09:37:53.180839Z","iopub.status.idle":"2023-06-05T09:37:55.942234Z","shell.execute_reply.started":"2023-06-05T09:37:53.180808Z","shell.execute_reply":"2023-06-05T09:37:55.940813Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#creating a fig and axes\nfig, ax = plt.subplots(3, figsize=(16,8), sharex=True)\n\nsns.lineplot(y = subject_slice_stumpy['AccV'],\n             x = subject_slice_stumpy.reset_index(drop = True).index,\n             ax = ax[0])\nsns.lineplot(data = mp_df['profile'],\n             ax = ax[1])\nsns.lineplot(data = cac,\n             ax = ax[2])\n\nfor i in regime_locations:\n    for adx in ax:\n        adx.axvline(x = i, color='C1')","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:41:16.034251Z","iopub.execute_input":"2023-06-05T09:41:16.034702Z","iopub.status.idle":"2023-06-05T09:41:17.682100Z","shell.execute_reply.started":"2023-06-05T09:41:16.034668Z","shell.execute_reply":"2023-06-05T09:41:17.680791Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        The result of this method is creating 4 areas (as <code>regimes</code> equals 4) in time series.\n        <br>\n        As we can see here a border between our regimes are too narrow and to use this method we should take a smaller time series part\n        <br>\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"## Chains","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        <b>Time series chains</b> are patterns that evolve and drift over time\n    </p>\n</div>","metadata":{}},{"cell_type":"code","source":"#using allc method to create chains\nall_chain_set, unanchored_chain = stumpy.allc(mp[:, 2], mp[:, 3])","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:39:21.175131Z","iopub.execute_input":"2023-06-05T09:39:21.175598Z","iopub.status.idle":"2023-06-05T09:39:21.307161Z","shell.execute_reply.started":"2023-06-05T09:39:21.175540Z","shell.execute_reply":"2023-06-05T09:39:21.305763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#plotting\nfig, ax = plt.subplots(figsize=(16,8), sharex=True)\n\nsns.lineplot(y = subject_slice_stumpy['AccV'],\n             x = subject_slice_stumpy.reset_index(drop = True).index\n            )\nfor i in unanchored_chain:\n    rect = Rectangle((i, -4), 200, 20, facecolor=\"lightgreen\")\n    ax.add_patch(rect)\nax.legend(['AccV', 'Chains']);","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:40:23.062708Z","iopub.execute_input":"2023-06-05T09:40:23.063208Z","iopub.status.idle":"2023-06-05T09:40:23.763933Z","shell.execute_reply.started":"2023-06-05T09:40:23.063169Z","shell.execute_reply":"2023-06-05T09:40:23.762724Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n        Heree we see several parts of time series\n    </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"## Shapelet ","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\n<u>Shapelets</u> are subsequences that can be used to represent a class. Matrix profiles make it possible to identify these shapelets\n    </p>","metadata":{}},{"cell_type":"code","source":"wavelets = stumpy.stump(subject_slice_stumpy['AccV'], m)[:, 0].astype(float)\nfig, ax = plt.subplots(figsize=(24, 8))\nsns.lineplot(data = wavelets, \n             legend = True,\n             ax = ax);\nsns.lineplot(y = subject_slice_stumpy['AccV'],\n             x = subject_slice_stumpy.reset_index(drop = True).index,\n             legend = True,\n             ax = ax);\n#ax.set_ylim(-1,0.5)\nax.legend(['Matrix profile', 'Raw series']);","metadata":{"execution":{"iopub.status.busy":"2023-06-05T09:41:40.723188Z","iopub.execute_input":"2023-06-05T09:41:40.723647Z","iopub.status.idle":"2023-06-05T09:41:45.532068Z","shell.execute_reply.started":"2023-06-05T09:41:40.723612Z","shell.execute_reply":"2023-06-05T09:41:45.530639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\nShapelets searching is most difficult to me and this is only demonstration of possibilities of stumpy.\n<br>\n        We can go further and build a Shapelet Based Model to find a classes in time series(e.g. FoG-events)\n    </p>","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\nThere still work to do and now extraction of valuavle information is almost impossible.\n        <br>\n        However, matrix profile from <code>stumpy</code> can be illustrative and even now we can see some patterns of events \n    </p>","metadata":{}},{"cell_type":"markdown","source":"# Conclusion","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    <p>\nI gave a short description to several tools of time series analysis:\n        <ol>\n            <li>Rolling window method</li>\n            <li>Decomposition to parts</li>\n            <li>Stumpy methods</li>\n        <br>\n            <u>The stumpy methods are most perspective </u> in the task of FoG-events prediction and now I highlight them:\n            <ol>\n                <li>Shapelet discovery method can help to find all of the types of events in time series</li>\n                <li>Motif(pattern) method may help to filter low-amplitude part of accelerometer signal </li>\n                <li>Semantic segmentation method could be used to determine time series piece with all of event-types</li>\n                <li>Chains method will find a chain with similar events in time series</li>\n                </ol>\n            <br>\n            There are many others useful tools to analyse time series with FoG-events in other datafames\n    </p>","metadata":{}},{"cell_type":"markdown","source":"# Links and info","metadata":{}},{"cell_type":"markdown","source":"<div style=\"font-family:verdana; word-spacing:2px;\">\n    \n-[stumpy library and docs](https://stumpy.readthedocs.io/en/latest/)\n    \n-[stumpy basics course](https://towardsdatascience.com/stumpy-basics-21844a2d2d92)\n     \n-[stumpy noteebook](https://www.kaggle.com/code/nabeelvalley/time-series-analysis-with-stumpy)\n     \n","metadata":{}}]}