{"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":"# Notebook for Data Preprocessing","metadata":{}},{"cell_type":"markdown","source":"In this notebook we will discuss the leverage of path signature method with ensemble machine learning on anomaly detection with an example of kaggle's [Large-scale Energy Anomaly Detection Competition](https://www.kaggle.com/competitions/energy-anomaly-detection/overview) . Signature method has shown to be effective in extract patterns in data and thus improve the predictive power of the model.\n\n#### Section 1: **Exploratory analysis and data cleaning**\nPrimary observation in the patterns of data is made. Static features with high correlations are filtered. For each meter reading $x_t$, we compute the lagged differences $x_{t\\pm1}$, $x_{t\\pm24}$, $x_{t\\pm168}$ (1 hour, 1 day, and 1 week respectively) from the point $x_t$ with $t$ in hours.\n\n#### Section 2: **Computation of signature features**\nNext, we associate and concatenate lagged readings into a multi-dimensional path to each point $x$, by using a rolling window over the sequential data for each building. For each point, signatures features are then computed with these paths and combined with other original features for model training.","metadata":{}},{"cell_type":"markdown","source":"First of all, install the iisignature package and import relevant modules","metadata":{}},{"cell_type":"code","source":"pip install iisignature","metadata":{"execution":{"iopub.status.busy":"2022-11-16T14:58:58.980062Z","iopub.execute_input":"2022-11-16T14:58:58.980574Z","iopub.status.idle":"2022-11-16T14:59:46.672896Z","shell.execute_reply.started":"2022-11-16T14:58:58.980452Z","shell.execute_reply":"2022-11-16T14:59:46.671489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sklearn\nimport numpy as np\nimport pandas as pd\nfrom xgboost import XGBClassifier\nimport lightgbm as lgb\nfrom tqdm import tqdm, trange\nimport seaborn as sns\nimport datetime\nimport warnings\nwarnings.filterwarnings('ignore')\nfrom joblib import dump, load\nfrom sklearn import metrics\nfrom sklearn.preprocessing import StandardScaler\n\n# set seed for all random processes in this notebook\nr_s = 5","metadata":{"execution":{"iopub.status.busy":"2022-11-16T14:59:46.676600Z","iopub.execute_input":"2022-11-16T14:59:46.677024Z","iopub.status.idle":"2022-11-16T14:59:48.676142Z","shell.execute_reply.started":"2022-11-16T14:59:46.676976Z","shell.execute_reply":"2022-11-16T14:59:48.674858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Exploratory analysis and data cleaning","metadata":{}},{"cell_type":"markdown","source":"Thanks to [this notebook](https://www.kaggle.com/code/patrick0302/2-anomaly-detection-features-csv-revised-ensem) by Chun Fu, who has done an amazing job in data exploratory and data cleaning. In this section, we will use his results and improve on it.","metadata":{}},{"cell_type":"code","source":"train = pd.read_pickle('../input/largescale-energy-anomaly/train_features.pickle')\ntest = pd.read_pickle('../input/largescale-energy-anomaly/test_features.pickle')","metadata":{"execution":{"iopub.status.busy":"2022-11-16T14:59:48.677798Z","iopub.execute_input":"2022-11-16T14:59:48.678202Z","iopub.status.idle":"2022-11-16T15:00:40.286137Z","shell.execute_reply.started":"2022-11-16T14:59:48.678166Z","shell.execute_reply":"2022-11-16T15:00:40.284859Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['dayofyear'] = pd.to_datetime(train['timestamp']).dt.dayofyear+pd.to_datetime(train['timestamp']).dt.hour/24\ntest['dayofyear'] = pd.to_datetime(test['timestamp']).dt.dayofyear+pd.to_datetime(test['timestamp']).dt.hour/24","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:00:40.289643Z","iopub.execute_input":"2022-11-16T15:00:40.290161Z","iopub.status.idle":"2022-11-16T15:00:42.100644Z","shell.execute_reply.started":"2022-11-16T15:00:40.290113Z","shell.execute_reply":"2022-11-16T15:00:42.099105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"features = list(train.drop(['wind_direction','air_temperature_std_lag73','anomaly','lag_value_-168', \n                            'lag_value_-24','lag_value_-1','lag_value_1','lag_value_24','lag_value_168'],axis=1).select_dtypes(include=['float','int']).columns)","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:00:42.102284Z","iopub.execute_input":"2022-11-16T15:00:42.102727Z","iopub.status.idle":"2022-11-16T15:00:46.442055Z","shell.execute_reply.started":"2022-11-16T15:00:42.102691Z","shell.execute_reply":"2022-11-16T15:00:46.440680Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test.index.names=['row_id']","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:00:46.443728Z","iopub.execute_input":"2022-11-16T15:00:46.444110Z","iopub.status.idle":"2022-11-16T15:00:46.449429Z","shell.execute_reply.started":"2022-11-16T15:00:46.444073Z","shell.execute_reply":"2022-11-16T15:00:46.448331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Add in the attribute of missing readings:","metadata":{}},{"cell_type":"code","source":"df_train_f_original = pd.read_csv('../input/largescale-energy-anomaly/train_features.csv')\ndf_test_f_original  = pd.read_csv('../input/largescale-energy-anomaly/test_features.csv')","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:00:46.450806Z","iopub.execute_input":"2022-11-16T15:00:46.451351Z","iopub.status.idle":"2022-11-16T15:01:33.468168Z","shell.execute_reply.started":"2022-11-16T15:00:46.451316Z","shell.execute_reply":"2022-11-16T15:01:33.466783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['missing'] = df_train_f_original.meter_reading\ntrain['missing'] = train['missing'].apply(lambda x: 1 if np.isnan(x) else 0)\n\ntest['missing'] = df_test_f_original.meter_reading\ntest['missing'] = test['missing'].apply(lambda x: 1 if np.isnan(x) else 0)\n\ndef cum_miss_func(df_f):\n    temp_df = []\n    for _id, _df in df_f.groupby('building_id')[['meter_reading', 'missing']]:\n        _df.loc[:, 'missing'] = np.cumsum(_df.missing)\n        temp_df.append(_df)\n    df = pd.concat(temp_df)\n    df_f['cum_missing'] = df['missing']\n    del temp_df, df, _id, _df\n    return df_f\n\ntrain = cum_miss_func(train)\ntest = cum_miss_func(test)\ntest.reset_index(inplace=True)","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:01:33.470086Z","iopub.execute_input":"2022-11-16T15:01:33.470595Z","iopub.status.idle":"2022-11-16T15:02:07.189845Z","shell.execute_reply.started":"2022-11-16T15:01:33.470543Z","shell.execute_reply":"2022-11-16T15:02:07.188517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train[train.missing == 1]['anomaly'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:02:07.192052Z","iopub.execute_input":"2022-11-16T15:02:07.192672Z","iopub.status.idle":"2022-11-16T15:02:07.531136Z","shell.execute_reply.started":"2022-11-16T15:02:07.192626Z","shell.execute_reply":"2022-11-16T15:02:07.529603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del df_train_f_original, df_test_f_original","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:02:07.532848Z","iopub.execute_input":"2022-11-16T15:02:07.533208Z","iopub.status.idle":"2022-11-16T15:02:07.733133Z","shell.execute_reply.started":"2022-11-16T15:02:07.533177Z","shell.execute_reply":"2022-11-16T15:02:07.731666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We notice that all points with missing meter_reading values are not anomalies. Therefore, to increase the effectiveness and robustness of the model, at the stage of prediction, we can simply assign value 0 under the column 'anomaly'(0 for anomaly and 1 for normal point) for all missing values.","metadata":{}},{"cell_type":"code","source":"train = train.rename_axis('index').sort_values(by=['building_id', 'index']).reset_index(drop=True)\ntest = test.rename_axis('index').sort_values(by=['building_id', 'index']).reset_index(drop=True)","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:02:07.738624Z","iopub.execute_input":"2022-11-16T15:02:07.739224Z","iopub.status.idle":"2022-11-16T15:02:20.944443Z","shell.execute_reply.started":"2022-11-16T15:02:07.739177Z","shell.execute_reply":"2022-11-16T15:02:20.943396Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Noticing there are some time when the entire row of data are missing, we fill in these rows with NaN. This is for later computing signature paths using window sliding.","metadata":{}},{"cell_type":"code","source":"def fill_empty_time(df, set_phase):\n    df_original = pd.read_csv('../input/largescale-energy-anomaly/'+set_phase+'_features.csv')\n    df_original = df_original.rename_axis('index').sort_values(by=['building_id', 'index']).reset_index(drop=True)\n    df['meter_reading'] = df_original['meter_reading']\n    df['timestamp'] = df_original['timestamp']\n\n    temp_df = []\n    for _, df__ in df.groupby('building_id'):\n        df__.index = pd.DatetimeIndex(df__.timestamp)\n        df__ = df__.reindex(pd.date_range('00:00:00 01-01-2016', '23:00:00 12-31-2016', freq='1H'))\n        df__['building_id'] = df__['building_id'].fillna(method='ffill').fillna(method='bfill')\n        temp_df.append(df__)\n    df = pd.concat(temp_df)\n    del df_original, temp_df\n    return df","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:02:20.946136Z","iopub.execute_input":"2022-11-16T15:02:20.946883Z","iopub.status.idle":"2022-11-16T15:02:20.957170Z","shell.execute_reply.started":"2022-11-16T15:02:20.946837Z","shell.execute_reply":"2022-11-16T15:02:20.955754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = fill_empty_time(train, 'train')\ntest = fill_empty_time(test, 'test')","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:02:20.959165Z","iopub.execute_input":"2022-11-16T15:02:20.959732Z","iopub.status.idle":"2022-11-16T15:03:12.656983Z","shell.execute_reply.started":"2022-11-16T15:02:20.959686Z","shell.execute_reply":"2022-11-16T15:03:12.655547Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.to_pickle('train_win_with_miss.pkl.gz', compression='gzip')\ntest.to_pickle('test_win_with_miss.pkl.gz', compression='gzip')","metadata":{"execution":{"iopub.status.busy":"2022-11-16T00:38:16.072453Z","iopub.execute_input":"2022-11-16T00:38:16.073031Z","iopub.status.idle":"2022-11-16T00:59:19.511003Z","shell.execute_reply.started":"2022-11-16T00:38:16.072979Z","shell.execute_reply":"2022-11-16T00:59:19.509128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Computation of signature features","metadata":{}},{"cell_type":"markdown","source":"The general idea of signature features is that these features are purely from exploiting the information in the meter_reading data. We will concatenate these features to other available features such as 'building_id', to form the complete training input.","metadata":{}},{"cell_type":"markdown","source":"Useful functions about computing signatures are adapted from [this notebook](https://github.com/datasig-ac-uk/Kaggle22/blob/main/GradientBoostingNotebook.ipynb). \nWe first form one dimensional signature paths for each point at time $t$, which are streams of meter_reading data from 1W, 1D, and 1 H before time $t$ untill $t$, and from $t$ to 1H, 1D, 1W after time $t$.  i.e.\n\n$X_{[t-168,t]}= (x_{t-168}, x_{t-167}, x_{t-166},..., x_{t})$\n\n$X_{[t-24,t]} = (x_{t-24}, x_{t-23}, x_{t-22},..., x_{t})$\n\n$X_{[t-1,t]} = (x_{t-1}, x_{t})$\n\n$X_{[t,t+1]} = (x_{t}, x_{t+1})$\n\n$X_{[t,t+24]} = (x_{t}, x_{t+1}, x_{t+2},..., x_{t+24})$\n\n$X_{[t,t+168]} = (x_{t}, x_{t+1}, x_{t+2},..., x_{t+168})$","metadata":{}},{"cell_type":"markdown","source":"Then, before computing the sets of signatures for each signature path and each data point, we define the necessary parameters which can be further tuned. \nFor now, let us set the signature order $s=1$ and use time transformation to add another dimension to the signature paths.","metadata":{}},{"cell_type":"code","source":"sig_order = 2\nlead_lag_para = False\ntime_para = True\nreset_para = False\nhyper = [sig_order, lead_lag_para, time_para, reset_para]\nwindow_list = [-168, -24, -1, 1, 24, 168]","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:03:12.658606Z","iopub.execute_input":"2022-11-16T15:03:12.658978Z","iopub.status.idle":"2022-11-16T15:03:12.666639Z","shell.execute_reply.started":"2022-11-16T15:03:12.658935Z","shell.execute_reply":"2022-11-16T15:03:12.665247Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The function 'obtain_model_inputs' will perform the following tasks:\n1. form signature paths $X_{[t-168,t]}$,...,$X_{[t,t+168]}$\n2. compute the sets of signatures $S_{[t-168,t]}$,...,$S_{[t,t+168]}$ respectively for each point\n3. concatenate computed signature features to the following features from Chun Fu's notebook (all except lagged readings)","metadata":{}},{"cell_type":"code","source":"# # features from Chun Fu's notebook\n# features = [ 'meter_reading',\n#              'building_id',\n#              'site_id',\n#              'square_feet',\n#              'year_built',\n#              'floor_count',\n#              'air_temperature',\n#              'cloud_coverage',\n#              'dew_temperature',\n#              'precip_depth_1_hr',\n#              'sea_level_pressure',\n#              'wind_direction',\n#              'wind_speed',\n#              'air_temperature_std_lag7',\n#              'air_temperature_std_lag73',\n#              'hour',\n#              'weekday',\n#              'month',\n#              'hour_x',\n#              'hour_y',\n#              'month_x',\n#              'month_y',\n#              'weekday_x',\n#              'weekday_y',\n#              'is_holiday',\n#              'gte_hour',\n#              'gte_weekday',\n#              'gte_month',\n#              'gte_building_id',\n#              'gte_primary_use',\n#              'gte_site_id',\n# #              'lag_value_-168', \n# #              'lag_value_-24', \n# # #             'lag_value_-1', \n# #             'lag_value_1', \n# #             'lag_value_24', \n# #              'lag_value_168'\n#            ]","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:03:12.668522Z","iopub.execute_input":"2022-11-16T15:03:12.669853Z","iopub.status.idle":"2022-11-16T15:03:12.680343Z","shell.execute_reply.started":"2022-11-16T15:03:12.669799Z","shell.execute_reply":"2022-11-16T15:03:12.679195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# function to process the signature paths to signature features and concatenate them to other original features\ndef obtain_model_inputs(df, phase, window_list, features, hyper):\n    import iisignature\n    import itertools\n    from tqdm import tqdm\n    from joblib import Memory, Parallel, delayed\n    import shutil\n\n    def get_stream(raw_stream, include_time=False, lead_lag_transform=False, invisibility_transform=False):\n        stream = raw_stream\n\n        if include_time:\n            stream = np.column_stack((stream, np.arange(stream.shape[0])))\n\n        if lead_lag_transform:\n            stream = np.repeat(stream, 2, axis=0)\n            stream = np.column_stack((stream[1:, :], stream[:-1, :]))\n\n        if invisibility_transform:\n            stream = np.vstack(((stream, stream[-1], np.zeros_like(stream[-1]))))\n            stream = np.column_stack((stream, np.append(np.ones(stream.shape[0]-2), [0, 0])))\n\n        return stream\n\n    def compute_stream_signature(inlier_test, outlier_test, signature_order, random_state=r_s):    \n        sigs_inliers = np.array(compute_sig(inlier_test, signature_order, groupby=['building_id', 'timestamp'], \n                                                               cache_dir='cache_dir'))\n\n        if outlier_test is not None:\n            sigs_outliers = np.array(compute_sig(outlier_test, signature_order, groupby=['building_id', 'timestamp'], \n                                                                cache_dir='cache_dir'))\n            return sigs_inliers, sigs_outliers\n        else:\n            return sigs_inliers\n\n    def compute_sig(paths, order, cache_dir=None, groupby=None, enable_progress=True, **parallel_kwargs):\n        sigs = Memory(cache_dir, verbose=0).cache(_prepare_sig)(paths, order,\n                                                                   enable_progress,\n                                                                   **parallel_kwargs)\n        return sigs\n\n    def _prepare_sig(paths, order, enable_progress=True, **parallel_kwargs):\n        sigs = np.array(Parallel(**parallel_kwargs)(delayed(_sig)(p, order)\n                                                    for p in tqdm(paths,\n                                                                  disable=not enable_progress,\n                                                                  desc=\"Computing signatures\")))\n        return sigs\n\n    def _sig(p, order, I=None):\n        if I is None:\n            L = len(p)\n            I = [0, L]\n        return np.r_[1., iisignature.sig(p[I[0] : I[1]], order)]\n\n\n    def path_generation(df, window):\n        if window > 0:\n            window += 1\n            rolling_df1 = [w.values.tolist() for w in df.groupby('building_id')[['meter_reading']].rolling(window)] \n            rolling_df1 = [np.array(w) if len(w)==window else np.array((window-len(w))*[[np.nan]]+w) for w in rolling_df1]\n            df['Stream'] = rolling_df1\n            del rolling_df1  \n        else:\n            window = -1*window+1\n            rolling_df2 = [w.values.tolist() for w in df[::-1].groupby('building_id')[['meter_reading']].rolling(window)] \n            rolling_df2 = [np.array(w[::-1]) if len(w)==window else np.array(((window-len(w))*[[np.nan]]+w)[::-1]) for w in rolling_df2]\n            df['Stream'] = rolling_df2\n            del rolling_df2\n\n            final_df = []\n            for _, df_ in df.groupby(['building_id']):\n                df_['Stream'] = np.array(df_['Stream'].values[::-1])\n                final_df.append(df_)\n            df = pd.concat(final_df)\n            del final_df\n        return df\n\n    stream_function = lambda raw_stream: get_stream(raw_stream, invisibility_transform=hyper[3],\n                                                    include_time=hyper[2],\n                                                    lead_lag_transform=hyper[1])\n\n    sig_list = []\n    buildings = df.building_id.unique()\n    part = 10\n    build_number = len(buildings)//part\n    for window in window_list:\n        print(window)\n        if abs(window) > 50:\n            train_list = [df[df.building_id.isin(buildings[build_number*i: build_number*(i+1)])] for i in range(part-1)] + [df[df.building_id.isin(buildings[build_number*(part-1):])]]\n\n        else:\n            train_list = [df]\n        sig_local = []\n        print('length of train_list', len(train_list))\n        for _df in train_list:\n            sig1 = path_generation(_df[['building_id', 'meter_reading']], window) \n            print('path generated')\n            sig1['Stream'] = sig1['Stream'].apply(stream_function)\n            print('path transformed')\n            sigs = compute_stream_signature(sig1['Stream'], None, hyper[0], random_state=r_s)\n            print('signature generated!')\n            if not len(sig_local):\n                sig_local = sigs[:, 1:-1]\n            else:\n                sig_local = np.append(sig_local, sigs[:, 1:-1], axis=0)\n        if not len(sig_list):\n            sig_list = sig_local\n        else:\n            sig_list = np.append(sig_list, sig_local, axis=1)\n        shutil.rmtree(\"./cache_dir\")\n    \n    if phase == 'train':\n        dframe = np.concatenate([np.array(np.array(sig_list)), df[features].values], axis=1)\n        dframe = pd.DataFrame(dframe, columns=['sig{}'.format(i) for i in range(sig_list.shape[1])]+features)\n        y_val = df.anomaly\n        y_val.reset_index(drop=True, inplace=True)\n        return dframe, y_val\n    else:\n        dframe = np.concatenate([np.array(np.array(sig_list)), df[features+['row_id']].values], axis=1)\n        dframe = pd.DataFrame(dframe, columns=['sig{}'.format(i) for i in range(sig_list.shape[1])]+features+['row_id'])\n        return dframe, None","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:03:12.682431Z","iopub.execute_input":"2022-11-16T15:03:12.683412Z","iopub.status.idle":"2022-11-16T15:03:12.722217Z","shell.execute_reply.started":"2022-11-16T15:03:12.683358Z","shell.execute_reply":"2022-11-16T15:03:12.720794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x_val_whole, y_val_whole = obtain_model_inputs(train, 'train', window_list, features, hyper)\nwhole_set = pd.concat([x_val_whole, y_val_whole], axis=1)\nwhole_set = whole_set[~np.isnan(whole_set.anomaly)]\nwhole_set.reset_index(drop=True, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2022-11-16T15:03:12.723653Z","iopub.execute_input":"2022-11-16T15:03:12.724067Z","iopub.status.idle":"2022-11-16T16:55:14.981501Z","shell.execute_reply.started":"2022-11-16T15:03:12.724013Z","shell.execute_reply":"2022-11-16T16:55:14.973810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"whole_set.to_pickle('whole_set_with_sig_'+str(sig_order)+'.pkl.gz', compression='gzip')","metadata":{"execution":{"iopub.status.busy":"2022-11-16T16:55:14.988224Z","iopub.execute_input":"2022-11-16T16:55:14.988700Z","iopub.status.idle":"2022-11-16T17:09:32.015332Z","shell.execute_reply.started":"2022-11-16T16:55:14.988661Z","shell.execute_reply":"2022-11-16T17:09:32.013846Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Similar procedures to the training set is conducted on the prediction set.","metadata":{}},{"cell_type":"code","source":"x_predict = obtain_model_inputs(test, 'predict', window_list, features, hyper)[0]\nx_predict = x_predict[~np.isnan(x_predict.row_id)]\nx_predict.sort_values(by='row_id', inplace=True)\nx_predict.drop(columns='row_id', inplace=True)\nx_predict.reset_index(drop=True, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2022-11-16T17:09:32.017527Z","iopub.execute_input":"2022-11-16T17:09:32.018909Z","iopub.status.idle":"2022-11-16T19:08:37.897603Z","shell.execute_reply.started":"2022-11-16T17:09:32.018856Z","shell.execute_reply":"2022-11-16T19:08:37.891118Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x_predict.to_pickle('x_predict_with_sigs_'+str(sig_order)+'.pkl.gz', compression='gzip')","metadata":{"execution":{"iopub.status.busy":"2022-11-16T19:08:37.900111Z","iopub.execute_input":"2022-11-16T19:08:37.900717Z","iopub.status.idle":"2022-11-16T19:26:31.585982Z","shell.execute_reply.started":"2022-11-16T19:08:37.900678Z","shell.execute_reply":"2022-11-16T19:26:31.584347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}