{"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":"# Customer Compact Visualization!\n\nHi guys, this notebook can be used to understand customers on \"individual-level\" - \nin complimentary to most excellent notebooks we have, which more focus on macro EDA / macro properties of the dataset.\n\n## Usage\nI think this notebook can be used for explanable **\"error-analysis\"**. I.e. after making predictions, you can have a small eye-ball validation-set where you can investigate the recommendation performance manually. The error for some cases may be sensible e.g. a customer who buy totally random stuffs, **or non-sensible** e.g. a customer who has exact pattern of what to buy, but your model still guess them wrong.\n\nIn the latter case, this micro EDA/error-analysis can be help to adjust the model to be more sensible toward the easy-but-failed cases.","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport glob\n\nfrom tqdm import tqdm\nimport datetime\n\nimport matplotlib.image as mpimg\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2022-03-05T09:46:45.794521Z","iopub.execute_input":"2022-03-05T09:46:45.795186Z","iopub.status.idle":"2022-03-05T09:46:46.839375Z","shell.execute_reply.started":"2022-03-05T09:46:45.795152Z","shell.execute_reply":"2022-03-05T09:46:46.838518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Helper functions\nBelow is the helper function to show a list of arbitrary number of images.","metadata":{}},{"cell_type":"code","source":"from IPython.display import display\n\ndef show_image(image,figsize=None,title=None):\n    \n    if figsize is not None:\n        fig = plt.figure(figsize=figsize)\n    \n    if isinstance(image, str):\n        try:\n            image = mpimg.imread(f'../input/h-and-m-personalized-fashion-recommendations/images/{str(image)[:3]}/0{int(image)}.jpg')\n        except:\n            image = np.zeros([16,16,3])\n    \n    if image.ndim == 2:\n        plt.imshow(image,cmap='gray')\n    else:\n        plt.imshow(image)\n            \ndef show_Nimages_all(imgs,scale=1,titles=None):\n\n    N=len(imgs)\n    fig = plt.figure(figsize=(25/scale, 16/scale))\n    for i, img in enumerate(imgs):\n        ax = fig.add_subplot(1, N, i + 1, xticks=[], yticks=[])\n        show_image(img)\n        if titles is not None:\n            title = titles[i]\n            ax.title.set_text(title)\n    plt.show()\n\ndef show_Nimages(imgs, num_per_row=10, scale=1, titles=None):\n\n    N=len(imgs)\n    current=0\n    remaining=N\n    while remaining > num_per_row:\n        images = imgs[current:current+10]\n        \n        current_titles = None\n        if titles is not None:\n            current_titles = titles[current:current+10]\n            \n        show_Nimages_all(images, scale, current_titles)\n        \n        remaining -= num_per_row\n        try: \n            imgs = imgs[current+10:]\n            titles = titles[current+10:]\n        except: pass\n    \n    if len(imgs) > 0:\n        show_Nimages_all(imgs, scale, titles)","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2022-03-05T09:46:46.840958Z","iopub.execute_input":"2022-03-05T09:46:46.841188Z","iopub.status.idle":"2022-03-05T09:46:46.857490Z","shell.execute_reply.started":"2022-03-05T09:46:46.841158Z","shell.execute_reply":"2022-03-05T09:46:46.856474Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Next is the helper and main function used for print information of each customer. To use this function, just call `print_customer()` as shown below.","metadata":{}},{"cell_type":"code","source":"def make_titles(titles, prices):\n    new_titles = []\n    assert len(titles) == len(prices)\n    \n    for i in range(len(titles)):\n        new_titles.append(f'{titles[i]}:{prices[i]:.2f}')\n    return new_titles\n\ndef print_customer(data, articles_df, cus_id, repeat_threshold=3):\n    cus = cus_id\n\n    cus_df = data.query('customer_id == @cus')\n    print(f'\\n** Total items bought = {cus_df.shape[0]}**\\n')\n\n    titles = cus_df.t_dat.values\n    prices = cus_df.price.values\n    plt.plot(prices)\n    plt.title(f'average price = {prices.mean():.3f}, std = {prices.std():.3f}')\n    \n#     print('sales_channel_id')\n    print(f'channel_id:', cus_df['sales_channel_id'].value_counts(),'\\n')\n    \n    # favorite colors, repeated sales\n    article_id_str = cus_df['article_id'].values\n    cus_df.loc[:,'article_id'] = cus_df['article_id'].apply(lambda x: int(x))\n    cus_df = pd.merge(cus_df, articles_df, how=\"left\", on=[\"article_id\"])\n    cus_df.loc[:,'article_id'] = article_id_str\n        \n#     print(cus_df['perceived_colour_master_name'].value_counts(normalize=True)[:3])\n    \n    f, ax = plt.subplots(figsize=(10, 6))\n    ax = sns.histplot(data=cus_df, y='perceived_colour_master_name', hue='prod_name', multiple=\"stack\")\n    ax.set_xlabel('Count by color')\n    ax.set_ylabel('Favorite Colors')\n    plt.show()\n    \n    product_repeated_num = cus_df['prod_name'].value_counts(normalize=False)\n    if product_repeated_num[0] >= repeat_threshold:\n        print('** LOVE buy repeating items **')\n        print(product_repeated_num[:3])\n    else:\n        print('** DONT love buy repeating items **')\n    \n    titles  = make_titles(titles, prices)\n    \n    images = cus_df.article_id.values\n    show_Nimages(images,titles=titles)\n","metadata":{"_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2022-03-05T09:46:47.062307Z","iopub.execute_input":"2022-03-05T09:46:47.063157Z","iopub.status.idle":"2022-03-05T09:46:47.078546Z","shell.execute_reply.started":"2022-03-05T09:46:47.063117Z","shell.execute_reply":"2022-03-05T09:46:47.077489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\ndata_df = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/transactions_train.csv\", dtype={'article_id':str})\nprint(data_df.shape)\ndata_df.head()\n\narticles_df = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/articles.csv\")\ncustomers_df = pd.read_csv(\"../input/h-and-m-personalized-fashion-recommendations/customers.csv\")\n\nall_cus = data_df['customer_id'].unique()\nprint(len(all_cus))","metadata":{"execution":{"iopub.status.busy":"2022-03-05T09:46:47.605423Z","iopub.execute_input":"2022-03-05T09:46:47.606000Z","iopub.status.idle":"2022-03-05T09:48:06.813968Z","shell.execute_reply.started":"2022-03-05T09:46:47.605974Z","shell.execute_reply":"2022-03-05T09:48:06.813195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Customer Visualization\nVisualize of customer #0 who bought 18 items, and has pattern to buy black-color stuffs, and love long-sleeve shirt.","metadata":{}},{"cell_type":"code","source":"cus = all_cus[0]\nprint_customer(data_df, articles_df, cus)","metadata":{"execution":{"iopub.status.busy":"2022-03-05T09:48:15.050525Z","iopub.execute_input":"2022-03-05T09:48:15.051176Z","iopub.status.idle":"2022-03-05T09:48:23.501296Z","shell.execute_reply.started":"2022-03-05T09:48:15.051132Z","shell.execute_reply":"2022-03-05T09:48:23.499885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Visualize of customer #10 who bought 30 items during the past 2 years, with some gray and pink stuffs. She seem loves to repeatly buy the same products.","metadata":{}},{"cell_type":"code","source":"cus = all_cus[10]\nprint_customer(data_df, articles_df, cus)","metadata":{"execution":{"iopub.status.busy":"2022-03-05T09:48:23.502515Z","iopub.execute_input":"2022-03-05T09:48:23.502705Z","iopub.status.idle":"2022-03-05T09:48:32.856010Z","shell.execute_reply.started":"2022-03-05T09:48:23.502678Z","shell.execute_reply":"2022-03-05T09:48:32.855472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Customer #1000 is a fan of H&M and bought 72 items during the last 2 years. She used both channel 1 and channel 2 (online and offline ??).\nHer favorite color is obviously black. She bought lots of diversed items, so her decision next week is not easy to guess (except that the items should be black). So if the model failed on this case, you should not be worried too much.","metadata":{}},{"cell_type":"code","source":"cus = all_cus[1000]\nprint_customer(data_df, articles_df, cus)","metadata":{"execution":{"iopub.status.busy":"2022-03-05T09:48:32.857085Z","iopub.execute_input":"2022-03-05T09:48:32.857361Z","iopub.status.idle":"2022-03-05T09:48:59.414611Z","shell.execute_reply.started":"2022-03-05T09:48:32.857334Z","shell.execute_reply":"2022-03-05T09:48:59.413718Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}