{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport seaborn as sns\n\ninput_dir = '/kaggle/input/h-and-m-personalized-fashion-recommendations/'","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-03-29T08:43:29.815148Z","iopub.execute_input":"2022-03-29T08:43:29.815515Z","iopub.status.idle":"2022-03-29T08:43:30.834142Z","shell.execute_reply.started":"2022-03-29T08:43:29.815423Z","shell.execute_reply":"2022-03-29T08:43:30.833098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"articles_path = input_dir + 'articles.csv'\narticles = pd.read_csv(articles_path, index_col='article_id')\narticles.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:43:50.214722Z","iopub.execute_input":"2022-03-29T08:43:50.215017Z","iopub.status.idle":"2022-03-29T08:43:51.281922Z","shell.execute_reply.started":"2022-03-29T08:43:50.214984Z","shell.execute_reply":"2022-03-29T08:43:51.281143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"articles.isnull().sum()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:43:59.707558Z","iopub.execute_input":"2022-03-29T08:43:59.708334Z","iopub.status.idle":"2022-03-29T08:43:59.779543Z","shell.execute_reply.started":"2022-03-29T08:43:59.708295Z","shell.execute_reply":"2022-03-29T08:43:59.778843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Note that only the detail_desc column has missing data. Since our approach won't make use of NLP, there is no need to apply a missing values strategy for this dataset","metadata":{}},{"cell_type":"code","source":"articles.nunique()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:47:32.202757Z","iopub.execute_input":"2022-03-29T08:47:32.203058Z","iopub.status.idle":"2022-03-29T08:47:32.335419Z","shell.execute_reply.started":"2022-03-29T08:47:32.203025Z","shell.execute_reply":"2022-03-29T08:47:32.334423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Note that some features are related, basically representing the same thing, but expressed differently (for example, product_type_no, product_type_name, product_group_name). Generally, we have pairs of tuples of features where one of them is a numeric code and the others are names or short descriptions. Note that the numeric columns have more unique values than the others. So we could try using those for our analysis.","metadata":{}},{"cell_type":"code","source":"article_features = ['product_type_no', 'graphical_appearance_no', 'colour_group_code', 'perceived_colour_value_id', 'perceived_colour_master_id',\n                    'department_no', 'index_code', 'index_group_no', 'section_no', 'garment_group_no']\ntrain_articles = articles[article_features]\nobject_cols = [col for col in train_articles.columns if train_articles[col].dtype == 'object']\nprint(object_cols)","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:51:21.979989Z","iopub.execute_input":"2022-03-29T08:51:21.980289Z","iopub.status.idle":"2022-03-29T08:51:21.991048Z","shell.execute_reply.started":"2022-03-29T08:51:21.980256Z","shell.execute_reply":"2022-03-29T08:51:21.99018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Checking whether some of those codes are non-numeric. There is indeed one such column, the 'index_code' one.","metadata":{}},{"cell_type":"code","source":"train_articles['index_code'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:52:07.443601Z","iopub.execute_input":"2022-03-29T08:52:07.44387Z","iopub.status.idle":"2022-03-29T08:52:07.457652Z","shell.execute_reply.started":"2022-03-29T08:52:07.44384Z","shell.execute_reply":"2022-03-29T08:52:07.456716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.preprocessing import OrdinalEncoder\nencoder = OrdinalEncoder()\ntrain_articles[['index_code']] = encoder.fit_transform(articles[['index_code']]).astype(int)\ntrain_articles.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:52:49.423738Z","iopub.execute_input":"2022-03-29T08:52:49.424451Z","iopub.status.idle":"2022-03-29T08:52:49.60405Z","shell.execute_reply.started":"2022-03-29T08:52:49.424413Z","shell.execute_reply":"2022-03-29T08:52:49.603108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We are simply going to encode the index_code column. Another possible way would be a one hot encoding.","metadata":{}},{"cell_type":"code","source":"customers_path = input_dir + 'customers.csv'\ncustomers = pd.read_csv(customers_path)\ncustomers.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:53:15.017872Z","iopub.execute_input":"2022-03-29T08:53:15.018146Z","iopub.status.idle":"2022-03-29T08:53:20.467043Z","shell.execute_reply.started":"2022-03-29T08:53:15.018113Z","shell.execute_reply":"2022-03-29T08:53:20.466312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers.isnull().sum()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:53:24.069969Z","iopub.execute_input":"2022-03-29T08:53:24.070546Z","iopub.status.idle":"2022-03-29T08:53:24.358319Z","shell.execute_reply.started":"2022-03-29T08:53:24.070504Z","shell.execute_reply":"2022-03-29T08:53:24.357389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers['Active'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:53:55.247256Z","iopub.execute_input":"2022-03-29T08:53:55.248151Z","iopub.status.idle":"2022-03-29T08:53:55.268835Z","shell.execute_reply.started":"2022-03-29T08:53:55.248113Z","shell.execute_reply":"2022-03-29T08:53:55.267742Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers['FN'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:54:04.068209Z","iopub.execute_input":"2022-03-29T08:54:04.069093Z","iopub.status.idle":"2022-03-29T08:54:04.089005Z","shell.execute_reply.started":"2022-03-29T08:54:04.069041Z","shell.execute_reply":"2022-03-29T08:54:04.088071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"customers['club_member_status'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:54:21.630298Z","iopub.execute_input":"2022-03-29T08:54:21.630738Z","iopub.status.idle":"2022-03-29T08:54:21.719157Z","shell.execute_reply.started":"2022-03-29T08:54:21.6307Z","shell.execute_reply":"2022-03-29T08:54:21.718383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Note that the FN and Active columns have many missing values and it is not quite clear what those could be replaced with (for example, one might try to replace the Active missing values with 0, suggesting inactivity, however there are many cases where there is a nan Active value but the club_member_status is ACTIVE). As a result, we are just going to drop them. On the other hand, we may assume that the lack of information regarding club_member_status might mean that those customers are not yet active in that club, thus we will replace those nan values with PRE-CREATE.","metadata":{}},{"cell_type":"code","source":"customers['fashion_news_frequency'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:57:24.806662Z","iopub.execute_input":"2022-03-29T08:57:24.807091Z","iopub.status.idle":"2022-03-29T08:57:24.901069Z","shell.execute_reply.started":"2022-03-29T08:57:24.80706Z","shell.execute_reply":"2022-03-29T08:57:24.900125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can also assume that the missing values in fashion_news_frequency column correspond to NONE values, so we're going to replace them like that.","metadata":{}},{"cell_type":"code","source":"from sklearn.impute import SimpleImputer\n\nfeatures = ['customer_id', 'club_member_status', 'fashion_news_frequency', 'age']\n\ntrain_customers = customers[features]\ntrain_customers['club_member_status'] = customers['club_member_status'].fillna('PRE-CREATE').map(\n    {'LEFT CLUB': 0, 'PRE-CREATE': 1, 'ACTIVE': 2}).astype(int)\ntrain_customers['fashion_news_frequency'] = customers['fashion_news_frequency'].copy().fillna('NONE').map(\n    {'NONE': 0, 'None': 0, 'Monthly': 1, 'Regularly': 2}).astype(int)\n\nimputer = SimpleImputer(strategy='mean').fit(customers[['age']])\ntrain_customers[['age']] = imputer.transform(train_customers[['age']])\ntrain_customers = train_customers.set_index('customer_id')\ntrain_customers.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:58:30.219759Z","iopub.execute_input":"2022-03-29T08:58:30.220036Z","iopub.status.idle":"2022-03-29T08:58:30.954315Z","shell.execute_reply.started":"2022-03-29T08:58:30.220007Z","shell.execute_reply":"2022-03-29T08:58:30.95343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transactions_path = input_dir + 'transactions_train.csv'\ntransactions = pd.read_csv(transactions_path, parse_dates=['t_dat'])\ntransactions.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T08:58:46.752187Z","iopub.execute_input":"2022-03-29T08:58:46.752488Z","iopub.status.idle":"2022-03-29T08:59:53.686828Z","shell.execute_reply.started":"2022-03-29T08:58:46.752456Z","shell.execute_reply":"2022-03-29T08:59:53.685799Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transactions.isnull().sum()","metadata":{"execution":{"iopub.status.busy":"2022-03-29T09:00:07.867773Z","iopub.execute_input":"2022-03-29T09:00:07.868121Z","iopub.status.idle":"2022-03-29T09:00:09.541337Z","shell.execute_reply.started":"2022-03-29T09:00:07.868083Z","shell.execute_reply":"2022-03-29T09:00:09.540668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There are no missing values here, so no need for any imputation","metadata":{}},{"cell_type":"code","source":"articles_by_customers = transactions.groupby('customer_id').article_id.apply(set).to_dict()\narticles_popularity = transactions.groupby('article_id').customer_id.nunique().sort_values(ascending=False)","metadata":{"execution":{"iopub.status.busy":"2022-03-29T09:01:11.891449Z","iopub.execute_input":"2022-03-29T09:01:11.891915Z","iopub.status.idle":"2022-03-29T09:02:19.855722Z","shell.execute_reply.started":"2022-03-29T09:01:11.891884Z","shell.execute_reply":"2022-03-29T09:02:19.854931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We are trying to create a simple measure for an article's popularity, more precisely how many customers have ever bought one such article (we could have tried the total number of times an article has been bought regardless of how many times the same customer bought it, but we may encounter bias by doing so, for example we might have one customer buying one article many times and thus skew the measurement) ","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import StandardScaler\nscaled_customers = pd.DataFrame(StandardScaler().fit_transform(train_customers), index=train_customers.index, columns=train_customers.columns)\ndisplay(scaled_customers.head())\n\nscaled_articles = pd.DataFrame(StandardScaler().fit_transform(train_articles), index=train_articles.index, columns=train_articles.columns)\ndisplay(scaled_articles.head())","metadata":{"execution":{"iopub.status.busy":"2022-03-29T09:04:23.514022Z","iopub.execute_input":"2022-03-29T09:04:23.514313Z","iopub.status.idle":"2022-03-29T09:04:23.68769Z","shell.execute_reply.started":"2022-03-29T09:04:23.514285Z","shell.execute_reply":"2022-03-29T09:04:23.686758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Our approach will use the NearestNeighbors algorithm, because of that, even though our data is mostly discrete, rather than continuous, it makes sense to scale it, otherwise the distances computated by NN may be heavily influenced by a subset of features with wider ranges. ","metadata":{}},{"cell_type":"code","source":"scaled_customers.to_csv('scaled_customers.csv')","metadata":{"execution":{"iopub.status.busy":"2022-03-29T09:10:05.252484Z","iopub.execute_input":"2022-03-29T09:10:05.252816Z","iopub.status.idle":"2022-03-29T09:10:13.147196Z","shell.execute_reply.started":"2022-03-29T09:10:05.252782Z","shell.execute_reply":"2022-03-29T09:10:13.146289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scaled_articles.to_csv('scaled_articles.csv')","metadata":{"execution":{"iopub.status.busy":"2022-03-29T09:10:42.660805Z","iopub.execute_input":"2022-03-29T09:10:42.661112Z","iopub.status.idle":"2022-03-29T09:10:44.21237Z","shell.execute_reply.started":"2022-03-29T09:10:42.661078Z","shell.execute_reply":"2022-03-29T09:10:44.211511Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}