{"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":"# *Standard Libraries*","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:00:14.146246Z","iopub.execute_input":"2021-12-30T20:00:14.146574Z","iopub.status.idle":"2021-12-30T20:00:14.151266Z","shell.execute_reply.started":"2021-12-30T20:00:14.146542Z","shell.execute_reply":"2021-12-30T20:00:14.150311Z"}}},{"cell_type":"code","source":"import datetime\nimport numpy as np\nimport pandas as pd\nimport datatable as dt\npd.set_option(\"display.max_columns\", None, \"display.max_rows\", None)\n\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport matplotlib.dates as mdates","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-12-30T20:45:48.976046Z","iopub.execute_input":"2021-12-30T20:45:48.976659Z","iopub.status.idle":"2021-12-30T20:45:49.997671Z","shell.execute_reply.started":"2021-12-30T20:45:48.976512Z","shell.execute_reply":"2021-12-30T20:45:49.996797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\ntrain = dt.fread('../input/g-research-crypto-forecasting/train.csv').to_pandas()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:45:49.999382Z","iopub.execute_input":"2021-12-30T20:45:49.999654Z","iopub.status.idle":"2021-12-30T20:46:28.101547Z","shell.execute_reply.started":"2021-12-30T20:45:49.999592Z","shell.execute_reply":"2021-12-30T20:46:28.098974Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train.set_index('timestamp')\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:46:28.104758Z","iopub.execute_input":"2021-12-30T20:46:28.105056Z","iopub.status.idle":"2021-12-30T20:46:34.871431Z","shell.execute_reply.started":"2021-12-30T20:46:28.105018Z","shell.execute_reply":"2021-12-30T20:46:34.870654Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in range(14):\n    print(i, len(train[train.Asset_ID == i])%60)","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:46:34.874061Z","iopub.execute_input":"2021-12-30T20:46:34.875145Z","iopub.status.idle":"2021-12-30T20:46:38.456448Z","shell.execute_reply.started":"2021-12-30T20:46:34.875094Z","shell.execute_reply":"2021-12-30T20:46:38.455452Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train.sort_index()\nind = train.index.unique()\ndef reindex(df):\n    df = df.reindex(range(ind[0],ind[-1]+60,60),method='nearest')\n    # fill null values with zeros for visualization\n    df = df.fillna(0).fillna(0)\n    return df\ntrain = train.groupby('Asset_ID').apply(reindex).reset_index(0, drop=True).sort_index()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:46:38.457740Z","iopub.execute_input":"2021-12-30T20:46:38.457964Z","iopub.status.idle":"2021-12-30T20:47:01.927428Z","shell.execute_reply.started":"2021-12-30T20:46:38.457938Z","shell.execute_reply":"2021-12-30T20:47:01.926499Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ensure dataframe is well padded\nfor i in range(14):\n    print(i, len(train[train.Asset_ID == i])%60)","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:47:01.928948Z","iopub.execute_input":"2021-12-30T20:47:01.929313Z","iopub.status.idle":"2021-12-30T20:47:06.042679Z","shell.execute_reply.started":"2021-12-30T20:47:01.929262Z","shell.execute_reply":"2021-12-30T20:47:06.041763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ensure there is no null values in the dataframe\ntrain.isnull().sum()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:47:06.044008Z","iopub.execute_input":"2021-12-30T20:47:06.044334Z","iopub.status.idle":"2021-12-30T20:47:06.558582Z","shell.execute_reply.started":"2021-12-30T20:47:06.044295Z","shell.execute_reply":"2021-12-30T20:47:06.557748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train.reset_index()\n# sort dataframe by timestamp then by asset id for consistency\ntrain = train.sort_values(by=['timestamp', 'Asset_ID'])\n# convert date to datetime to enable extracting more useful info\ntrain['date'] = pd.to_datetime(train.timestamp, unit='s')\ntrain.drop(['timestamp'], axis=1, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:47:06.559977Z","iopub.execute_input":"2021-12-30T20:47:06.560299Z","iopub.status.idle":"2021-12-30T20:47:21.622680Z","shell.execute_reply.started":"2021-12-30T20:47:06.560261Z","shell.execute_reply":"2021-12-30T20:47:21.621914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# extract useful features from the datetime feature\ntrain['min_'] = pd.DatetimeIndex(train['date']).minute.astype('uint8')\ntrain['hr_'] = pd.DatetimeIndex(train['date']).hour.astype('uint8')\ntrain['day_'] = pd.DatetimeIndex(train['date']).day.astype('uint8')\ntrain['day_in_week'] = pd.DatetimeIndex(train['date']).day_of_week.astype('uint8')\ntrain['week'] = pd.DatetimeIndex(train['date']).week.astype('uint8')\ntrain['month'] = pd.DatetimeIndex(train['date']).month.astype('uint8')\ntrain['is_month_start'] = pd.DatetimeIndex(train['date']).is_month_start.astype(int).astype('uint8')\ntrain['is_month_end'] = pd.DatetimeIndex(train['date']).is_month_end.astype(int).astype('uint8')\ntrain['is_quarter_start'] = pd.DatetimeIndex(train['date']).is_quarter_start.astype(int).astype('uint8')\ntrain['is_quarter_end'] = pd.DatetimeIndex(train['date']).is_quarter_end.astype(int).astype('uint8')\ntrain['is_year_start'] = pd.DatetimeIndex(train['date']).is_year_start.astype(int).astype('uint8')\ntrain['is_year_end'] = pd.DatetimeIndex(train['date']).is_year_end.astype(int).astype('uint8')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:47:21.624207Z","iopub.execute_input":"2021-12-30T20:47:21.624539Z","iopub.status.idle":"2021-12-30T20:48:03.138601Z","shell.execute_reply.started":"2021-12-30T20:47:21.624501Z","shell.execute_reply":"2021-12-30T20:48:03.137681Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# convert float columns to float64 to reduce memory usage\nfloat_cols = []\nfor col in train.columns.tolist():\n    if train[col].dtypes == 'float64':\n        float_cols.append(col)\n    \ntrain[float_cols] = train[float_cols].astype('float16')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:03.141503Z","iopub.execute_input":"2021-12-30T20:48:03.141773Z","iopub.status.idle":"2021-12-30T20:48:09.905104Z","shell.execute_reply.started":"2021-12-30T20:48:03.141743Z","shell.execute_reply":"2021-12-30T20:48:09.903991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# *EDA*","metadata":{}},{"cell_type":"markdown","source":"#### *In this section will display only the data of Bitcoin (Asset_ID = 1) for the sake of simplicity. You can also explore other assets.*","metadata":{}},{"cell_type":"code","source":"# select only date from 2021 for computational simplicity\ndf = train[train['date'] >= datetime.datetime(2021, 1, 1)].set_index('date', drop=True)\nprint(df.shape)","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:09.906392Z","iopub.execute_input":"2021-12-30T20:48:09.906670Z","iopub.status.idle":"2021-12-30T20:48:11.546857Z","shell.execute_reply.started":"2021-12-30T20:48:09.906638Z","shell.execute_reply":"2021-12-30T20:48:11.546035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from the line plot we can extract info on trends and seasonality\ndf[df['Asset_ID'] == 1][['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target']].plot(subplots=True, figsize=(15, 25))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:11.548041Z","iopub.execute_input":"2021-12-30T20:48:11.548290Z","iopub.status.idle":"2021-12-30T20:48:30.947229Z","shell.execute_reply.started":"2021-12-30T20:48:11.548265Z","shell.execute_reply":"2021-12-30T20:48:30.946597Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### *We can see how the Target and the features are extremely noisy and hence we will try to smoothen these curves using some techniques. This might enable us to extract valuable information from our data*","metadata":{}},{"cell_type":"code","source":"# first method to smoothen the curves is by resampling (aggregating) data over a wider period of time and taking the mean of these values\ndf_month = df[df['Asset_ID'] == 1].resample(\"M\").mean()\n\nfig, axes = plt.subplots(8, 1, figsize=(15, 25), sharex=True)\nfor name, ax in zip(['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target'], axes):\n    ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))\n    ax.bar(df_month.index, df_month[name], width=17, align='center')\n    ax.set_ylabel(\"\")\n    ax.set_title(name)\n    if ax != axes[-1]:\n        ax.set_xlabel('')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:30.948405Z","iopub.execute_input":"2021-12-30T20:48:30.949226Z","iopub.status.idle":"2021-12-30T20:48:32.861534Z","shell.execute_reply.started":"2021-12-30T20:48:30.949182Z","shell.execute_reply":"2021-12-30T20:48:32.860786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# boxplots provides us with more precious information where some important stats as median, interquartile range, outliers can be \n# explored\nfig, axes = plt.subplots(8, 1, figsize=(15, 25), sharex=True)\nfor name, ax in zip(['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target'], axes):\n    sns.boxplot(data = df[df['Asset_ID'] == 1], x='month', y=name, ax=ax)\n    ax.set_ylabel(\"\")\n    ax.set_title(name)\n    if ax != axes[-1]:\n        ax.set_xlabel('')\nplt.savefig(\"fig_4.png\")","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:32.862748Z","iopub.execute_input":"2021-12-30T20:48:32.863339Z","iopub.status.idle":"2021-12-30T20:48:37.059305Z","shell.execute_reply.started":"2021-12-30T20:48:32.863301Z","shell.execute_reply":"2021-12-30T20:48:37.058731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# again we perform resampling and rolling, but this time on a daily basis which might provide more specific info\ndf_day = df[df['Asset_ID'] == 1].resample(\"D\").mean()\ndf_day = df_day.fillna(0)","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:37.060347Z","iopub.execute_input":"2021-12-30T20:48:37.060727Z","iopub.status.idle":"2021-12-30T20:48:37.196447Z","shell.execute_reply.started":"2021-12-30T20:48:37.060699Z","shell.execute_reply":"2021-12-30T20:48:37.195538Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from the line plot we can extract info on daily trends and seasonality\ndf_day[df_day['Asset_ID'] == 1][['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target']].plot(subplots=True, figsize=(15, 25))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:37.197954Z","iopub.execute_input":"2021-12-30T20:48:37.198337Z","iopub.status.idle":"2021-12-30T20:48:39.337347Z","shell.execute_reply.started":"2021-12-30T20:48:37.198296Z","shell.execute_reply":"2021-12-30T20:48:39.336450Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# we still see a lot of spikes which are not helpful for modelling\n# let's smooth them down by resampling to a lower freq and rolling\ndf_week = df[df['Asset_ID'] == 1].resample(\"W\").mean()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:39.338781Z","iopub.execute_input":"2021-12-30T20:48:39.339020Z","iopub.status.idle":"2021-12-30T20:48:39.472877Z","shell.execute_reply.started":"2021-12-30T20:48:39.338993Z","shell.execute_reply":"2021-12-30T20:48:39.471646Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_week = df_week.astype('float64')\ndf_week = df_week.fillna(method='pad')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:39.474129Z","iopub.execute_input":"2021-12-30T20:48:39.474362Z","iopub.status.idle":"2021-12-30T20:48:39.484109Z","shell.execute_reply.started":"2021-12-30T20:48:39.474337Z","shell.execute_reply":"2021-12-30T20:48:39.482852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# here we compare the daily trends and seasonality to the weekly ones\nfig, axes = plt.subplots(8, 1, figsize=(15, 25), sharex=True)\nfor name, ax in zip(['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target'], axes):\n    ax.plot(df_day[name], marker='.', linestyle='-', linewidth = 0.5, label='Daily', color='black')\n    ax.plot(df_week[name], marker='o', markersize=8, linestyle='-', label='Weekly', color='coral')\n    ax.set_ylabel(name)\n    ax.legend()\n    ax.set_ylabel(\"\")\n    ax.set_title(name)\n    if ax != axes[-1]:\n        ax.set_xlabel('')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:39.486120Z","iopub.execute_input":"2021-12-30T20:48:39.486471Z","iopub.status.idle":"2021-12-30T20:48:42.528800Z","shell.execute_reply.started":"2021-12-30T20:48:39.486431Z","shell.execute_reply":"2021-12-30T20:48:42.527927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cols = ['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target']\nfor col in cols:\n    df_week[f'{col}_Change'] = df_week[col].div(df_week[col].shift())\n    \ndf_week = df_week.fillna(method='bfill')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:42.530196Z","iopub.execute_input":"2021-12-30T20:48:42.530574Z","iopub.status.idle":"2021-12-30T20:48:42.548161Z","shell.execute_reply.started":"2021-12-30T20:48:42.530546Z","shell.execute_reply":"2021-12-30T20:48:42.547540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot the weekly change =====> thisWeekReading / previousWeekReading\n# this enables us to trace the trend of the ratio of these features. \n# wherever the value is greater than one, this means that this feature \n# has reported an increment from the previous week \ndf_week[df_week['Asset_ID'] == 1][[f'{col}_Change' for col in ['Count', 'Open', 'High', 'Low', 'Close', \n                                                               'Volume', 'VWAP', 'Target']]].plot(subplots=True, figsize=(15, 25))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:42.549300Z","iopub.execute_input":"2021-12-30T20:48:42.549676Z","iopub.status.idle":"2021-12-30T20:48:44.640454Z","shell.execute_reply.started":"2021-12-30T20:48:42.549633Z","shell.execute_reply":"2021-12-30T20:48:44.639429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# in many cases percent change is informative\ncols = ['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target']\nfor col in cols:\n    df_week[f'{col}_pct_change'] = df_week[col].pct_change()*100\n    \ndf_week = df_week.fillna(method='bfill')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:44.641570Z","iopub.execute_input":"2021-12-30T20:48:44.641841Z","iopub.status.idle":"2021-12-30T20:48:44.661275Z","shell.execute_reply.started":"2021-12-30T20:48:44.641803Z","shell.execute_reply":"2021-12-30T20:48:44.660574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot the weekly change =====> thisWeekReading / previousWeekReading\ndf_week[df_week['Asset_ID'] == 1][[f'{col}_pct_change' for col in ['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target']]]\\\n                                                                    .plot(kind='bar', subplots=True, figsize=(15, 25))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:44.662450Z","iopub.execute_input":"2021-12-30T20:48:44.662790Z","iopub.status.idle":"2021-12-30T20:48:47.570651Z","shell.execute_reply.started":"2021-12-30T20:48:44.662757Z","shell.execute_reply":"2021-12-30T20:48:47.569674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# here for each column we plot the original values versus the expanding mean (cumulative mean) versus the expanding std\n\ncols = ['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target']\nfor col in cols:\n    df_week[f'{col}_expanding_mean'] = df_week[col].expanding().mean()\n    df_week[f'{col}_expanding_std'] = df_week[col].expanding().std()\n\ndf_week = df_week.fillna(method='bfill')","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:47.571756Z","iopub.execute_input":"2021-12-30T20:48:47.571982Z","iopub.status.idle":"2021-12-30T20:48:47.604574Z","shell.execute_reply.started":"2021-12-30T20:48:47.571957Z","shell.execute_reply":"2021-12-30T20:48:47.603928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# here for each column we plot the original values versus the expanding mean (cumulative mean) versus the expanding std\n\nfig, axes = plt.subplots(8, 1, figsize=(15, 25), sharex=False)\nfor i, (name, ax) in enumerate(zip(['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP', 'Target'], axes)):\n    plt.subplot(8, 1, i+1)\n    ax = df_week[f'{name}'].plot(label=name)\n    ax = df_week[f'{name}_expanding_mean'].plot(label=f'{name} expanding mean')\n    ax = df_week[f'{name}_expanding_std'].plot(label=f'{name} expanding std')\n    ax.set_ylabel(name)\n    ax.legend()\n    ax.set_ylabel(\"\")\n    ax.set_title(name)\n    if ax != axes[-1]:\n        ax.set_xlabel('')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:47.606029Z","iopub.execute_input":"2021-12-30T20:48:47.606504Z","iopub.status.idle":"2021-12-30T20:48:50.435565Z","shell.execute_reply.started":"2021-12-30T20:48:47.606465Z","shell.execute_reply":"2021-12-30T20:48:50.434714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### *These plots assures the high volatility of the data, where data statistics such as mean and standard deviation are varying over time.*","metadata":{}},{"cell_type":"code","source":"df_hour = df[df['Asset_ID'] == 1].resample(\"H\").mean().astype('float64')\ndf_hour = df_hour.fillna(method='pad')\n\ndf_hour['day_'] = df_hour['day_'].astype(int)\ndf_hour['hr_'] = df_hour['hr_'].astype(int)","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:50.436607Z","iopub.execute_input":"2021-12-30T20:48:50.436837Z","iopub.status.idle":"2021-12-30T20:48:50.573714Z","shell.execute_reply.started":"2021-12-30T20:48:50.436811Z","shell.execute_reply":"2021-12-30T20:48:50.572923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"hour_day_df = pd.pivot_table(df_hour, \n                             values=\"Close\",\n                             index=['hr_'],\n                             columns=['day_'],\n                             fill_value=0,\n                             margins=True)","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:50.575129Z","iopub.execute_input":"2021-12-30T20:48:50.575370Z","iopub.status.idle":"2021-12-30T20:48:50.686771Z","shell.execute_reply.started":"2021-12-30T20:48:50.575346Z","shell.execute_reply":"2021-12-30T20:48:50.685838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# this heat map enables us to keep track of hours of all days over all months and see which hours of day had the \n# the highest mean close value and which ones had the smallest values \nplt.figure(figsize=(15, 15))\nax = sns.heatmap(hour_day_df, cmap='RdYlGn_r', robust=True, fmt='.0f', \n                 annot=True, linewidths=.5, annot_kws={'size':9, 'rotation': 90}, \n                 cbar_kws={'shrink':.8, 'label':'Close'})                       \n    \nax.set_yticklabels(ax.get_yticklabels(), rotation=0, fontsize=10)\nax.set_xticklabels(ax.get_xticklabels(), rotation=0, fontsize=10)\nplt.title('Average Close', fontdict={'fontsize':18},    pad=14)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:48:50.690949Z","iopub.execute_input":"2021-12-30T20:48:50.691482Z","iopub.status.idle":"2021-12-30T20:48:54.498636Z","shell.execute_reply.started":"2021-12-30T20:48:50.691434Z","shell.execute_reply":"2021-12-30T20:48:54.497754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### *We can see that on average the days with the high opening values take place during the half of the month, especially between the 9th and 17th day*  ","metadata":{"execution":{"iopub.status.busy":"2021-12-30T20:35:48.108155Z","iopub.execute_input":"2021-12-30T20:35:48.108511Z","iopub.status.idle":"2021-12-30T20:35:48.113586Z","shell.execute_reply.started":"2021-12-30T20:35:48.108477Z","shell.execute_reply":"2021-12-30T20:35:48.112374Z"}}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}