{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":31254,"databundleVersionId":3103714,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"#### Key Insights From EDA","metadata":{}},{"cell_type":"markdown","source":"1. Number of transactions follow patterns that are seasonal in nature. Consequently, the predictions for the same customer could differ based on the window being considered.\n2. Repeat customer rate is high and previous preferences of the customer would be crucial in recommending articles.\n3. Top selling items across age-groups vary widely.","metadata":{}},{"cell_type":"code","source":"# Libraries\nimport os\nimport gc\nimport wandb\nimport time\nimport random\nimport math\nimport glob\nfrom scipy import spatial\nfrom tqdm import tqdm\nimport warnings\nimport cv2\nimport pandas as pd\nimport numpy as np\nfrom numpy import dot, sqrt\nimport seaborn as sns\nimport matplotlib as mpl\nimport matplotlib.patches as patches\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nfrom matplotlib.offsetbox import AnnotationBbox, OffsetImage\nfrom IPython.display import display_html\nfrom wordcloud import WordCloud, STOPWORDS\nfrom PIL import Image\nplt.rcParams.update({'font.size': 16})\n\n# Environment check\nwarnings.filterwarnings(\"ignore\")\nos.environ[\"WANDB_SILENT\"] = \"true\"\nCONFIG = {'competition': 'HandM', '_wandb_kernel': 'aot'}\n\n# Custom colors\nclass clr:\n    S = '\\033[1m' + '\\033[95m'\n    E = '\\033[0m'\n    \nmy_colors = [\"#AF0848\", \"#E90B60\", \"#CB2170\", \"#954E93\", \"#705D98\", \"#5573A8\", \"#398BBB\", \"#00BDE3\"]","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:35:17.934121Z","iopub.execute_input":"2025-08-08T01:35:17.934390Z","iopub.status.idle":"2025-08-08T01:35:22.760665Z","shell.execute_reply.started":"2025-08-08T01:35:17.934370Z","shell.execute_reply":"2025-08-08T01:35:22.759760Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pd.set_option('display.max_columns', None)\npd.set_option('display.max_rows', None)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:35:22.762081Z","iopub.execute_input":"2025-08-08T01:35:22.762446Z","iopub.status.idle":"2025-08-08T01:35:22.767628Z","shell.execute_reply.started":"2025-08-08T01:35:22.762427Z","shell.execute_reply":"2025-08-08T01:35:22.766124Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transactions = pd.read_csv('../input/h-and-m-personalized-fashion-recommendations/transactions_train.csv')\n# Let's convert back to parquet and load it in parquet format\ntransactions.to_parquet('transactions.parquet')\ntransactions_parquet = pd.read_parquet('./transactions.parquet')\ndel transactions # to save space\n\narticles = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/articles.csv')\ncustomers = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/customers.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:35:22.769120Z","iopub.execute_input":"2025-08-08T01:35:22.769551Z","iopub.status.idle":"2025-08-08T01:37:02.543484Z","shell.execute_reply.started":"2025-08-08T01:35:22.769516Z","shell.execute_reply":"2025-08-08T01:37:02.540707Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Memory Optimization","metadata":{}},{"cell_type":"markdown","source":"Convert the columns customer_id and article_id from string to integers to save space. \n1. customer_id is stored as a 64 byte string; converting it to int64 (8 bytes) ==> saves 8x space.\n2. article_id is stored as a 10 byte string. Remove the leading 0 and convert it to int32 (4 Bytes) ==> saves 2.5x space","metadata":{}},{"cell_type":"code","source":"transactions_parquet['customer_id2'] =\\\n    transactions_parquet['customer_id'].apply(lambda x: int(x[-16:],16) ).astype('int64')\n\ncustomers['customer_id'] = customers['customer_id'].apply(lambda x: int(x[-16:], 16) ).astype('int64')\n\nprint(transactions_parquet['customer_id2'].nunique())\nprint(transactions_parquet['customer_id'].nunique())\n\ntransactions_parquet.drop(['customer_id'], axis=1, inplace=True)\ntransactions_parquet.rename(columns={'customer_id2': 'customer_id'}, inplace=True)\ntransactions_parquet.head(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:02.547204Z","iopub.execute_input":"2025-08-08T01:37:02.548109Z","iopub.status.idle":"2025-08-08T01:37:32.900436Z","shell.execute_reply.started":"2025-08-08T01:37:02.548077Z","shell.execute_reply":"2025-08-08T01:37:32.899285Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Quick check to ensure 1-1 mapping after converting to int32","metadata":{}},{"cell_type":"code","source":"transactions_parquet['article_id2'] = transactions_parquet['article_id'].astype('int32')\n# Quick check to ensure 1-1 mapping after converting to int32\nprint(transactions_parquet['article_id2'].nunique())\nprint(transactions_parquet['article_id'].nunique())\n\ntransactions_parquet.rename({'article_id2':'article_id'}, inplace=True)\narticles['article_id'] = articles['article_id'].astype('int32')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:32.901639Z","iopub.execute_input":"2025-08-08T01:37:32.901975Z","iopub.status.idle":"2025-08-08T01:37:47.587441Z","shell.execute_reply.started":"2025-08-08T01:37:32.901945Z","shell.execute_reply":"2025-08-08T01:37:47.586334Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# EDA","metadata":{}},{"cell_type":"code","source":"print(clr.S+\"ARTICLES:\"+clr.E, articles.shape)\ndisplay_html(articles.head(3))\nprint(\"\\n\", clr.S+\"CUSTOMERS:\"+clr.E, customers.shape)\ndisplay_html(customers.head(3))\nprint(\"\\n\", clr.S+\"TRANSACTIONS:\"+clr.E, transactions_parquet.shape)\ndisplay_html(transactions_parquet.head(3))\n\nprint(\"\\n\", clr.S + \"Number of unique customers =\" + clr.E, transactions_parquet['customer_id'].nunique())\nprint(\"\\n\", clr.S + \"Number of unique articles purchased = \" + clr.E, transactions_parquet['article_id'].nunique())\nprint(\"\\n\", clr.S + \"Number of transactions = \" + clr.E, transactions_parquet.shape[0])\n\n\na = transactions_parquet['sales_channel_id'].value_counts()\nprint(\"\\n\", clr.S + \"Pct of Channel 1 purchases = \" + clr.E, round((a[1]*100/(a[1]+a[2])), 3), \"%\")\nprint(\"\\n\", clr.S + \"Pct of Channel 2 purchases = \" + clr.E, round((a[2]*100.0/(a[1]+a[2])), 3), \"%\")\n\nprint(\"\\n\", clr.S + \"Start Date:\" + clr.E, transactions_parquet['t_dat'].min())\nprint(\"\\n\", clr.S + \"End Date:\" + clr.E, transactions_parquet['t_dat'].max())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:47.588500Z","iopub.execute_input":"2025-08-08T01:37:47.588959Z","iopub.status.idle":"2025-08-08T01:37:52.051518Z","shell.execute_reply.started":"2025-08-08T01:37:47.588923Z","shell.execute_reply":"2025-08-08T01:37:52.050607Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Articles - EDA","metadata":{}},{"cell_type":"markdown","source":"##### Helper Functions ","metadata":{}},{"cell_type":"code","source":"def adjust_id(x):\n    '''Adjusts article ID code.'''\n    x = str(x)\n    if len(x) == 9:\n        x = \"0\"+x\n    \n    return x\n\ndef insert_image(path, zoom, xybox, ax):\n    '''Insert an image within matplotlib'''\n    imagebox = OffsetImage(mpimg.imread(path), zoom=zoom)\n    ab = AnnotationBbox(imagebox, xy=(0.5, 0.7), frameon=False, pad=1, xybox=xybox)\n    ax.add_artist(ab)\n\ndef show_values_on_bars(axs, h_v=\"v\", space=0.4):\n    '''Plots the value at the end of the a seaborn barplot.\n    axs: the ax of the plot\n    h_v: whether or not the barplot is vertical/ horizontal'''\n    \n    def _show_on_single_plot(ax):\n        if h_v == \"v\":\n            for p in ax.patches:\n                _x = p.get_x() + p.get_width() / 2\n                _y = p.get_y() + p.get_height()\n                value = int(p.get_height())\n                ax.text(_x, _y, format(value, ','), ha=\"center\") \n        elif h_v == \"h\":\n            for p in ax.patches:\n                _x = p.get_x() + p.get_width() + float(space)\n                _y = p.get_y() + p.get_height()\n                value = int(p.get_width())\n                ax.text(_x, _y, format(value, ','), ha=\"left\")\n\n    if isinstance(axs, np.ndarray):\n        for idx, ax in np.ndenumerate(axs):\n            _show_on_single_plot(ax)\n    else:\n        _show_on_single_plot(axs)\n\ndef convert_to_date(s):\n    \"\"\"\n    Memoization technique - very fast conversion to pure python dates\n    \"\"\"\n    dates = {date:datetime.datetime.strptime(date,'%Y-%m') for date in s.unique()}\n    return s.map(dates)\n\n\ndef plot_values( df, col):\n\n    print(clr.S+ f\"Total Number of unique {col} values:\"+clr.E, df[col].nunique())\n\n    # Data\n    val_cnt = df[col].value_counts().reset_index().head(15)\n    clrs = ['#954E93' for x in val_cnt[col]]\n    # clrs = [\"#CB2170\" if x==max(val_cnt[col]) else '#954E93' for x in val_cnt[col]]\n    \n    \n    # Plot\n    fig, ax = plt.subplots(figsize=(25, 13))\n    plt.title(f'- Most Frequent {col} values -', size=22, weight=\"bold\")\n    \n    sns.barplot(data=val_cnt, x=\"count\", y=col, ax=ax,\n                palette=clrs)\n\n    show_values_on_bars(ax, h_v=\"h\")\n    \n    x0,x1 = ax.get_xlim()\n    y0,y1 = ax.get_ylim()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:52.052417Z","iopub.execute_input":"2025-08-08T01:37:52.052675Z","iopub.status.idle":"2025-08-08T01:37:52.067791Z","shell.execute_reply.started":"2025-08-08T01:37:52.052654Z","shell.execute_reply":"2025-08-08T01:37:52.066643Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Missing Value Check","metadata":{}},{"cell_type":"code","source":"articles.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:52.069543Z","iopub.execute_input":"2025-08-08T01:37:52.070025Z","iopub.status.idle":"2025-08-08T01:37:52.157198Z","shell.execute_reply.started":"2025-08-08T01:37:52.069993Z","shell.execute_reply":"2025-08-08T01:37:52.155962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(clr.S+\"There are no missing values in any columns but 'Detail Description':\"+clr.E,\n      articles.isna().sum()[-1], \"total missing values\")\n# Replace missing values\narticles.fillna(value=\"No Description\", inplace=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:52.158535Z","iopub.execute_input":"2025-08-08T01:37:52.158857Z","iopub.status.idle":"2025-08-08T01:37:52.345602Z","shell.execute_reply.started":"2025-08-08T01:37:52.158820Z","shell.execute_reply":"2025-08-08T01:37:52.344524Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"analyse_cols = ['prod_name', 'product_type_name', 'graphical_appearance_name', 'colour_group_name', 'perceived_colour_value_name',\n                    'department_name', 'index_name', 'index_group_name', 'section_name', 'garment_group_name']\n\nfor col in analyse_cols:\n    plot_values( articles, col)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:52.349004Z","iopub.execute_input":"2025-08-08T01:37:52.349264Z","iopub.status.idle":"2025-08-08T01:37:56.566159Z","shell.execute_reply.started":"2025-08-08T01:37:52.349245Z","shell.execute_reply":"2025-08-08T01:37:56.565203Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"text = ' '.join(articles['detail_desc'].tolist())\n\n# Generate the word cloud\nwordcloud = WordCloud(width=800, height=400, background_color='black').generate(text)\n\n# Display the word cloud\nplt.figure(figsize=(10, 10))\nplt.imshow(wordcloud, interpolation='bilinear')\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:37:56.567304Z","iopub.execute_input":"2025-08-08T01:37:56.568037Z","iopub.status.idle":"2025-08-08T01:38:04.626365Z","shell.execute_reply.started":"2025-08-08T01:37:56.568005Z","shell.execute_reply":"2025-08-08T01:38:04.624860Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Transactions - EDA","metadata":{}},{"cell_type":"markdown","source":"1. Transaction volumes fell in the first half of 2020 (Covid) and picked up in the second half.\n2. Every month has at least a million transactions. The month of June is the busiest.\n4. Number of daily transactions fluctuates between 25,000 and 50,000.\n5. Spikes in daily transaction volumes could be due to festivals and sales.\n6. Sales 1 - sales 2 split is 30-70.\n7. Distribution of transaction amounts is skewed towards lower values --> Lower valued products sell more.\n8. Repeat customer rate is high - 130K customers have made only one purchase, 40K customers with 10 purchases and 20K.","metadata":{}},{"cell_type":"code","source":"transactions_parquet.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:38:04.627820Z","iopub.execute_input":"2025-08-08T01:38:04.628251Z","iopub.status.idle":"2025-08-08T01:38:04.646964Z","shell.execute_reply.started":"2025-08-08T01:38:04.628219Z","shell.execute_reply":"2025-08-08T01:38:04.643921Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tran_count = transactions_parquet.groupby('t_dat')['customer_id'].count().reset_index().rename(columns = {'customer_id' : 'count'})\n\nfig, ax = plt.subplots(figsize=(25, 13))\nplt.title('Number of Transactions by Date', size=22, weight=\"bold\")\n\nsns.lineplot(x='t_dat', y='count', data=tran_count)\nplt.xticks(np.arange(0, len(tran_count), 30), rotation=90)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:38:04.648086Z","iopub.execute_input":"2025-08-08T01:38:04.648510Z","iopub.status.idle":"2025-08-08T01:38:13.139907Z","shell.execute_reply.started":"2025-08-08T01:38:04.648482Z","shell.execute_reply":"2025-08-08T01:38:13.138720Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tran_count['t_dat'] = pd.to_datetime(tran_count['t_dat'])\n\ntran_count['year_month'] = tran_count['t_dat'].dt.to_period('M')\nmonthly_vol = tran_count.groupby('year_month')['count'].sum().reset_index()\n# monthly_vol['year_month'] = monthly_vol['year_month'].dt.to_timestamp()\n\nfig, ax = plt.subplots(figsize=(25, 13))\nsns.barplot(x='year_month', y='count', data=monthly_vol, ax=ax)\nplt.title('Monthly Transaction Volume', fontsize=18, fontweight='bold')\nplt.xlabel('Month')\nplt.ylabel('Number of Transactions')\nplt.xticks(rotation=90)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:38:13.141026Z","iopub.execute_input":"2025-08-08T01:38:13.141276Z","iopub.status.idle":"2025-08-08T01:38:13.714008Z","shell.execute_reply.started":"2025-08-08T01:38:13.141258Z","shell.execute_reply":"2025-08-08T01:38:13.712987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"purchase_counts = transactions_parquet['customer_id'].value_counts().reset_index()\n\nplt.figure(figsize=(10,6))\nsns.histplot(purchase_counts, bins=range(1, purchase_counts['count'].max()+2), kde=False)\nplt.xlabel('Number of Purchases per Customer')\nplt.ylabel('Number of Customers')\nplt.title('Distribution of Purchase Frequency per Customer')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:38:13.715233Z","iopub.execute_input":"2025-08-08T01:38:13.715738Z","iopub.status.idle":"2025-08-08T01:38:25.063339Z","shell.execute_reply.started":"2025-08-08T01:38:13.715708Z","shell.execute_reply":"2025-08-08T01:38:25.062185Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tmp = purchase_counts[purchase_counts['count']<=20].groupby('count')['customer_id'].count().reset_index().rename(columns={'count':'num_purchases', 'customer_id':'num_customers'})\nplt.figure(figsize=(10,6))\nsns.barplot(tmp, x='num_purchases', y='num_customers')\nplt.xlabel('Number of < 20 Purchases per Customer')\nplt.ylabel('Number of Customers')\nplt.title('Distribution of Purchase Frequency per Customer')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:38:25.064504Z","iopub.execute_input":"2025-08-08T01:38:25.064790Z","iopub.status.idle":"2025-08-08T01:38:25.345896Z","shell.execute_reply.started":"2025-08-08T01:38:25.064770Z","shell.execute_reply":"2025-08-08T01:38:25.344661Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(10,6))\nax = sns.countplot(x = 'sales_channel_id', data=transactions_parquet)\nplt.xlabel('Sales Channel')\nplt.ylabel('Number of Customers')\nplt.title('Transactions by Sales Channel')\n\nfor container in ax.containers:\n    ax.bar_label(container, labels=[f'{int(v):,}' for v in container.datavalues])\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:38:25.347087Z","iopub.execute_input":"2025-08-08T01:38:25.347441Z","iopub.status.idle":"2025-08-08T01:38:28.495040Z","shell.execute_reply.started":"2025-08-08T01:38:25.347416Z","shell.execute_reply":"2025-08-08T01:38:28.494020Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(10, 6))\nsns.histplot(data=transactions_parquet, x='price', bins=50, kde=True)\nplt.title('Distribution of Transaction Prices')\nplt.xlabel('Price')\nplt.ylabel('Frequency')\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:38:28.495956Z","iopub.execute_input":"2025-08-08T01:38:28.496575Z","iopub.status.idle":"2025-08-08T01:40:05.659258Z","shell.execute_reply.started":"2025-08-08T01:38:28.496537Z","shell.execute_reply":"2025-08-08T01:40:05.658114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Customers EDA","metadata":{}},{"cell_type":"code","source":"customers.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:40:05.660710Z","iopub.execute_input":"2025-08-08T01:40:05.661083Z","iopub.status.idle":"2025-08-08T01:40:05.673804Z","shell.execute_reply.started":"2025-08-08T01:40:05.661059Z","shell.execute_reply":"2025-08-08T01:40:05.672739Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"customers.isna().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:40:05.675062Z","iopub.execute_input":"2025-08-08T01:40:05.675526Z","iopub.status.idle":"2025-08-08T01:40:06.075990Z","shell.execute_reply.started":"2025-08-08T01:40:05.675498Z","shell.execute_reply":"2025-08-08T01:40:06.075119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(customers['FN'].value_counts(dropna=False))\nprint(customers['Active'].value_counts(dropna=False))\nprint(customers['club_member_status'].value_counts(dropna=False))\nprint(customers['fashion_news_frequency'].value_counts(dropna=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:40:06.076963Z","iopub.execute_input":"2025-08-08T01:40:06.077219Z","iopub.status.idle":"2025-08-08T01:40:06.193832Z","shell.execute_reply.started":"2025-08-08T01:40:06.077200Z","shell.execute_reply":"2025-08-08T01:40:06.192471Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fill FN and Active - the only available value is \"1\"\ncustomers[\"FN\"].fillna(0, inplace=True)\ncustomers[\"Active\"].fillna(0, inplace=True)\n\n# Set unknown the club member status & news frequency\ncustomers[\"club_member_status\"].fillna(\"UNKNOWN\", inplace=True)\n\ncustomers[\"fashion_news_frequency\"] = customers[\"fashion_news_frequency\"].replace({\"None\":\"NONE\"})\ncustomers[\"fashion_news_frequency\"].fillna(\"UNKNOWN\", inplace=True)\n\n# Set missing values in age with the median\ncustomers[\"age\"].fillna(customers[\"age\"].median(), inplace=True)\n\nprint(customers.isna().sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:40:06.194741Z","iopub.execute_input":"2025-08-08T01:40:06.195002Z","iopub.status.idle":"2025-08-08T01:40:06.746036Z","shell.execute_reply.started":"2025-08-08T01:40:06.194985Z","shell.execute_reply":"2025-08-08T01:40:06.744933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_values( df, col):\n\n    print(clr.S+ f\"Total Number of unique {col} values:\"+clr.E, df[col].nunique())\n\n    # Data\n    val_cnt = df[col].value_counts().reset_index().head(15)\n    clrs = ['#954E93' for x in val_cnt[col]]\n    # clrs = [\"#CB2170\" if x==max(val_cnt[col]) else '#954E93' for x in val_cnt[col]]\n    \n    \n    # Plot\n    fig, ax = plt.subplots(figsize=(25, 13))\n    plt.title(f'- Most Frequent {col} values -', size=22, weight=\"bold\")\n    \n    sns.barplot(data=val_cnt, x=\"count\", y=col, ax=ax,\n                palette=clrs)\n\n    show_values_on_bars(ax, h_v=\"h\")\n    \n    x0,x1 = ax.get_xlim()\n    y0,y1 = ax.get_ylim()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:40:06.747000Z","iopub.execute_input":"2025-08-08T01:40:06.747325Z","iopub.status.idle":"2025-08-08T01:40:06.754583Z","shell.execute_reply.started":"2025-08-08T01:40:06.747290Z","shell.execute_reply":"2025-08-08T01:40:06.753664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"customers.dtypes","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:51:36.717992Z","iopub.execute_input":"2025-08-08T01:51:36.719074Z","iopub.status.idle":"2025-08-08T01:51:36.726638Z","shell.execute_reply.started":"2025-08-08T01:51:36.719042Z","shell.execute_reply":"2025-08-08T01:51:36.725298Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cust2 = customers.drop(['customer_id', 'postal_code', 'age'], axis=1)\ncust2.value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:53:13.604650Z","iopub.execute_input":"2025-08-08T01:53:13.604988Z","iopub.status.idle":"2025-08-08T01:53:13.848034Z","shell.execute_reply.started":"2025-08-08T01:53:13.604969Z","shell.execute_reply":"2025-08-08T01:53:13.847237Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Combined DataFrame","metadata":{}},{"cell_type":"code","source":"articles['article_id'] = articles['article_id'].astype('int64')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:44:58.270042Z","iopub.execute_input":"2025-08-08T01:44:58.270317Z","iopub.status.idle":"2025-08-08T01:44:58.276122Z","shell.execute_reply.started":"2025-08-08T01:44:58.270297Z","shell.execute_reply":"2025-08-08T01:44:58.275136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"combined_df1 = transactions_parquet.merge(articles, on='article_id', how='left')\ndisplay_html(combined_df1.head(3))\ndel articles\ndel transactions_parquet","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:45:00.204491Z","iopub.execute_input":"2025-08-08T01:45:00.206141Z","iopub.status.idle":"2025-08-08T01:45:20.800336Z","shell.execute_reply.started":"2025-08-08T01:45:00.206102Z","shell.execute_reply":"2025-08-08T01:45:20.799505Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"combined_df1 = combined_df1[['t_dat', 'customer_id', 'article_id', 'price', 'sales_channel_id',\n       'product_code', 'prod_name', 'product_type_name',\n       'product_group_name',\n       'graphical_appearance_name', 'colour_group_name',\n       'perceived_colour_value_name',\n       'perceived_colour_master_name',\n       'department_name', 'index_name',\n       'index_group_name', 'section_name',\n       'garment_group_name', 'detail_desc']]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:48:43.979519Z","iopub.execute_input":"2025-08-08T01:48:43.980098Z","iopub.status.idle":"2025-08-08T01:48:55.205323Z","shell.execute_reply.started":"2025-08-08T01:48:43.980070Z","shell.execute_reply":"2025-08-08T01:48:55.204317Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"combined_df = combined_df1.merge(customers, on='customer_id', how='left')\ndisplay_html(combined_df.head(3))\ndel combined_df1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:49:02.508676Z","iopub.execute_input":"2025-08-08T01:49:02.509000Z","iopub.status.idle":"2025-08-08T01:49:37.567219Z","shell.execute_reply.started":"2025-08-08T01:49:02.508982Z","shell.execute_reply":"2025-08-08T01:49:37.566249Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(clr.S+\"Total Number of Transacting Customers\"+clr.E, \\\n          f\"{combined_df['customer_id'].nunique():,}\")\nprint(clr.S+\"Total Number of Unique Articles purchased\"+clr.E, \\\n          f\"{combined_df['article_id'].nunique():,}\")\nprint(clr.S+\"Min Price of items purchased\"+clr.E, \\\n          combined_df['price'].min())\nprint(clr.S+\"Max Price of items purchased\"+clr.E, \\\n          combined_df['price'].max())\nprint(clr.S+\"Total Number of unique Addresses:\"+clr.E, \\\n          f\"{combined_df['postal_code'].nunique():,}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:50:08.800312Z","iopub.execute_input":"2025-08-08T01:50:08.800623Z","iopub.status.idle":"2025-08-08T01:50:18.248974Z","shell.execute_reply.started":"2025-08-08T01:50:08.800604Z","shell.execute_reply":"2025-08-08T01:50:18.247915Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(clr.S+\"Missing values within customers dataset:\"+clr.E)\nprint(combined_df.isna().sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:50:18.250525Z","iopub.execute_input":"2025-08-08T01:50:18.251422Z","iopub.status.idle":"2025-08-08T01:50:40.791747Z","shell.execute_reply.started":"2025-08-08T01:50:18.251396Z","shell.execute_reply":"2025-08-08T01:50:40.790748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(combined_df['FN'].value_counts(dropna=False))\nprint(combined_df['Active'].value_counts(dropna=False))\nprint(combined_df['club_member_status'].value_counts(dropna=False))\nprint(combined_df['fashion_news_frequency'].value_counts(dropna=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:53:48.349746Z","iopub.execute_input":"2025-08-08T01:53:48.350237Z","iopub.status.idle":"2025-08-08T01:53:50.640990Z","shell.execute_reply.started":"2025-08-08T01:53:48.350200Z","shell.execute_reply":"2025-08-08T01:53:50.639727Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fill FN and Active - the only available value is \"1\"\ncombined_df[\"FN\"].fillna(0, inplace=True)\ncombined_df[\"Active\"].fillna(0, inplace=True)\n\n# Set unknown the club member status & news frequency\ncombined_df[\"club_member_status\"].fillna(\"UNKNOWN\", inplace=True)\n\ncombined_df[\"fashion_news_frequency\"] = combined_df[\"fashion_news_frequency\"].replace({\"None\":\"NONE\"})\ncombined_df[\"fashion_news_frequency\"].fillna(\"UNKNOWN\", inplace=True)\n\n# Set missing values in age with the median\ncombined_df[\"age\"].fillna(customers[\"age\"].median(), inplace=True)\n\ncombined_df['detail_desc'].fillna(\" \", inplace=True)\n\nprint(combined_df.isna().sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:53:52.935701Z","iopub.execute_input":"2025-08-08T01:53:52.936179Z","iopub.status.idle":"2025-08-08T01:54:23.910343Z","shell.execute_reply.started":"2025-08-08T01:53:52.936152Z","shell.execute_reply":"2025-08-08T01:54:23.909473Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### Seasonality","metadata":{}},{"cell_type":"code","source":"combined_df['Month-Year'] = combined_df['t_dat'].str.slice(0, 7)\n\nprod_cnt_df = combined_df.groupby(['Month-Year', 'product_type_name'])\\\n                ['product_type_name'].count().rename('Count').reset_index()\nmost_purchased = prod_cnt_df.groupby('Month-Year').apply(lambda x: x.nlargest(10, 'Count'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:54:30.320257Z","iopub.execute_input":"2025-08-08T01:54:30.320649Z","iopub.status.idle":"2025-08-08T01:54:44.519910Z","shell.execute_reply.started":"2025-08-08T01:54:30.320621Z","shell.execute_reply":"2025-08-08T01:54:44.518587Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sweater_cnt_df = prod_cnt_df[(prod_cnt_df['product_type_name'] == 'Sweater') | (prod_cnt_df['product_type_name'] == 'Bikini top')]\n\n# Plot\nfig, ax = plt.subplots(figsize=(20, 10))\nplt.title('- No. of Sweaters / Bikinis Sold -', size=22, weight=\"bold\")\n\n\nsns.lineplot(x='Month-Year', y='Count', data=sweater_cnt_df, ax=ax, hue = 'product_type_name', markers='*')\nx0,x1 = ax.get_xlim()\ny0,y1 = ax.get_ylim()\n\nticks = range(0, len(sweater_cnt_df), 6)\nplt.xticks(ticks)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:54:47.972266Z","iopub.execute_input":"2025-08-08T01:54:47.972544Z","iopub.status.idle":"2025-08-08T01:54:48.386007Z","shell.execute_reply.started":"2025-08-08T01:54:47.972519Z","shell.execute_reply":"2025-08-08T01:54:48.383226Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##### Purchase Preferences According to Age","metadata":{}},{"cell_type":"code","source":"combined_df['age'].describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:54:52.667776Z","iopub.execute_input":"2025-08-08T01:54:52.668943Z","iopub.status.idle":"2025-08-08T01:54:54.118413Z","shell.execute_reply.started":"2025-08-08T01:54:52.668906Z","shell.execute_reply":"2025-08-08T01:54:54.116375Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_age_interval(x):\n    if x <= 25:\n        return [16, 25]\n    elif x <= 35:\n        return [26, 35]\n    elif x <= 45:\n        return [36, 45]\n    elif x <= 55:\n        return [46, 55]\n    elif x <= 65:\n        return [56, 65]\n    else:\n        return [66, 99]\n\ncombined_df[\"age_interval\"] = combined_df[\"age\"].apply(lambda x: create_age_interval(x))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:54:54.226471Z","iopub.execute_input":"2025-08-08T01:54:54.226749Z","iopub.status.idle":"2025-08-08T01:55:29.254941Z","shell.execute_reply.started":"2025-08-08T01:54:54.226731Z","shell.execute_reply":"2025-08-08T01:55:29.252999Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(24, 10))\nplt.suptitle('- Customer Profile -', size=22, weight=\"bold\")\n\nax1 = plt.subplot(2,2,1)\nax2 = plt.subplot(2,2,2)\nax3 = plt.subplot(2,1,2)\n\nsns.countplot(data=customers, x=\"club_member_status\", ax=ax1,\n              order=customers['club_member_status'].value_counts().index,\n              palette=my_colors[2:])\nshow_values_on_bars(axs=ax1, h_v=\"v\", space=0.4)\nax1.set_title(\"Club Member Status\", size=18, weight=\"bold\")\nax1.set_yticks([])\nax1.set_xlabel(\"\")\nax1.set_ylabel(\"\")\n\nsns.countplot(data=customers, x=\"fashion_news_frequency\", ax=ax2,\n              order=customers['fashion_news_frequency'].value_counts().index,\n              palette=my_colors[2:])\nshow_values_on_bars(axs=ax2, h_v=\"v\", space=0.4)\nax2.set_title(\"Fashion News frequency\", size=18, weight=\"bold\")\nax2.set_yticks([])\nax2.set_xlabel(\"\")\nax2.set_ylabel(\"\")\n\nsns.distplot(customers[\"age\"], color=my_colors[-3], ax=ax3,\n             hist_kws=dict(edgecolor=my_colors[-3]))\nax3.set_title(\"Age Distribution\", size=18, weight=\"bold\")\nax3.set_ylabel(\"\")\n\nfor ax in [ax1, ax2]:\n    x0,x1 = ax.get_xlim()\n    y0,y1 = ax.get_ylim()\n    # ax.imshow(bk_image, zorder=0, extent=[x0, x1, y0, y1], alpha=0.35, aspect='auto')\n    \n# insert_image(path='../input/hm-fashion-recommender-dataset/pics/vans.jpg', zoom=0.5, xybox=(60, 0.00), ax=ax3)\n\nsns.despine(left=True, bottom=True)\nplt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=0.99);","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:55:29.257302Z","iopub.execute_input":"2025-08-08T01:55:29.257636Z","iopub.status.idle":"2025-08-08T01:55:35.610666Z","shell.execute_reply.started":"2025-08-08T01:55:29.257603Z","shell.execute_reply":"2025-08-08T01:55:35.608741Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def create_age_interval2(x):\n    if x <= 25:\n        return '16-25'\n    elif x <= 35:\n        return '26-35'\n    elif x <= 45:\n        return '36-45'\n    elif x <= 55:\n        return '46-55'\n    elif x <= 65:\n        return '56-65'\n    else:\n        return '66-99'\n\ncombined_df[\"age_interval\"] = combined_df[\"age\"].apply(lambda x: create_age_interval2(x))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:55:35.612286Z","iopub.execute_input":"2025-08-08T01:55:35.612642Z","iopub.status.idle":"2025-08-08T01:55:45.963396Z","shell.execute_reply.started":"2025-08-08T01:55:35.612621Z","shell.execute_reply":"2025-08-08T01:55:45.962588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"age_prod_df = combined_df.groupby(['age_interval', 'sales_channel_id'])['sales_channel_id'].count().rename('Count').reset_index()\n# most_purchased = age_prod_df.groupby('age_interval').apply(lambda x: x.nlargest(6, 'Count'))\nage_prod_df['channel-wise %'] = age_prod_df['Count'] / age_prod_df.groupby('age_interval')['Count'].transform('sum') * 100\n\nage_prod_df.head(1000)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T01:55:45.965219Z","iopub.execute_input":"2025-08-08T01:55:45.965617Z","iopub.status.idle":"2025-08-08T01:55:49.230779Z","shell.execute_reply.started":"2025-08-08T01:55:45.965595Z","shell.execute_reply":"2025-08-08T01:55:49.229923Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"age_prod_df = combined_df.groupby(['age_interval', 'prod_name'])['perceived_colour_value_name'].count().rename('Count').reset_index()\nmost_purchased = age_prod_df.groupby('age_interval').apply(lambda x: x.nlargest(10, 'Count'))\n\nmost_purchased.head(1000)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-08T02:03:09.010750Z","iopub.execute_input":"2025-08-08T02:03:09.011789Z","iopub.status.idle":"2025-08-08T02:03:17.288776Z","shell.execute_reply.started":"2025-08-08T02:03:09.011756Z","shell.execute_reply":"2025-08-08T02:03:17.287988Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}