{"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":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport seaborn as sns; sns.set()\n\nfrom collections import Counter, defaultdict\nfrom PIL import Image\nfrom pathlib import Path\npath = Path(\"/kaggle/input/h-and-m-personalized-fashion-recommendations/\")\n\ndef show_images(article_ids, cols=1, rows=-1):\n    if isinstance(article_ids, int) or isinstance(article_ids, str):\n        article_ids = [article_ids]\n    article_count = len(article_ids)\n    if rows < 0: rows = (article_count // cols) + 1\n    plt.figure(figsize=(3 + 3.5 * cols, 3 + 5 * rows))\n    for i in range(article_count):\n        article_id = (\"0\" + str(article_ids[i]))[-10:]\n        plt.subplot(rows, cols, i + 1)\n        plt.axis('off')\n        plt.title(article_id)\n        try:\n            image = Image.open(f\"/kaggle/input/h-and-m-personalized-fashion-recommendations/images/{article_id[:3]}/{article_id}.jpg\")\n            plt.imshow(image)\n        except:\n            pass\n\narticles = pd.read_csv(path / \"articles.csv\", dtype = {'article_id': str})\n\ntrain = pd.read_csv(path / \"transactions_train.csv\", dtype = {'article_id': str})\ntrain = train[[\"t_dat\", \"article_id\", \"sales_channel_id\"]]\ntrain[\"t_dat\"] = pd.to_datetime(train[\"t_dat\"])\ntrain = train.query(\"sales_channel_id == 2\")\ntrain = train.sort_values([\"article_id\", \"t_dat\"], ascending=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:06:04.091752Z","iopub.execute_input":"2022-02-26T10:06:04.092064Z","iopub.status.idle":"2022-02-26T10:07:08.562872Z","shell.execute_reply.started":"2022-02-26T10:06:04.092034Z","shell.execute_reply":"2022-02-26T10:07:08.562105Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sales_counts = Counter(train.article_id)\nfor i in articles.index:\n    articles.at[i, \"sales_count\"] = sales_counts[articles.at[i, \"article_id\"]]\n\nperiod_df = train.groupby([\"article_id\"])[\"t_dat\"].agg(lambda x: (list(x)[0], list(x)[-1])).reset_index()\nperiod_df = period_df.merge(articles[\"article_id\"], how=\"right\")\n\narticles[\"latest\"] = period_df[\"t_dat\"].apply(lambda x: None if pd.isna(x) else x[0])\narticles[\"earliest\"] = period_df[\"t_dat\"].apply(lambda x: None if pd.isna(x) else x[1])\narticles[\"period\"] = (articles.latest.values - articles.earliest).dt.total_seconds() // (60 * 60 * 24)\n\nmonthly_sales = {}\nYM = [201809, 201810]\nwhile YM[0] < 202010:\n    start, end = \"-\".join(map(str, [YM[0] // 100, YM[0] % 100, 1])), \"-\".join(map(str, [YM[1] // 100, YM[1] % 100, 1]))\n    monthly_sales = Counter(train.query(f\"'{start}' <= t_dat < '{end}'\").article_id)\n\n    articles[YM[0]] = 0\n    for i in articles.index:\n        articles.at[i, YM[0]] = monthly_sales[articles.at[i, \"article_id\"]]\n\n    print(\"\\r Done :\", YM[0], end=\"\")\n    YM[0] = YM[1]\n    YM[1] = (YM[1] + 100 - 11) if YM[1] % 100 == 12 else (YM[1] + 1)\n\narticles.iloc[:, 5:].to_csv(\"articles_sales_extension.csv\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:07:08.565215Z","iopub.execute_input":"2022-02-26T10:07:08.565541Z","iopub.status.idle":"2022-02-26T10:08:48.322857Z","shell.execute_reply.started":"2022-02-26T10:07:08.565497Z","shell.execute_reply":"2022-02-26T10:08:48.321968Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Overview","metadata":{}},{"cell_type":"markdown","source":"*Modification: I have limited the data to online transactions.*\n\nIn this notebook, I would like to think about the sales cycle and the seasonality of items.  \nIn the H&M competition, the following two points both seem to be important:  \n1. Articles that were well sold in the last week will be sold well in the next week than the ones that were well sold in the last year but not in the last week.  \n2. A certain number of customers buy the same items repeatedly at H&M stores.  \n\nThese two points may have something to do with the sales cycle.  \nThis is because articles that are not in stock in stores will not be sold well the following week, nor can they be sold repeatedly.  \n\nI hope something in this notebook could be helpful to you.  ","metadata":{}},{"cell_type":"markdown","source":"# Sales Period","metadata":{"execution":{"iopub.status.busy":"2022-02-13T02:42:52.646282Z","iopub.execute_input":"2022-02-13T02:42:52.647378Z","iopub.status.idle":"2022-02-13T02:42:52.65088Z","shell.execute_reply.started":"2022-02-13T02:42:52.647316Z","shell.execute_reply":"2022-02-13T02:42:52.650112Z"}}},{"cell_type":"markdown","source":"The figures below show the distribution of  \n- the earliest purchase date of each item (after Sept. 20, 2018)  \n- the latest purchase date of each item (before Sept. 22, 2020)  \n- the sales period calculated as the number of days between the earliest and the latest purchase dates","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(16, 6))\nplt.subplot(1, 2, 1)\nsns.histplot(x=\"earliest\", hue=\"index_group_name\", multiple=\"stack\", data=articles.query(\"period != 0\"))\nplt.subplot(1, 2, 2)\nsns.histplot(x=\"latest\", hue=\"index_group_name\", multiple=\"stack\", data=articles.query(\"period != 0\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:08:48.324313Z","iopub.execute_input":"2022-02-26T10:08:48.325739Z","iopub.status.idle":"2022-02-26T10:08:50.630763Z","shell.execute_reply.started":"2022-02-26T10:08:48.325692Z","shell.execute_reply":"2022-02-26T10:08:50.629891Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(8, 6))\nsns.histplot(x=\"period\", hue=\"index_group_name\", multiple=\"stack\", data=articles.query(\"period != 0\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:08:50.632538Z","iopub.execute_input":"2022-02-26T10:08:50.633144Z","iopub.status.idle":"2022-02-26T10:08:51.960108Z","shell.execute_reply.started":"2022-02-26T10:08:50.633097Z","shell.execute_reply":"2022-02-26T10:08:51.959264Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Sales Periods of the Monthly Best Selling Items","metadata":{}},{"cell_type":"code","source":"def plot_sales(article_id, imshow=False):\n    plt.figure(figsize=(24, 1.5))\n    plot_df = articles.query(f\"article_id == '{article_id}'\")\n    sns.barplot(x=plot_df.columns[29:], y=list(*plot_df.values)[29:], palette=sns.husl_palette(12))\n    plt.title(\" \".join([\"Monthly Sales of ID :\", article_id, \"    earliest :\", str(plot_df.iloc[0, 27])[:10], \"    latest :\", str(plot_df.iloc[0, 26])[:10]]))\n    if imshow:\n        show_images(articles.article_id[loc])","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:08:51.96298Z","iopub.execute_input":"2022-02-26T10:08:51.963297Z","iopub.status.idle":"2022-02-26T10:08:51.970016Z","shell.execute_reply.started":"2022-02-26T10:08:51.96326Z","shell.execute_reply":"2022-02-26T10:08:51.969041Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = articles.sort_values([202009, \"period\"], ascending=False).head(100)\nlongsellers = temp_df.query(\"300 < period\")\nnewitems = temp_df.query(\"period <= 30\")\ntemp_df[[\"article_id\", \"product_type_name\", \"colour_group_name\", \"period\"]].head(30)","metadata":{"_kg_hide-input":true,"_kg_hide-output":false,"execution":{"iopub.status.busy":"2022-02-26T10:08:51.97153Z","iopub.execute_input":"2022-02-26T10:08:51.972068Z","iopub.status.idle":"2022-02-26T10:08:52.069822Z","shell.execute_reply.started":"2022-02-26T10:08:51.97202Z","shell.execute_reply":"2022-02-26T10:08:52.068869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# - *Longtime Sellers*","metadata":{}},{"cell_type":"markdown","source":"Some of the monthly best selling items are *longtime sellers*, i.e., they were well sold in the whole training period.  \nMany of them are popular product types and have popular dark colors.","metadata":{}},{"cell_type":"code","source":"show_images(list(longsellers.article_id.values[:20]), 10)\nfor article_id in list(longsellers.article_id.values[:5]):\n    plot_sales(article_id)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:08:52.071201Z","iopub.execute_input":"2022-02-26T10:08:52.072103Z","iopub.status.idle":"2022-02-26T10:09:02.538403Z","shell.execute_reply.started":"2022-02-26T10:08:52.072058Z","shell.execute_reply":"2022-02-26T10:09:02.537426Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# - *New Items*","metadata":{}},{"cell_type":"markdown","source":"Another part of the monthly best selling items are *new items*, i.e., they were just launched around September 2020.  \nIt seems to me that many of them are popular seasonal articles and have light colors.","metadata":{}},{"cell_type":"code","source":"show_images(list(newitems.article_id.values[:20]), 10)\nfor article_id in list(newitems.article_id.values[:5]):\n    plot_sales(article_id)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:09:02.539903Z","iopub.execute_input":"2022-02-26T10:09:02.540149Z","iopub.status.idle":"2022-02-26T10:09:13.606749Z","shell.execute_reply.started":"2022-02-26T10:09:02.540119Z","shell.execute_reply":"2022-02-26T10:09:13.605995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# - *Summer Clothing*","metadata":{}},{"cell_type":"markdown","source":"Some articles that sold well in August did not sell at all in September.  \nThey seem to be *summer items*, and probably disappeared from the stores by the end of August.  \n\n2020","metadata":{}},{"cell_type":"code","source":"temp_df = articles.loc[articles[202008] > 500].loc[articles[202009] < 100].sort_values([202008], ascending=False)\nshow_images(list(temp_df.article_id.values[:20]), 10)\nfor article_id in list(temp_df.article_id.values[:5]):\n    plot_sales(article_id)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:09:13.607886Z","iopub.execute_input":"2022-02-26T10:09:13.608107Z","iopub.status.idle":"2022-02-26T10:09:24.239615Z","shell.execute_reply.started":"2022-02-26T10:09:13.60808Z","shell.execute_reply":"2022-02-26T10:09:24.238677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2019","metadata":{}},{"cell_type":"code","source":"temp_df = articles.loc[articles[201908] > 400].loc[articles[201909] < 100].sort_values([201908], ascending=False)\nshow_images(temp_df.article_id.values[0:20], 10)\nfor article_id in list(temp_df.article_id.values[:5]):\n    plot_sales(article_id)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:47.99309Z","iopub.execute_input":"2022-02-26T10:10:47.993734Z","iopub.status.idle":"2022-02-26T10:10:58.539509Z","shell.execute_reply.started":"2022-02-26T10:10:47.993702Z","shell.execute_reply":"2022-02-26T10:10:58.538698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# - *Autumn Clothing*","metadata":{}},{"cell_type":"markdown","source":"Instead, there were a number of items that started selling in September.  \nProbably we can call them *autumn clothing*.  \nThey tend to have autumn-like colours.","metadata":{}},{"cell_type":"markdown","source":"2019","metadata":{}},{"cell_type":"code","source":"temp_df = articles.loc[articles[201908] < 100].loc[articles[201909] > 500].sort_values([201909], ascending=False)\nshow_images(list(temp_df.article_id.values[:20]), 10)\nfor article_id in list(temp_df.article_id.values[:5]):\n    plot_sales(article_id)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:09:32.757444Z","iopub.execute_input":"2022-02-26T10:09:32.757854Z","iopub.status.idle":"2022-02-26T10:09:43.225088Z","shell.execute_reply.started":"2022-02-26T10:09:32.757807Z","shell.execute_reply":"2022-02-26T10:09:43.224143Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# K-means Clustering by Monthly Sales","metadata":{}},{"cell_type":"markdown","source":"The figures below show the results of K-means clustering based on monthly sales of each article.","metadata":{}},{"cell_type":"code","source":"articles = articles.sort_values(by=\"sales_count\")\n\nfrom sklearn import cluster\nn_clusters = 9\nmodel = cluster.KMeans(n_clusters=n_clusters)\nmodel.fit(articles.iloc[:,29:]) # consider the period\n\ndef plot_cluster(k, n=10):\n    temp_df = articles[model.labels_==k]\n    plt.figure(figsize=(24, 1.5))\n    plot_df = temp_df.iloc[:,29:].describe().loc[[\"mean\"]]\n    sns.barplot(x=plot_df.columns, y=list(*plot_df.values), palette=sns.husl_palette(12))\n    plt.title(\" \".join([\"Mean Monthly Sales of Cluster :\", str(k)]))\n    show_images(list(temp_df.article_id.values[:n]), 10)\n    show_images(list(temp_df.article_id.values[-n:]), 10)\n    return temp_df.iloc[:,[0] + [i for i in range(29, 54)]]","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:09:43.22648Z","iopub.execute_input":"2022-02-26T10:09:43.226856Z","iopub.status.idle":"2022-02-26T10:10:00.639761Z","shell.execute_reply.started":"2022-02-26T10:09:43.226813Z","shell.execute_reply":"2022-02-26T10:10:00.638795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(1, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:00.641885Z","iopub.execute_input":"2022-02-26T10:10:00.642617Z","iopub.status.idle":"2022-02-26T10:10:17.240514Z","shell.execute_reply.started":"2022-02-26T10:10:00.642563Z","shell.execute_reply":"2022-02-26T10:10:17.239647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(2, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:17.243012Z","iopub.execute_input":"2022-02-26T10:10:17.243755Z","iopub.status.idle":"2022-02-26T10:10:23.575649Z","shell.execute_reply.started":"2022-02-26T10:10:17.243713Z","shell.execute_reply":"2022-02-26T10:10:23.574821Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(3, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:23.577327Z","iopub.execute_input":"2022-02-26T10:10:23.577934Z","iopub.status.idle":"2022-02-26T10:10:44.15007Z","shell.execute_reply.started":"2022-02-26T10:10:23.577891Z","shell.execute_reply":"2022-02-26T10:10:44.149096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(4, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:44.151318Z","iopub.execute_input":"2022-02-26T10:10:44.151621Z","iopub.status.idle":"2022-02-26T10:10:44.661384Z","shell.execute_reply.started":"2022-02-26T10:10:44.151592Z","shell.execute_reply":"2022-02-26T10:10:44.6601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(5, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:44.662371Z","iopub.status.idle":"2022-02-26T10:10:44.6627Z","shell.execute_reply.started":"2022-02-26T10:10:44.662519Z","shell.execute_reply":"2022-02-26T10:10:44.662535Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(6, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:44.663741Z","iopub.status.idle":"2022-02-26T10:10:44.664032Z","shell.execute_reply.started":"2022-02-26T10:10:44.663874Z","shell.execute_reply":"2022-02-26T10:10:44.663889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(7, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:44.665224Z","iopub.status.idle":"2022-02-26T10:10:44.665499Z","shell.execute_reply.started":"2022-02-26T10:10:44.665355Z","shell.execute_reply":"2022-02-26T10:10:44.66537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(8, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:44.666548Z","iopub.status.idle":"2022-02-26T10:10:44.666866Z","shell.execute_reply.started":"2022-02-26T10:10:44.66672Z","shell.execute_reply":"2022-02-26T10:10:44.666736Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"temp_df = plot_cluster(0, 20)\nfor ID in list(temp_df.head(3).article_id): plot_sales(ID)\nfor ID in list(temp_df.tail(3).article_id): plot_sales(ID)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-02-26T10:10:44.667681Z","iopub.status.idle":"2022-02-26T10:10:44.667951Z","shell.execute_reply.started":"2022-02-26T10:10:44.667812Z","shell.execute_reply":"2022-02-26T10:10:44.667826Z"},"trusted":true},"execution_count":null,"outputs":[]}]}