{"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":"# Singular Vector Decomposition 🔹\n\nSingular Vector Decomposition is a very popular collaborative filtering technique for RecSys problems. It has been used a lot in the past when there was a boom in RecSys (Netflix Prize). It's still a pretty damn good model. We'll be exploring how to use this on our competition here.\n\n#### If you liked this notebook, please leave an upvote!","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"# Problems 🔹\n\n1. One of the problem of SVD for this task has been that it has implicit data (only positive outcomes) rather than explicit feedback (variation of at least two outcomes - yes/no, 1/0, 1-5 etc). The major task at hand is to convert the implicit feedback to explicit feedback.\n\n2. The other bottleneck would have been the size of the user-item matrix which would have been too huge to fit in memory if we use the scipy svd. I'll use my recsys library where I had implemented the cython backed iterative version of svd or [FunkSVD](https://sifter.org/simon/journal/20061211.html) as it is famously called.","metadata":{}},{"cell_type":"markdown","source":"**Negative Sampling:** I experimented with different negative sampling policies but could not achieve any clear improvement over the existing methods. Hence I'll not include negative sampling mechanism here. Feel free to experiment.","metadata":{}},{"cell_type":"markdown","source":"#### reco (https://github.com/mayukh18/reco) \nWe'll be using my RecSys library **reco**. Give it a ⭐ if it helps you in this competition. Check it out. There are other models implemented which can all be applied in this competition.","metadata":{}},{"cell_type":"code","source":"# It is available on pypi but I haven't updated the release there. So install from github\n!pip install git+https://github.com/mayukh18/reco","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:35:41.742303Z","iopub.execute_input":"2023-08-15T13:35:41.742705Z","iopub.status.idle":"2023-08-15T13:36:23.145476Z","shell.execute_reply.started":"2023-08-15T13:35:41.742602Z","shell.execute_reply":"2023-08-15T13:36:23.144456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport numpy as np\nimport pandas as pd\nimport os\nimport glob\nimport reco\nfrom tqdm import tqdm\nimport datetime\nfrom collections import Counter","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:36:23.148967Z","iopub.execute_input":"2023-08-15T13:36:23.149283Z","iopub.status.idle":"2023-08-15T13:36:23.159191Z","shell.execute_reply.started":"2023-08-15T13:36:23.14925Z","shell.execute_reply":"2023-08-15T13:36:23.158146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Forming Train Set 🔹\n\nWe'll keep 2 weeks as train and the last week as validation.","metadata":{}},{"cell_type":"code","source":"data = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/transactions_train.csv\", dtype={'article_id':str})\ndata[\"t_dat\"] = pd.to_datetime(data[\"t_dat\"])\ndata.head()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:36:23.160475Z","iopub.execute_input":"2023-08-15T13:36:23.160757Z","iopub.status.idle":"2023-08-15T13:37:54.532686Z","shell.execute_reply.started":"2023-08-15T13:36:23.160722Z","shell.execute_reply":"2023-08-15T13:37:54.531914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"All Transactions Date Range: {} to {}\".format(data['t_dat'].min(), data['t_dat'].max()))\n\ndata[\"t_dat\"] = pd.to_datetime(data[\"t_dat\"])\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)]","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:37:54.534843Z","iopub.execute_input":"2023-08-15T13:37:54.535757Z","iopub.status.idle":"2023-08-15T13:37:56.6831Z","shell.execute_reply.started":"2023-08-15T13:37:54.535709Z","shell.execute_reply":"2023-08-15T13:37:56.681972Z"},"trusted":true},"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":{"iopub.status.busy":"2023-08-15T13:37:56.684449Z","iopub.execute_input":"2023-08-15T13:37:56.684716Z","iopub.status.idle":"2023-08-15T13:38:06.212014Z","shell.execute_reply.started":"2023-08-15T13:37:56.684685Z","shell.execute_reply":"2023-08-15T13:38:06.211208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Implicit to Explicit Feedback 🔹\n\nThere can be multiple ways to convert the implicit data to explicit. But all ways have the same fundamental idea. We'll treat the purchase of an unpopular item to be of high explicit feedback and that of a popular item to be of low explicit feedback. Also, if an user buys a lot of items, that can weigh down the importance of one single item.\n\nSome ideas:\n\n1. user-item purchase count weighed down by time decaying popularity of the item as introduced in https://www.kaggle.com/mayukh18/time-decaying-popularity-benchmark-0-0216 (We will use this in this notebook)\n2. user-item purchase count weighed down by the product of total purchase count of the item and total purchase count of the user.\n\nNote: No idea is the absolute perfect. As you will combine different heuristic approaches with the SVD you'll realize different conversion techniques are favourable for different heuristics approaches.","metadata":{}},{"cell_type":"code","source":"train = pd.concat([train1, train2, train3, train4], axis=0)\n\n#time decay popularity of each article\ntrain['pop_factor'] = train['t_dat'].apply(lambda x: 1/(datetime.datetime(2020,9,16) - x).days**2)\npopular_items_group = train.groupby(['article_id'])['pop_factor'].sum()\n\n# purchase count of each article\nitems_total_count = train.groupby(['article_id'])['article_id'].count()\n# purchase count of each user\nusers_total_count = train.groupby(['customer_id'])['customer_id'].count()\n\n\ntrain['feedback'] = 1\ntrain = train.groupby(['customer_id', 'article_id']).sum().reset_index()\ntrain['feedback'] = train.apply(lambda row: row['feedback']/popular_items_group[row['article_id']], axis=1)\n\ntrain['feedback'] = train['feedback'].apply(lambda x: 5.0 if x>5.0 else x)\ntrain.drop(['price', 'sales_channel_id'], axis=1, inplace=True)\n\n# shuffling\ntrain = train.sample(frac=1).reset_index(drop=True)\ntrain['feedback'].describe()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:38:06.213578Z","iopub.execute_input":"2023-08-15T13:38:06.214168Z","iopub.status.idle":"2023-08-15T13:39:07.096268Z","shell.execute_reply.started":"2023-08-15T13:38:06.214117Z","shell.execute_reply":"2023-08-15T13:39:07.095304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Basic time decaying popularity to combine it with the SVD.","metadata":{}},{"cell_type":"code","source":"train_pop = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,9,1)) & (data['t_dat'] < datetime.datetime(2020,9,16))]\ntrain_pop['pop_factor'] = train_pop['t_dat'].apply(lambda x: 1/(datetime.datetime(2020,9,16) - x).days)\npopular_items_group = train_pop.groupby(['article_id'])['pop_factor'].sum()\n\n_, popular_items = zip(*sorted(zip(popular_items_group, popular_items_group.keys()))[::-1])\n\ntrain_pop['pop_factor'].describe()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:39:07.098104Z","iopub.execute_input":"2023-08-15T13:39:07.098605Z","iopub.status.idle":"2023-08-15T13:39:18.864186Z","shell.execute_reply.started":"2023-08-15T13:39:07.098567Z","shell.execute_reply":"2023-08-15T13:39:18.863165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Below part is a cool new addition which gives a small bump. Did not add it to the validation part but it has the same effect there too. It predicts the most frequent next item bought by an user after a particular item.","metadata":{}},{"cell_type":"code","source":"def get_most_freq_next_item(user_group):\n    next_items = {}\n    for user in tqdm(user_group.keys()):\n        items = user_group[user]\n        for i,item in enumerate(items[:-1]):\n            if item not in next_items:\n                next_items[item] = []\n            if item != items[i+1]:\n                next_items[item].append(items[i+1])\n\n    pred_next = {}\n    for item in next_items:\n        if len(next_items[item]) >= 5:\n            most_common = Counter(next_items[item]).most_common()\n            ratio = most_common[0][1]/len(next_items[item])\n            if ratio >= 0.1:\n                pred_next[item] = most_common[0][0]\n            \n    return pred_next\n\nuser_group = train.groupby(['customer_id'])['article_id'].apply(list)\npred_next = get_most_freq_next_item(user_group)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:39:18.865794Z","iopub.execute_input":"2023-08-15T13:39:18.86634Z","iopub.status.idle":"2023-08-15T13:39:30.106958Z","shell.execute_reply.started":"2023-08-15T13:39:18.866299Z","shell.execute_reply":"2023-08-15T13:39:30.105907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# SVD Training 🔹","metadata":{}},{"cell_type":"code","source":"from reco.recommender import FunkSVD\nfrom reco.metrics import rmse\n\n# k = number of dimensions of the latent embedding. formatizer dict takes in names of the columns\n# for user, item and values/feedback/ratings respectively.\n\nsvd = FunkSVD(k=8, learning_rate=0.008, regularizer = .01, iterations = 80, method = 'stochastic', bias=True)\nsvd.fit(X=train, formatizer={'user':'customer_id', 'item':'article_id', 'value':'feedback'},verbose=True)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:39:30.108441Z","iopub.execute_input":"2023-08-15T13:39:30.10873Z","iopub.status.idle":"2023-08-15T13:40:05.139215Z","shell.execute_reply.started":"2023-08-15T13:39:30.108693Z","shell.execute_reply":"2023-08-15T13:40:05.138221Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Validation 🔹\n\nThis is mostly based on [this notebook](https://www.kaggle.com/mayukh18/time-decaying-popularity-benchmark-0-0216) where I have used same pipeline. Will use SVD for re-ranking. Will read all the data anew and train a new model on our new train set with new date ranges for submission. This will align us with the aforementioned notebook.","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.status.busy":"2023-08-15T13:40:05.142034Z","iopub.execute_input":"2023-08-15T13:40:05.142301Z","iopub.status.idle":"2023-08-15T13:40:05.151845Z","shell.execute_reply.started":"2023-08-15T13:40:05.142269Z","shell.execute_reply":"2023-08-15T13:40:05.150677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"positive_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])\n    \nprint(\"Total users in validation:\", len(val_users))","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:40:05.153385Z","iopub.execute_input":"2023-08-15T13:40:05.153728Z","iopub.status.idle":"2023-08-15T13:40:07.618813Z","shell.execute_reply.started":"2023-08-15T13:40:05.153689Z","shell.execute_reply":"2023-08-15T13:40:07.61773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Normal way of prediction without the SVD reranking","metadata":{}},{"cell_type":"code","source":"from collections import Counter\noutputs = []\ncnt = 0\n\npopular_items = list(popular_items)\n\nfor user in tqdm(val_users):\n    user_output = []\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    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    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    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    user_output += [pred_next[item] for item in user_output if item in pred_next and pred_next[item] not in user_output]      \n    \n    user_output += list(popular_items[:12 - len(user_output)])\n    outputs.append(user_output)\n    \nprint(\"mAP Score on Validation set:\", mapk(val_items, outputs))","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:40:07.619918Z","iopub.execute_input":"2023-08-15T13:40:07.620185Z","iopub.status.idle":"2023-08-15T13:40:14.444402Z","shell.execute_reply.started":"2023-08-15T13:40:07.620153Z","shell.execute_reply":"2023-08-15T13:40:14.443472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Now, prediction WITH the SVD reranking!","metadata":{}},{"cell_type":"code","source":"from collections import Counter\noutputs = []\ncnt = 0\n\npopular_items = list(popular_items)\nuserindexes = {svd.users[i]:i for i in range(len(svd.users))}\n\nfor user in tqdm(val_users):\n    user_output = []\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_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = svd.items.index(k)\n                pred_value = np.dot(svd.userfeatures[user_index], svd.itemfeatures[itemindex].T) + svd.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][: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_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = svd.items.index(k)\n                pred_value = np.dot(svd.userfeatures[user_index], svd.itemfeatures[itemindex].T) + svd.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][: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_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = svd.items.index(k)\n                pred_value = np.dot(svd.userfeatures[user_index], svd.itemfeatures[itemindex].T) + svd.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][: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_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = svd.items.index(k)\n                pred_value = np.dot(svd.userfeatures[user_index], svd.itemfeatures[itemindex].T) + svd.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][:12]\n        \n    user_output += [pred_next[item] for item in user_output if item in pred_next and pred_next[item] not in user_output]      \n    \n    user_output += list(popular_items[:12 - len(user_output)])\n    outputs.append(user_output)\n    \nprint(\"mAP Score on Validation set:\", mapk(val_items, outputs))","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:40:14.446285Z","iopub.execute_input":"2023-08-15T13:40:14.446626Z","iopub.status.idle":"2023-08-15T13:42:34.427233Z","shell.execute_reply.started":"2023-08-15T13:40:14.446579Z","shell.execute_reply":"2023-08-15T13:42:34.426086Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Decent jump of 0.001 I would say with 4 weeks of training data!","metadata":{}},{"cell_type":"markdown","source":"# Submission\n\nWe will do all the same things all over again just with different time periods.","metadata":{}},{"cell_type":"code","source":"train1 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,9,16)) & (data['t_dat'] < datetime.datetime(2020,9,23))]\ntrain2 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,9,8)) & (data['t_dat'] < datetime.datetime(2020,9,16))]\ntrain3 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,8,31)) & (data['t_dat'] < datetime.datetime(2020,9,8))]\ntrain4 = data.loc[(data[\"t_dat\"] >= datetime.datetime(2020,8,23)) & (data['t_dat'] < datetime.datetime(2020,8,31))]\n\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)\n\ntrain = pd.concat([train1, train2], axis=0)\ntrain['pop_factor'] = train['t_dat'].apply(lambda x: 1/(datetime.datetime(2020,9,23) - x).days)\npopular_items_group = train.groupby(['article_id'])['pop_factor'].sum()\n\n_, popular_items = zip(*sorted(zip(popular_items_group, popular_items_group.keys()))[::-1])\n\nuser_group = pd.concat([train1, train2, train3, train4], axis=0).groupby(['customer_id'])['article_id'].apply(list)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:42:34.428843Z","iopub.execute_input":"2023-08-15T13:42:34.429145Z","iopub.status.idle":"2023-08-15T13:43:06.027063Z","shell.execute_reply.started":"2023-08-15T13:42:34.429107Z","shell.execute_reply":"2023-08-15T13:43:06.026032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# SVD\ntrain = pd.concat([train1, train2, train3, train4], axis=0)\ntrain['pop_factor'] = train['t_dat'].apply(lambda x: 1/(datetime.datetime(2020,9,23) - x).days**2)\npopular_items_group = train.groupby(['article_id'])['pop_factor'].sum()\n\ntrain['feedback'] = 1\ntrain = train.groupby(['customer_id', 'article_id']).sum().reset_index()\n\ntrain['feedback'] = train.apply(lambda row: row['feedback']/popular_items_group[row['article_id']], axis=1)\n\ntrain['feedback'] = train['feedback'].apply(lambda x: 5.0 if x>5.0 else x)\ntrain.drop(['price', 'sales_channel_id'], axis=1, inplace=True)\ntrain['feedback'].describe()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:43:06.028633Z","iopub.execute_input":"2023-08-15T13:43:06.028909Z","iopub.status.idle":"2023-08-15T13:44:07.803391Z","shell.execute_reply.started":"2023-08-15T13:43:06.028875Z","shell.execute_reply":"2023-08-15T13:44:07.802376Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train.sample(frac=1).reset_index(drop=True)\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:44:07.805416Z","iopub.execute_input":"2023-08-15T13:44:07.806175Z","iopub.status.idle":"2023-08-15T13:44:08.236177Z","shell.execute_reply.started":"2023-08-15T13:44:07.806116Z","shell.execute_reply":"2023-08-15T13:44:08.234542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from reco.recommender import FunkSVD\nfrom reco.metrics import rmse\n\nf = FunkSVD(k=8, learning_rate=0.005, regularizer = .01, iterations = 200, method = 'stochastic', bias=True)\nf.fit(X=train, formatizer={'user':'customer_id', 'item':'article_id', 'value':'feedback'},verbose=True)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:44:08.238138Z","iopub.execute_input":"2023-08-15T13:44:08.238597Z","iopub.status.idle":"2023-08-15T13:45:15.026505Z","shell.execute_reply.started":"2023-08-15T13:44:08.238542Z","shell.execute_reply":"2023-08-15T13:45:15.025344Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/sample_submission.csv\")\nsubmission.head()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:45:15.028285Z","iopub.execute_input":"2023-08-15T13:45:15.028674Z","iopub.status.idle":"2023-08-15T13:45:21.66561Z","shell.execute_reply.started":"2023-08-15T13:45:15.028621Z","shell.execute_reply":"2023-08-15T13:45:21.664608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"outputs = []\ncnt = 0\n\npopular_items = list(popular_items)\nuserindexes = {f.users[i]:i for i in range(len(f.users))}\n\nfor user in tqdm(submission['customer_id']):\n    user_output = []\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        \n        user_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = f.items.index(k)\n                pred_value = np.dot(f.userfeatures[user_index], f.itemfeatures[itemindex].T) + f.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][: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        \n        user_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = f.items.index(k)\n                pred_value = np.dot(f.userfeatures[user_index], f.itemfeatures[itemindex].T) + f.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][: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        \n        user_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = f.items.index(k)\n                pred_value = np.dot(f.userfeatures[user_index], f.itemfeatures[itemindex].T) + f.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][: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        \n        user_index = userindexes[user]\n        new_order = {}\n        for k in list(most_common_items_of_user.keys())[:20]:\n            try:\n                itemindex = f.items.index(k)\n                pred_value = np.dot(f.userfeatures[user_index], f.itemfeatures[itemindex].T) + f.item_bias[0, itemindex]\n            except:\n                pred_value = most_common_items_of_user[k]\n            new_order[k] = pred_value\n        user_output += [k for k, v in sorted(new_order.items(), key=lambda item: item[1])][:12]\n        \n    user_output += [pred_next[item] for item in user_output if item in pred_next and pred_next[item] not in user_output]      \n    \n    user_output += list(popular_items[:12 - len(user_output)])\n    outputs.append(user_output)\n    \nstr_outputs = []\nfor output in outputs:\n    str_outputs.append(\" \".join([str(x) for x in output]))","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:45:21.667327Z","iopub.execute_input":"2023-08-15T13:45:21.667633Z","iopub.status.idle":"2023-08-15T13:59:54.576997Z","shell.execute_reply.started":"2023-08-15T13:45:21.667597Z","shell.execute_reply":"2023-08-15T13:59:54.57608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission['prediction'] = str_outputs\nsubmission.to_csv(\"submissions.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T13:59:54.578365Z","iopub.execute_input":"2023-08-15T13:59:54.578694Z","iopub.status.idle":"2023-08-15T14:02:11.09725Z","shell.execute_reply.started":"2023-08-15T13:59:54.578654Z","shell.execute_reply":"2023-08-15T14:02:11.095132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.head()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T14:02:11.100228Z","iopub.execute_input":"2023-08-15T14:02:11.100594Z","iopub.status.idle":"2023-08-15T14:02:11.118071Z","shell.execute_reply.started":"2023-08-15T14:02:11.100554Z","shell.execute_reply":"2023-08-15T14:02:11.117064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### That's it! Upvote and Enjoy!\nExamples of FunkSVD and FM are over at https://github.com/mayukh18/reco/tree/master/examples. I'll try to add an example of [Wide And Deep Network](https://arxiv.org/pdf/1606.07792.pdf) in the coming days. That is also a pretty cool model to try next.","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}