{"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":"code","source":"# !pip install cupy","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:40:01.727876Z","iopub.execute_input":"2022-03-02T04:40:01.728337Z","iopub.status.idle":"2022-03-02T04:40:01.733849Z","shell.execute_reply.started":"2022-03-02T04:40:01.728281Z","shell.execute_reply":"2022-03-02T04:40:01.732879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# import cupy as np\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:40:01.737684Z","iopub.execute_input":"2022-03-02T04:40:01.738171Z","iopub.status.idle":"2022-03-02T04:40:01.74652Z","shell.execute_reply.started":"2022-03-02T04:40:01.738141Z","shell.execute_reply":"2022-03-02T04:40:01.745751Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load data from different CSV files\ntransactions_df = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/transactions_train.csv\")\nsubmission_df = pd.read_csv('../input/h-and-m-personalized-fashion-recommendations/sample_submission.csv')","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:40:01.747746Z","iopub.execute_input":"2022-03-02T04:40:01.748104Z","iopub.status.idle":"2022-03-02T04:40:56.855903Z","shell.execute_reply.started":"2022-03-02T04:40:01.748075Z","shell.execute_reply":"2022-03-02T04:40:56.854743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_most_bought_articles(data, num_articles=5):\n    # Create dataframe that contains the number of times each article has been bought\n    articles_counts = data[['article_id', 't_dat']].groupby('article_id').count().reset_index().rename(columns={'t_dat': 'count'})\n    articles_counts = articles_counts.sort_values(by='count', ascending=False)\n        \n    most_bought_articles = articles_counts.loc[articles_counts['count'] >= num_articles]['article_id'].values\n    \n    return most_bought_articles","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:40:56.858199Z","iopub.execute_input":"2022-03-02T04:40:56.858469Z","iopub.status.idle":"2022-03-02T04:40:56.863663Z","shell.execute_reply.started":"2022-03-02T04:40:56.858437Z","shell.execute_reply":"2022-03-02T04:40:56.862705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create training dataset with positive examples.\n# The training data will contain all transactions starting from 01/07/2020.\n# Only items that have been bought at least 10 times will be kept. Also, we\n# are only going to compute the information for the customers that appear\n# in these transactions.\nstart_date = pd.to_datetime('2020-07-01')\n\nfiltered_transactions_df = transactions_df.copy()\nfiltered_transactions_df.t_dat = pd.to_datetime(filtered_transactions_df.t_dat)\nfiltered_transactions_df = filtered_transactions_df.loc[filtered_transactions_df.t_dat >= start_date]\n\ntrain_df = filtered_transactions_df.copy()\n\nmost_bought_articles = get_most_bought_articles(train_df, num_articles=10)\nmost_bought_articles = np.sort(most_bought_articles)\n\ntrain_df = train_df.drop(train_df.loc[~train_df.article_id.isin(most_bought_articles)].index)\nfiltered_transactions_df = filtered_transactions_df.drop(filtered_transactions_df.loc[~filtered_transactions_df.article_id.isin(most_bought_articles)].index)\n\nrecent_customers = train_df.loc[train_df.article_id.isin(most_bought_articles)].customer_id.unique()\nrecent_customers = np.sort(recent_customers)\n\nnum_articles = len(most_bought_articles)\nnum_customers = len(recent_customers)\n\n# Create dictionaries with mapping keys\narticles_id_to_idx = dict(zip(most_bought_articles, range(num_articles)))\ncustomers_id_to_idx = dict(zip(recent_customers, range(num_customers)))\n\ntrain_df = train_df.loc[train_df['article_id'].isin(most_bought_articles)]\ntrain_df = train_df[['customer_id', 'article_id']]\n\ntrain_df['article_id'] = train_df['article_id'].apply(lambda x: articles_id_to_idx[x])\ntrain_df['customer_id'] = train_df['customer_id'].apply(lambda x: customers_id_to_idx[x])\ntrain_df['bought'] = np.ones(train_df.shape[0])\n\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:40:56.865179Z","iopub.execute_input":"2022-03-02T04:40:56.8654Z","iopub.status.idle":"2022-03-02T04:41:09.868636Z","shell.execute_reply.started":"2022-03-02T04:40:56.865373Z","shell.execute_reply":"2022-03-02T04:41:09.86787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Generate negative examples\nnp.random.seed(47)\n\nnum_transactions = train_df.shape[0]\nnegative_data = pd.DataFrame(\n    {\n        'article_id': np.random.choice(num_articles, num_transactions),\n        'customer_id': np.random.choice(num_customers, num_transactions),\n        'bought': np.zeros(num_transactions)\n    }\n)\n\ntrain_df = pd.concat([train_df, negative_data])\ntrain_df = train_df.sample(frac=1).reset_index(drop=True)\n\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:41:09.869687Z","iopub.execute_input":"2022-03-02T04:41:09.869901Z","iopub.status.idle":"2022-03-02T04:41:11.115422Z","shell.execute_reply.started":"2022-03-02T04:41:09.869876Z","shell.execute_reply":"2022-03-02T04:41:11.114585Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def compute_rmse(y_pred, y_true):\n    \"\"\" Compute Root Mean Squared Error. \"\"\"\n    return np.sqrt(np.mean(np.power(y_pred - y_true, 2)))\n\n\ndef evaluate(predict_f,data_train,data_test):\n    \"\"\" RMSE-based predictive performance evaluation with pandas. \"\"\"\n    ids_to_estimate = zip(data_test.customer_id, data_test.article_id)\n    list_users = set(data_train.customer_id)\n    estimated = np.array([predict_f(u,i) for u, i in ids_to_estimate ])\n    real = data_test.bought.values\n    return compute_rmse(estimated, real)\n\n\n# Recommender\nclass ItemBasedRecommender:\n    \"\"\" Collaborative filtering using a custom sim(u,u'). \"\"\"\n    def __init__(self, df, num_articles, num_customers, num_components=10):\n        \"\"\" Constructor \"\"\"\n        self.num_components = num_components\n        self.num_articles = num_articles\n        self.num_customers = num_customers\n        \n        self.train = df\n        \n        self.articles = self.train.article_id.values\n        self.customers = self.train.customer_id.values\n        self.bought = self.train.bought.values\n\n\n    def __sdg__(self):\n        for idx in tqdm(self.training_indices):\n            customer_idx = self.customers[idx]\n            article_idx = self.articles[idx]\n            real_bought = self.bought[idx]\n            \n            prediction = self.__predict_train(customer_idx, article_idx)\n            error = (real_bought - prediction) # error\n            \n            #Update latent factors\n            self.customers_lat_mat[customer_idx] += self.learning_rate * \\\n                                    (error * self.articles_lat_mat[article_idx] - \\\n                                     self.lmbda * self.customers_lat_mat[customer_idx])\n            self.articles_lat_mat[article_idx] += self.learning_rate * \\\n                                    (error * self.customers_lat_mat[customer_idx] - \\\n                                     self.lmbda * self.articles_lat_mat[article_idx])\n                \n                \n    def fit(self, n_epochs=10, learning_rate=0.001, lmbda=0.1):\n        \"\"\"Compute the matrix factorization R = P \\times Q\"\"\"\n        self.learning_rate = learning_rate\n        self.lmbda = lmbda\n        self.n_samples = self.train.shape[0]\n        \n        self.train_rmse =[]\n        self.test_rmse = []\n        \n        # Initialize latent matrices\n        self.customers_lat_mat = np.random.normal(scale=1., size=(self.num_customers, self.num_components))\n        self.articles_lat_mat = np.random.normal(scale=1., size=(self.num_articles, self.num_components))\n\n        for epoch in range(n_epochs):\n            print('Epoch: {}'.format(epoch))\n            \n            self.training_indices = np.random.permutation(self.n_samples)\n            self.__sdg__()\n            \n            # self.train_rmse.append(evaluate(self.__predict_train, self.train, self.train))\n            # self.test_rmse.append(evaluate(self.predict,data_train,data_test))\n            \n            \n            # print('\\tTrain rmse: %s' % self.train_rmse[-1])\n            # print('\\tTest rmse: %s' % self.test_rmse[-1])\n        \n        del self.customers_lat_mat\n            \n        \n    def __predict_train(self, customer_idx, article_idx):\n        \"\"\" Single user and item prediction.\"\"\"\n        prediction = np.dot(self.customers_lat_mat[customer_idx], self.articles_lat_mat[article_idx])\n        prediction = np.clip(prediction, 0, 1)\n        \n        return prediction\n    \n    def predict(self, transactions_df, customers, most_bought_articles, articles_id_to_idx, customers_id_to_idx):\n        recommendations = []\n        \n        # Compute similarity matrix\n        similarity_matrix = np.dot(self.articles_lat_mat, self.articles_lat_mat.T)\n        norms = np.sqrt(np.sum(self.articles_lat_mat ** 2, axis=1)).reshape(-1, 1)\n        similarity_matrix = similarity_matrix / norms\n        similarity_matrix = similarity_matrix / norms.T\n        \n        # Convert the similarity matrix to a matrix of indices of the 12 most similar items for each item\n        similarity_matrix = np.argsort(similarity_matrix, axis=1)[:, ::-1]\n        similarity_matrix = similarity_matrix[:, :12]\n        \n        top_sold_articles = transactions_df[['article_id', 't_dat']].groupby('article_id').count().reset_index().rename(columns={'t_dat': 'count'})\n        top_sold_articles = top_sold_articles.sort_values(by='count', ascending=False).iloc[:12]\n        top_sold_articles = top_sold_articles.article_id.values\n        \n        default_recommendation = '0' + str(top_sold_articles[0]) + ' ' + ' 0'.join([str(article) for article in top_sold_articles[1:]])\n        \n        last_items = transactions_df.loc[transactions_df.groupby('customer_id').t_dat.idxmax()].article_id.values\n        \n        for customer in tqdm(customers):\n            try:\n                customer_idx = customers_id_to_idx[customer]                    \n                last_article = last_items[customer_idx]\n                article_idx = articles_id_to_idx[last_article]\n                \n                similar_articles_idx = similarity_matrix[article_idx]\n                \n                recommended_articles = most_bought_articles[similar_articles_idx]\n                recommendation = '0' + str(recommended_articles[0]) + ' ' + ' 0'.join([str(article) for article in recommended_articles[1:]])\n            except KeyError as kerr:\n                recommendation = default_recommendation\n            \n            recommendations.append(recommendation)\n        \n        predictions_df = pd.DataFrame({'customer_id': customers, 'prediction': recommendations})\n        \n        return predictions_df\n                \n    ","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:41:11.116712Z","iopub.execute_input":"2022-03-02T04:41:11.116923Z","iopub.status.idle":"2022-03-02T04:41:11.138599Z","shell.execute_reply.started":"2022-03-02T04:41:11.116898Z","shell.execute_reply":"2022-03-02T04:41:11.137913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"recommender = ItemBasedRecommender(train_df, num_articles, num_customers, num_components=1000)\nrecommender.fit(n_epochs=15)","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:41:11.139706Z","iopub.execute_input":"2022-03-02T04:41:11.140474Z","iopub.status.idle":"2022-03-02T04:46:16.158636Z","shell.execute_reply.started":"2022-03-02T04:41:11.140441Z","shell.execute_reply":"2022-03-02T04:46:16.157756Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers = submission_df.customer_id.values\nsubmission = recommender.predict(filtered_transactions_df, customers, most_bought_articles, articles_id_to_idx, customers_id_to_idx)\nsubmission","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:46:16.159948Z","iopub.execute_input":"2022-03-02T04:46:16.160184Z","iopub.status.idle":"2022-03-02T04:47:56.695217Z","shell.execute_reply.started":"2022-03-02T04:46:16.160155Z","shell.execute_reply":"2022-03-02T04:47:56.693405Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-03-02T04:47:56.696565Z","iopub.execute_input":"2022-03-02T04:47:56.696882Z","iopub.status.idle":"2022-03-02T04:48:03.373424Z","shell.execute_reply.started":"2022-03-02T04:47:56.69685Z","shell.execute_reply":"2022-03-02T04:48:03.372686Z"},"trusted":true},"execution_count":null,"outputs":[]}]}