{"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":"# Import libraries\n","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport glob\n#import reco\nfrom tqdm import tqdm\nimport datetime\nfrom functools import partial\nfrom dask.diagnostics import ProgressBar\nProgressBar().register()\nimport dask.dataframe as dd","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:26:01.417441Z","iopub.status.busy":"2022-03-04T08:26:01.417029Z","iopub.status.idle":"2022-03-04T08:26:05.540967Z","shell.execute_reply":"2022-03-04T08:26:05.540004Z","shell.execute_reply.started":"2022-03-04T08:26:01.417357Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tqdm.pandas()","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:26:05.544203Z","iopub.status.busy":"2022-03-04T08:26:05.543234Z","iopub.status.idle":"2022-03-04T08:26:05.552418Z","shell.execute_reply":"2022-03-04T08:26:05.551667Z","shell.execute_reply.started":"2022-03-04T08:26:05.544123Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Read data","metadata":{}},{"cell_type":"code","source":"data = pd.read_csv('transactions_train.csv',dtype={'article_id':str})\nprint(data.shape)","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:30:05.168987Z","iopub.status.busy":"2022-03-04T08:30:05.168540Z","iopub.status.idle":"2022-03-04T08:30:08.730796Z","shell.execute_reply":"2022-03-04T08:30:08.730119Z","shell.execute_reply.started":"2022-03-04T08:30:05.168956Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data.head()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data.t_dat = pd.to_datetime(data.t_dat)#data.t_dat.progress_apply(lambda x: datetime.datetime.strptime(x,'%Y-%m-%d'))\ndata = data[['t_dat','customer_id','article_id']]","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Select only the last weeks\n\nThis way we will keep the relevant data and keep its size reasonable. We will take 2 weeks for training and leave the last one for validation","metadata":{}},{"cell_type":"code","source":"print(\"All Transactions Date Range: {} to {}\".format(data['t_dat'].min(), data['t_dat'].max()))\n\ntrain1 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,9,8)) & (data['t_dat'] < datetime.datetime(2020,9,16))]\ntrain2 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,9,1)) & (data['t_dat'] < datetime.datetime(2020,9,8))]\ntrain3 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,8,23)) & (data['t_dat'] < datetime.datetime(2020,9,1))]\ntrain4 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,8,15)) & (data['t_dat'] < datetime.datetime(2020,8,23))]\n\nval = data.loc[data[\"t_dat\"] >= datetime.datetime(2020,9,16)]\n\n#del data","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:30:08.732604Z","iopub.status.busy":"2022-03-04T08:30:08.732251Z","iopub.status.idle":"2022-03-04T08:30:08.772891Z","shell.execute_reply":"2022-03-04T08:30:08.772184Z","shell.execute_reply.started":"2022-03-04T08:30:08.732568Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# List of all purchases per user (has repetitions)\npositive_items_per_user1 = train1.groupby(['customer_id'])['article_id'].apply(list)\npositive_items_per_user2 = train2.groupby(['customer_id'])['article_id'].apply(list)\npositive_items_per_user3 = train3.groupby(['customer_id'])['article_id'].apply(list)\npositive_items_per_user4 = train4.groupby(['customer_id'])['article_id'].apply(list)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.concat([train1,train2], axis=0) #train2","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Non-personalized \n\nFor this we will compute the top best items in our data as well as stratify the customer population in ages (divided in deciles), as we suppose different intervals will buy different items. \n\nThe best items will have a score sensible to time, as items that have not been bought in the recent weeks might not be as relevant as new ones. ","metadata":{}},{"cell_type":"markdown","source":"Let's read the customer dataset to find the age:","metadata":{}},{"cell_type":"code","source":"customers = pd.read_csv('customers.csv')\ncustomers = customers[['customer_id','age']]","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:30:13.578933Z","iopub.status.busy":"2022-03-04T08:30:13.578681Z","iopub.status.idle":"2022-03-04T08:30:13.815258Z","shell.execute_reply":"2022-03-04T08:30:13.814542Z","shell.execute_reply.started":"2022-03-04T08:30:13.578906Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Merge it with the training dataset:","metadata":{}},{"cell_type":"code","source":"train = train.merge(customers, how='left', on='customer_id')\nval = val.merge(customers, how='left', on='customer_id')","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:30:16.796227Z","iopub.status.busy":"2022-03-04T08:30:16.795981Z","iopub.status.idle":"2022-03-04T08:30:17.303134Z","shell.execute_reply":"2022-03-04T08:30:17.302434Z","shell.execute_reply.started":"2022-03-04T08:30:16.796200Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Calculate deciles of age and a popularity factor based on the time of the transaction:","metadata":{}},{"cell_type":"code","source":"train['age2'] = pd.qcut(train['age'], 10)\ntrain['age2'].value_counts()\ntrain['pop_factor'] = train['t_dat'].apply(lambda x: 1/(datetime.datetime(2020,9,16) - x).days)","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:30:18.647795Z","iopub.status.busy":"2022-03-04T08:30:18.646071Z","iopub.status.idle":"2022-03-04T08:30:23.849316Z","shell.execute_reply":"2022-03-04T08:30:23.848589Z","shell.execute_reply.started":"2022-03-04T08:30:18.647749Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We define the function that calculates the most frequent bought items on the training dataset given an age that is in the training set:","metadata":{}},{"cell_type":"code","source":"intervals = train.age2.unique().dropna()\n\ntop_by_age = {}\nfor inter in intervals: \n    train_age = train.loc[train.age2 == inter]\n    popular_items_group = train_age.groupby(['article_id'])['pop_factor'].sum()\n    _, popular_items = zip(*sorted(zip(popular_items_group, popular_items_group.keys()))[::-1][:12])\n    \n    top_by_age.setdefault(inter,popular_items)\n\nage_interval = {age : interval for age in range(15,100) for interval in intervals if age in interval}\n\npopular_items_group = train.groupby(['article_id'])['pop_factor'].sum()\n_, top = zip(*sorted(zip(popular_items_group, popular_items_group.keys()))[::-1][:12])\n","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:30:23.858169Z","iopub.status.busy":"2022-03-04T08:30:23.857237Z","iopub.status.idle":"2022-03-04T08:30:24.022599Z","shell.execute_reply":"2022-03-04T08:30:24.021845Z","shell.execute_reply.started":"2022-03-04T08:30:23.858130Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.pop_factor.describe()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"top","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Item-Based collaborative filtering\n\nFor this methodology we will only pick users that have bought more than 20 times, as this calculus is very memory consuming. Either way, it is also important to pick this group as we need information to generate the scores. \n\nMemory-wise, we have also implemented a sparse matrix, as normal numpy matrices consumed all our RAM :(. This has also resulted in faster computation. \n\nAlso in order to be able to retrieve data after sparsing the data, we have created different dictionaries which allow us to access to any data in real-time. \n","metadata":{}},{"cell_type":"markdown","source":"Filter for the most popular users (have bought >=20 times):","metadata":{}},{"cell_type":"code","source":"train_cf = train[['customer_id','article_id','age']].copy()\nprint(train_cf.shape)\nv = train_cf.customer_id.value_counts()\ntrain_cf = train_cf[train_cf.customer_id.isin(v.index[v.gt(20)])]\ntrain_cf['rating'] = 1\nprint(train_cf.shape)","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:30:38.747855Z","iopub.status.busy":"2022-03-04T08:30:38.747329Z","iopub.status.idle":"2022-03-04T08:30:38.776477Z","shell.execute_reply":"2022-03-04T08:30:38.775788Z","shell.execute_reply.started":"2022-03-04T08:30:38.747818Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Generate the sparse matrix","metadata":{}},{"cell_type":"code","source":"from sklearn.decomposition import TruncatedSVD\nfrom scipy import sparse\n\n\nnum_components = 100 #20\n\nsvd = TruncatedSVD(n_components=num_components)\n\nusers = dict(map(lambda x: x[::-1],enumerate(train_cf.customer_id.unique())))\nitems = dict(map(lambda x: x[::-1],enumerate(train_cf.article_id.unique())))\nid_item = dict(enumerate(train_cf.article_id.unique()))\n\n\nrow = []\ncol = []\nfor i,j,k in zip(train_cf.customer_id.values,train_cf.article_id.values,train_cf.rating.values):\n    row.append(users[i])\n    col.append(items[j])\n\nprint(len(users),len(items),len(train_cf.rating))\n\nX = sparse.csr_matrix((train_cf.rating.values, (col, row)), shape=(len(items),len(users)))\n        \nmatrix = svd.fit_transform(X)\n","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:31:36.763618Z","iopub.status.busy":"2022-03-04T08:31:36.763209Z","iopub.status.idle":"2022-03-04T08:31:38.607858Z","shell.execute_reply":"2022-03-04T08:31:38.606927Z","shell.execute_reply.started":"2022-03-04T08:31:36.763576Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Design the function that given a user it computes the top12 items more suitable:","metadata":{}},{"cell_type":"code","source":"#Funció per a cada usuari:\n\ndef predict_cf(customer_id):\n    '''\n    Function that receives a customer_id and based on the sparse matrix return the 12 best items for it.\n    '''\n    try:\n        #Get the indexes of the items that the user has bought\n        index = X.todense()[:,users[customer_id]].flatten().nonzero()[1]\n\n        #Get vector of row means\n        mean = matrix[[index],:][0].mean(axis=0)\n        \n        #Multiply the sparse with the vector \n        pred = np.dot(matrix, mean)\n\n        #Get the top 12 according to pred\n        top_12_indexes = np.argpartition(-pred,12)[:12]\n\n        top12_to_sort = list(zip(top_12_indexes,pred[top_12_indexes]))\n\n        top12_sorted = list(sorted(top12_to_sort,key=lambda x: x[1],reverse=True))\n\n        top12_articles = [id_item[pair[0]] for pair in top12_sorted]\n\n        return top12_articles\n    except:\n        pass\n    \n","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:31:42.146076Z","iopub.status.busy":"2022-03-04T08:31:42.145704Z","iopub.status.idle":"2022-03-04T08:31:42.160631Z","shell.execute_reply":"2022-03-04T08:31:42.159547Z","shell.execute_reply.started":"2022-03-04T08:31:42.146037Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Compute the CF RecSys for the training dataset:","metadata":{}},{"cell_type":"code","source":"ddf = dd.from_pandas(pd.DataFrame(list(users.keys()),columns=['customer_id']), npartitions=12)\n\nddf['recom'] = ddf.map_partitions(lambda df: df.customer_id.apply(lambda x: predict_cf(x))).compute()\n\nrecom = pd.DataFrame(ddf.compute())\n\n\nrecom","metadata":{"execution":{"iopub.execute_input":"2022-03-04T08:32:34.439968Z","iopub.status.busy":"2022-03-04T08:32:34.439511Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# RecSys Implementation\n\nWe implement the recommendation system. We also add code to simulate the score. The implementation is the following:\n\nFor a user, if it's found in the training set that we use to compute the collaborative-filtering (users that buy >=15 items) we will give the recommendation based on this algorithm computed before. If in the contrary, the user is found in the training set but not in the one used for CF, we recommend the most common items for this user. The most common items is calculated first looking at the items bought in the last week, then if no item is found we look at the second week, and so on. Finally, if the user is not in any of the sets used for training, we give him the most popular items based on the age (if it's found) or in general.\n","metadata":{}},{"cell_type":"markdown","source":"Calculation of the score:","metadata":{}},{"cell_type":"code","source":"def apk(actual, predicted, k=12):\n    if len(predicted)>k:\n        predicted = predicted[:k]\n\n    score = 0.0\n    num_hits = 0.0\n\n    for i,p in enumerate(predicted):\n        if p in actual and p not in predicted[:i]:\n            num_hits += 1.0\n            score += num_hits / (i+1.0)\n\n    if not actual:\n        return 0.0\n\n    return score / min(len(actual), k)\n\ndef mapk(actual, predicted, k=12):\n    return np.mean([apk(a,p,k) for a,p in zip(actual, predicted)])","metadata":{"execution":{"iopub.execute_input":"2022-03-03T23:40:37.703614Z","iopub.status.busy":"2022-03-03T23:40:37.703342Z","iopub.status.idle":"2022-03-03T23:40:37.71022Z","shell.execute_reply":"2022-03-03T23:40:37.709498Z","shell.execute_reply.started":"2022-03-03T23:40:37.703586Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Implement the recommender system in the validation dataset:","metadata":{}},{"cell_type":"code","source":"#Comparem el validation items (actual output del validation) amb el predit del validation a partir del training!\npositive_items_val = val.groupby(['customer_id'])['article_id'].apply(list)\nval_users = positive_items_val.keys()\nval_items = []\n\nfor i,user in tqdm(enumerate(val_users)):\n    val_items.append(positive_items_val[user])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import Counter\noutputs = []\ncnt = 0\ncnt2 = 0\n\nuser_age = dict(zip(val.customer_id,val.age))\npopular_items = list(popular_items)\n\nfor user in tqdm(val_users):\n    if user not in users.keys():\n        \n        user_output = []\n        \n        if user in positive_items_per_user1.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user1[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n            \n        if user in positive_items_per_user2.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user2[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n            \n        if user in positive_items_per_user3.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user3[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n            \n        if user in positive_items_per_user4.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user4[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n            \n        if user in user_age.keys() and ~np.isnan(user_age[user]) and user_age[user] >= 15.0 and user_age[user] < max(age_interval.keys()):\n            cnt2 += 1\n            user_output += list(top_by_age[age_interval[int(user_age[user])]][:12 - len(user_output)])\n            outputs.append(user_output)\n        \n        \n        else: \n            user_output += list(top[:12 - len(user_output)])\n            outputs.append(user_output)\n    else:\n        user_output = list(recom.loc[recom['customer_id'] == user,'recom'].values)\n        user_output += list(top[:12 - len(user_output)])\n        outputs.append(list(user_output))\n        cnt+=1\n        \n    \n        \nprint(cnt2,cnt)\nprint(\"mAP Score on Validation set:\", mapk(val_items, outputs))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Test submission:","metadata":{}},{"cell_type":"markdown","source":"Implement the RecSyst for the test dataset:","metadata":{}},{"cell_type":"code","source":"train1_t = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,9,16)) & (data['t_dat'] < datetime.datetime(2020,9,23))]\ntrain2_t = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,9,8)) & (data['t_dat'] < datetime.datetime(2020,9,16))]\ntrain3_t = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,8,31)) & (data['t_dat'] < datetime.datetime(2020,9,8))]\ntrain4_t = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,8,23)) & (data['t_dat'] < datetime.datetime(2020,8,31))]\n\npositive_items_per_user1_t = train1_t.groupby(['customer_id'])['article_id'].apply(list)\npositive_items_per_user2_t = train2_t.groupby(['customer_id'])['article_id'].apply(list)\npositive_items_per_user3_t = train3_t.groupby(['customer_id'])['article_id'].apply(list)\npositive_items_per_user4_t = train4_t.groupby(['customer_id'])['article_id'].apply(list)\n\ntrain_t = pd.concat([train1_t,train2_t], axis=0) #train2_t\ntrain_t = train_t.merge(customers, how='left', on='customer_id')\ntrain_t['pop_factor'] = train_t['t_dat'].apply(lambda x: 1/(datetime.datetime(2020,9,23) - x).days)\n\ntrain_t['age2'] = pd.qcut(train_t['age'], 10)\ntrain_t","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"intervals_t = train_t.age2.unique().dropna()\n\ntop_by_age = {}\nfor inter in intervals_t: \n    train_age_t = train_t.loc[train_t.age2 == inter]\n    popular_items_group_t = train_age_t.groupby(['article_id'])['pop_factor'].sum()\n    _, popular_items_t = zip(*sorted(zip(popular_items_group_t, popular_items_group_t.keys()))[::-1][:12])\n    \n    top_by_age.setdefault(inter,popular_items_t)\n\nage_interval = {age : interval for age in range(15,100) for interval in intervals_t if age in interval}\n\npopular_items_group_t = train.groupby(['article_id'])['pop_factor'].sum()\n_, top = zip(*sorted(zip(popular_items_group_t, popular_items_group_t.keys()))[::-1][:12])\n\nuser_group = pd.concat([train1, train2, train3, train4], axis=0).groupby(['customer_id'])['article_id'].apply(list)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load test submission:","metadata":{}},{"cell_type":"code","source":"test = pd.read_csv(\"sample_submission.csv\")\ntest = test[['customer_id']]\ntest.head()","metadata":{"execution":{"iopub.execute_input":"2022-03-03T22:34:50.927694Z","iopub.status.busy":"2022-03-03T22:34:50.927436Z","iopub.status.idle":"2022-03-03T22:34:51.201421Z","shell.execute_reply":"2022-03-03T22:34:51.20068Z","shell.execute_reply.started":"2022-03-03T22:34:50.927666Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Apply the RecSys on these customers ids to get predictions:","metadata":{}},{"cell_type":"code","source":"test1 = test.copy()","metadata":{"execution":{"iopub.execute_input":"2022-03-03T22:34:53.731125Z","iopub.status.busy":"2022-03-03T22:34:53.730727Z","iopub.status.idle":"2022-03-03T22:34:54.609539Z","shell.execute_reply":"2022-03-03T22:34:54.608806Z","shell.execute_reply.started":"2022-03-03T22:34:53.731089Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test = test1.merge(customers, how='left', on='customer_id')\n\ndef to_submission(data):\n    return \" \".join([str(x) for x in data])\n        \ndef recommend(user,age): \n    recommendation = []\n    if user not in users.keys():\n        user_output = []\n        if user in positive_items_per_user1_t.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user1_t[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n            \n        if user in positive_items_per_user2_t.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user2_t[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n            \n        if user in positive_items_per_user3_t.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user3_t[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n            \n        if user in positive_items_per_user4_t.keys():\n            most_common_items_of_user = {k:v for k, v in Counter(positive_items_per_user4_t[user]).most_common()}\n            user_output += list(most_common_items_of_user.keys())[:12]\n\n        if user in user_age.keys() and ~np.isnan(user_age[user]) and user_age[user] >= 15.0 and user_age[user] < max(age_interval.keys()):\n            user_output += list(top_by_age[age_interval[int(user_age[user])]][:12 - len(user_output)])\n        else: \n            user_output += list(top[:12 - len(user_output)])\n            \n        return user_output\n    else:\n        user_output = list(recom.loc[recom['customer_id'] == user,'recom'].values)\n        user_output += list(top[:12 - len(user_output)])\n        \n        return user_output\n\n\ntest['prediction'] = test.progress_apply(lambda x: to_submission(recommend(x.customer_id,x.age)),axis=1)\n","metadata":{"execution":{"iopub.execute_input":"2022-03-03T23:23:45.8425Z","iopub.status.busy":"2022-03-03T23:23:45.842228Z","iopub.status.idle":"2022-03-03T23:26:54.222689Z","shell.execute_reply":"2022-03-03T23:26:54.221163Z","shell.execute_reply.started":"2022-03-03T23:23:45.842468Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test","metadata":{"execution":{"iopub.execute_input":"2022-03-03T23:27:25.240315Z","iopub.status.busy":"2022-03-03T23:27:25.239753Z","iopub.status.idle":"2022-03-03T23:27:25.250787Z","shell.execute_reply":"2022-03-03T23:27:25.25016Z","shell.execute_reply.started":"2022-03-03T23:27:25.240279Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Add the predictions to the test dataset to create the final submission:","metadata":{}},{"cell_type":"code","source":"del test['age']\n\ntest.to_csv(f'submission.csv',index=False)\ntest.head()","metadata":{"execution":{"iopub.execute_input":"2022-03-03T23:27:40.150123Z","iopub.status.busy":"2022-03-03T23:27:40.149618Z","iopub.status.idle":"2022-03-03T23:27:51.416633Z","shell.execute_reply":"2022-03-03T23:27:51.415897Z","shell.execute_reply.started":"2022-03-03T23:27:40.150079Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}