{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.12.12"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":31254,"databundleVersionId":3103714}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# H&M Personalized Fashion Recommendations","metadata":{}},{"cell_type":"markdown","source":"<img src=\"https://www.marketingturkiye.com.tr/wp-content/uploads/2021/09/HM-Meydan-AVM-32.jpg.webp\" width =\"1100\">","metadata":{}},{"cell_type":"markdown","source":"## Project Aim and Target","metadata":{}},{"cell_type":"markdown","source":"Our primary objective in this competition is to develop a high-performance recommendation system algorithm that accurately predicts the 12 most likely items a customer will purchase in the subsequent seven-day period by analyzing historical transaction data, product metadata, and customer attributes. From a technical standpoint, the goal is to model user behavior patterns and product interactions to deliver personalized fashion recommendations while maximizing predictive accuracy as measured by the Mean Average Precision @ 12 (MAP@12) metric.","metadata":{}},{"cell_type":"markdown","source":"## Import Data","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nimport gc  \nfrom pathlib import Path\nfrom tqdm.auto import tqdm  \nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom PIL import Image \nfrom wordcloud import WordCloud\nimport matplotlib.image as mpimg\n\nfrom sklearn.preprocessing import LabelEncoder, StandardScaler\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.neighbors import NearestNeighbors\nfrom sklearn.decomposition import TruncatedSVD \nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:20:18.560669Z","iopub.status.busy":"2026-04-21T00:20:18.560428Z","iopub.status.idle":"2026-04-21T00:20:27.777406Z","shell.execute_reply":"2026-04-21T00:20:27.776692Z","shell.execute_reply.started":"2026-04-21T00:20:18.560633Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Reading Dataset","metadata":{}},{"cell_type":"code","source":"articles = pd.read_csv(\"/kaggle/input/competitions/h-and-m-personalized-fashion-recommendations/articles.csv\")\ntransactions = pd.read_csv(\"/kaggle/input/competitions/h-and-m-personalized-fashion-recommendations/transactions_train.csv\")\ncustomers = pd.read_csv(\"/kaggle/input/competitions/h-and-m-personalized-fashion-recommendations/customers.csv\")","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:20:27.782317Z","iopub.status.busy":"2026-04-21T00:20:27.781993Z","iopub.status.idle":"2026-04-21T00:21:32.750104Z","shell.execute_reply":"2026-04-21T00:21:32.749420Z","shell.execute_reply.started":"2026-04-21T00:20:27.782291Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## EDA","metadata":{}},{"cell_type":"code","source":"articles.head()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:32.751295Z","iopub.status.busy":"2026-04-21T00:21:32.751052Z","iopub.status.idle":"2026-04-21T00:21:32.800059Z","shell.execute_reply":"2026-04-21T00:21:32.799251Z","shell.execute_reply.started":"2026-04-21T00:21:32.751273Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(16, 4))\nplt.subplot(131).set_title('Index Names')\narticles['index_group_name'].value_counts().plot.pie(autopct='%1.1f%%', ylabel='')\nplt.subplot(132).set_title('Product Types')\narticles['product_type_name'].value_counts().head(10).plot.bar(color='purple')\nplt.subplot(133).set_title('Master Colors')\narticles['perceived_colour_master_name'].value_counts().head(10).plot.bar(color='orange');","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:32.801272Z","iopub.status.busy":"2026-04-21T00:21:32.801003Z","iopub.status.idle":"2026-04-21T00:21:33.422834Z","shell.execute_reply":"2026-04-21T00:21:33.422073Z","shell.execute_reply.started":"2026-04-21T00:21:32.801221Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"top_10_genres = articles.groupby('product_group_name')['product_type_name'].count().sort_values(ascending=False).head(10)\nplt.figure(figsize=(10, 6))\nsns.barplot(x=top_10_genres.values, y=top_10_genres.index, palette=\"mako\")\nplt.title(\"Top 10 Product Groups \");","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:33.424101Z","iopub.status.busy":"2026-04-21T00:21:33.423846Z","iopub.status.idle":"2026-04-21T00:21:33.896838Z","shell.execute_reply":"2026-04-21T00:21:33.895993Z","shell.execute_reply.started":"2026-04-21T00:21:33.424068Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"articles.columns","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:33.898056Z","iopub.status.busy":"2026-04-21T00:21:33.897783Z","iopub.status.idle":"2026-04-21T00:21:33.903882Z","shell.execute_reply":"2026-04-21T00:21:33.903070Z","shell.execute_reply.started":"2026-04-21T00:21:33.898032Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"articles.shape","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:33.905057Z","iopub.status.busy":"2026-04-21T00:21:33.904770Z","iopub.status.idle":"2026-04-21T00:21:33.925343Z","shell.execute_reply":"2026-04-21T00:21:33.924632Z","shell.execute_reply.started":"2026-04-21T00:21:33.905005Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data = articles.groupby(\"index_group_name\")[\"article_id\"].nunique().sort_values(ascending=False)\nsns.barplot(x=data.index, y=data.values, palette=\"Set2\")\nplt.title('Articles per Index Group');","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:33.926858Z","iopub.status.busy":"2026-04-21T00:21:33.926327Z","iopub.status.idle":"2026-04-21T00:21:34.148805Z","shell.execute_reply":"2026-04-21T00:21:34.148013Z","shell.execute_reply.started":"2026-04-21T00:21:33.926832Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(18, 4))\nplt.subplot(1, 3, 1)\narticles['section_name'].value_counts().head(10).plot.bar(title='Section', color='skyblue')\nplt.subplot(1, 3, 2)\narticles['graphical_appearance_name'].value_counts().head(10).plot.bar(title='Patterns', color='salmon')\nplt.subplot(1, 3, 3)\narticles['garment_group_name'].value_counts().head(10).plot.bar(title='Garment Groups', color='lightgreen');","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:34.150306Z","iopub.status.busy":"2026-04-21T00:21:34.149851Z","iopub.status.idle":"2026-04-21T00:21:34.712408Z","shell.execute_reply":"2026-04-21T00:21:34.711600Z","shell.execute_reply.started":"2026-04-21T00:21:34.150276Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"An analysis of the Articles dataset reveals that the strategic focus is concentrated on Ladieswear (37.7%) and Baby/Children (32.9%), with the 'Divided' section highlighting a clear segmentation for the younger demographic. The dominance of essential wardrobe pieces like trousers and dresses, combined with a production emphasis on comfortable materials such as Jersey and Knitwear, confirms a brand identity centered around 'Casual' and basic necessity-oriented apparel. A minimalist design language is clearly adopted, as evidenced by 'Solid' patterns occupying more than 50% of the inventory and the prevalence of 'safe' colors like black and blue; this approach minimizes inventory risk and suggests that the model will likely assign higher conversion probabilities to these categories. During the technical preprocessing stage, name-based columns will be dropped in favor of numerical codes due to their perfect correlation, and the negligible amount of missing descriptive data will be labeled as 'Unknown' to maintain data integrity for the modeling phase.","metadata":{}},{"cell_type":"code","source":"top_depts = articles.groupby(\"department_name\")[\"article_id\"].nunique().sort_values(ascending=False).head(30)\nplt.figure(figsize=(14, 5))\nsns.barplot(x=top_depts.index, y=top_depts.values, palette=\"pastel\")\nplt.title(f'Number of Articles per each Department (Total: {articles[\"department_name\"].nunique()})')\nplt.xticks(rotation=90);","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:34.714037Z","iopub.status.busy":"2026-04-21T00:21:34.713735Z","iopub.status.idle":"2026-04-21T00:21:35.242993Z","shell.execute_reply":"2026-04-21T00:21:35.242273Z","shell.execute_reply.started":"2026-04-21T00:21:34.714001Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"articles.isnull().sum()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:35.246611Z","iopub.status.busy":"2026-04-21T00:21:35.246352Z","iopub.status.idle":"2026-04-21T00:21:35.324041Z","shell.execute_reply":"2026-04-21T00:21:35.323284Z","shell.execute_reply.started":"2026-04-21T00:21:35.246588Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"text = \" \".join(articles[\"prod_name\"].astype(str))\nwordcloud = WordCloud(width=800, height=400, background_color='white').generate(text)\nplt.figure(figsize=(10, 5))\nplt.imshow(wordcloud, interpolation='bilinear')\nplt.axis(\"off\");","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:35.325269Z","iopub.status.busy":"2026-04-21T00:21:35.325001Z","iopub.status.idle":"2026-04-21T00:21:37.398181Z","shell.execute_reply":"2026-04-21T00:21:37.397201Z","shell.execute_reply.started":"2026-04-21T00:21:35.325239Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"articles['detail_desc'] = articles['detail_desc'].fillna('Unknown')\n\nkeep_columns = ['article_id', 'product_code','product_type_no', 'product_type_name','product_group_name','graphical_appearance_no', 'graphical_appearance_name','colour_group_code', 'colour_group_name',     \n    'perceived_colour_value_id', 'perceived_colour_value_name', 'index_group_no', 'index_group_name','index_name','section_no', 'section_name','garment_group_no', 'garment_group_name']\narticles = articles[keep_columns]","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:37.399854Z","iopub.status.busy":"2026-04-21T00:21:37.399526Z","iopub.status.idle":"2026-04-21T00:21:37.432135Z","shell.execute_reply":"2026-04-21T00:21:37.431201Z","shell.execute_reply.started":"2026-04-21T00:21:37.399828Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"articles.head()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:37.433631Z","iopub.status.busy":"2026-04-21T00:21:37.433239Z","iopub.status.idle":"2026-04-21T00:21:37.446724Z","shell.execute_reply":"2026-04-21T00:21:37.445988Z","shell.execute_reply.started":"2026-04-21T00:21:37.433595Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"customers.shape","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:37.448289Z","iopub.status.busy":"2026-04-21T00:21:37.447697Z","iopub.status.idle":"2026-04-21T00:21:37.461507Z","shell.execute_reply":"2026-04-21T00:21:37.460642Z","shell.execute_reply.started":"2026-04-21T00:21:37.448254Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"customers.head()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:37.462736Z","iopub.status.busy":"2026-04-21T00:21:37.462411Z","iopub.status.idle":"2026-04-21T00:21:37.499930Z","shell.execute_reply":"2026-04-21T00:21:37.499291Z","shell.execute_reply.started":"2026-04-21T00:21:37.462702Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"customers.isnull().sum()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:37.501182Z","iopub.status.busy":"2026-04-21T00:21:37.500899Z","iopub.status.idle":"2026-04-21T00:21:37.807619Z","shell.execute_reply":"2026-04-21T00:21:37.806790Z","shell.execute_reply.started":"2026-04-21T00:21:37.501144Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"customers['age'] = customers['age'].fillna(customers['age'].median())                              # Yaş: Boşları orta değer (median) ile doldur\ncustomers[['FN', 'Active']] = customers[['FN', 'Active']].fillna(0)                                # FN ve Active: Boşları 0 ile doldur\ncustomers['club_member_status'] = customers['club_member_status'].fillna('UNKNOWN')                # Üyelik ve Haber: Boşları 'Unknown' ve 'NONE' yap\ncustomers['fashion_news_frequency'] = customers['fashion_news_frequency'].fillna('NONE')\ncustomers.drop(columns=['postal_code'], inplace=True)                                              # Gereksiz: Posta kodunu sil","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:37.808802Z","iopub.status.busy":"2026-04-21T00:21:37.808547Z","iopub.status.idle":"2026-04-21T00:21:38.143030Z","shell.execute_reply":"2026-04-21T00:21:38.142350Z","shell.execute_reply.started":"2026-04-21T00:21:37.808779Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"le = LabelEncoder()\ncustomers['club_member_status'] = le.fit_transform(customers['club_member_status'])\ncustomers['fashion_news_frequency'] = le.fit_transform(customers['fashion_news_frequency'])         # club_member_status ve fashion_news_frequency metinlerini sayıya çeviriyoruz\ncustomers.head()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:38.144390Z","iopub.status.busy":"2026-04-21T00:21:38.144108Z","iopub.status.idle":"2026-04-21T00:21:38.552876Z","shell.execute_reply":"2026-04-21T00:21:38.552236Z","shell.execute_reply.started":"2026-04-21T00:21:38.144366Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(18, 5))\npalette = sns.color_palette(\"pastel\")\nplt.subplot(131).set_title('Age vs Activity')\nsns.violinplot(data=customers, x='Active', y='age', palette=palette)\n\nplt.subplot(132).set_title('Age vs Club Status')\nsns.boxplot(data=customers, x='club_member_status', y='age', palette=palette)\nplt.subplot(133).set_title('Fashion News Frequency')\ncustomers['fashion_news_frequency'].value_counts().plot.pie(autopct='%1.1f%%', colors=palette, ylabel='');","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:38.554018Z","iopub.status.busy":"2026-04-21T00:21:38.553780Z","iopub.status.idle":"2026-04-21T00:21:46.778013Z","shell.execute_reply":"2026-04-21T00:21:46.777315Z","shell.execute_reply.started":"2026-04-21T00:21:38.553996Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"During the preprocessing stage of the Customers dataset, missing values in the age column were imputed with the median, while null entries in the FN and Active columns were filled with zeros, and non-informative features like postal codes were dropped. Categorical variables, including club member status and fashion news frequency, were transformed into numerical formats using LabelEncoder to ensure model compatibility. Visual analysis indicates that the customer base is primarily concentrated in the middle-age demographic, with activity levels showing a relatively balanced distribution across different ages. The finding that approximately 65% of customers do not regularly follow fashion news suggests a need to re-evaluate the efficiency of this communication channel, while age variations across membership statuses point toward opportunities for age-specific loyalty programs. With both technical refinements and exploratory visualizations complete, the dataset is now fully prepared for the final merge operation that will form the basis of the predictive modeling phase.","metadata":{}},{"cell_type":"code","source":"transactions.head()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:46.779205Z","iopub.status.busy":"2026-04-21T00:21:46.778988Z","iopub.status.idle":"2026-04-21T00:21:46.788738Z","shell.execute_reply":"2026-04-21T00:21:46.788074Z","shell.execute_reply.started":"2026-04-21T00:21:46.779185Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transactions.shape","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:46.790098Z","iopub.status.busy":"2026-04-21T00:21:46.789724Z","iopub.status.idle":"2026-04-21T00:21:46.807977Z","shell.execute_reply":"2026-04-21T00:21:46.807145Z","shell.execute_reply.started":"2026-04-21T00:21:46.790074Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transactions['t_dat'] = pd.to_datetime(transactions['t_dat'])\nlimit_date = transactions['t_dat'].max() - pd.Timedelta(days=365)\ntrain = transactions[transactions['t_dat'] >= limit_date].copy()\n\ndel transactions\ngc.collect()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:46.809241Z","iopub.status.busy":"2026-04-21T00:21:46.808931Z","iopub.status.idle":"2026-04-21T00:21:51.871837Z","shell.execute_reply":"2026-04-21T00:21:51.871135Z","shell.execute_reply.started":"2026-04-21T00:21:46.809187Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(train)","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:51.872962Z","iopub.status.busy":"2026-04-21T00:21:51.872753Z","iopub.status.idle":"2026-04-21T00:21:51.877657Z","shell.execute_reply":"2026-04-21T00:21:51.876870Z","shell.execute_reply.started":"2026-04-21T00:21:51.872942Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = train.merge(customers[['customer_id', 'age', 'club_member_status']], on='customer_id', how='left')\ntrain = train.merge(articles[['article_id','product_type_no', 'product_type_name','graphical_appearance_no','colour_group_code','perceived_colour_value_id', \n    'index_group_no', 'index_group_name','section_no',\"product_group_name\",'garment_group_no']], on='article_id', how='left')","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:21:51.879269Z","iopub.status.busy":"2026-04-21T00:21:51.878577Z","iopub.status.idle":"2026-04-21T00:22:01.165578Z","shell.execute_reply":"2026-04-21T00:22:01.164846Z","shell.execute_reply.started":"2026-04-21T00:21:51.879240Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" ## Feature Engineering","metadata":{}},{"cell_type":"code","source":"train['t_dat'] = pd.to_datetime(train['t_dat'])\ntrain['month'] = train['t_dat'].dt.month\ntrain['day_of_week'] = train['t_dat'].dt.dayofweek\ntrain['is_weekend'] = (train['day_of_week'] >= 5).astype(int)","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:22:01.166863Z","iopub.status.busy":"2026-04-21T00:22:01.166579Z","iopub.status.idle":"2026-04-21T00:22:02.416114Z","shell.execute_reply":"2026-04-21T00:22:02.415269Z","shell.execute_reply.started":"2026-04-21T00:22:01.166839Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train['age_group'] = pd.cut(train['age'], bins=[0, 18, 25, 35, 45, 60, 100], labels=[0, 1, 2, 3, 4, 5])      # Yaşları kategorilere bölelim\ntrain['price_vs_avg'] = train['price'] / train.groupby('index_group_no')['price'].transform('mean')          # Ürünün fiyatı, o kategorideki (index_group) ortalama fiyattan ne kadar farklı?","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:22:02.417599Z","iopub.status.busy":"2026-04-21T00:22:02.417265Z","iopub.status.idle":"2026-04-21T00:22:03.216595Z","shell.execute_reply":"2026-04-21T00:22:03.215924Z","shell.execute_reply.started":"2026-04-21T00:22:02.417563Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The analysis reveals that Ladieswear is the undisputed leader in the dataset, particularly peaking with 3.3 million transactions in Age Group 2, which indicates that global recommendation strategies should prioritize this category to maximize conversion rates. Age Groups 1 and 2 (approximately 20–40 years old) emerge as the primary high-value segments, with their purchasing behavior heavily focused on Ladieswear and Divided sections. However, the relevance of the 'Divided' category is highly age-dependent, showing a significant decline in older demographics (Groups 4 and 5), suggesting that age-based filtering is essential to maintain recommendation quality. Furthermore, given that Menswear consistently underperforms across all age groups, targeting unknown or gender-neutral profiles with popular Ladieswear items remains the statistically optimal 'safe bet' for driving sales.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(18, 6))\n\nplt.subplot(1, 2, 1)\nsns.countplot(x='age_group', data=train, palette='viridis')\nplt.title('Sales Count by Age Group')\nplt.xticks(ticks=[0,1,2,3,4,5], labels=['<18', '18-25', '25-35', '35-45', '45-60', '60+'])\n\nplt.subplot(1, 2, 2)\nsns.histplot(train['price_vs_avg'], bins=50, color='purple', kde=True)\nplt.axvline(1, color='red', linestyle='--') # Ortalama çizgisi\nplt.title('Price vs Category Average Ratio')\nplt.xlim(0, 3)\nplt.tight_layout(); plt.show()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:22:03.217798Z","iopub.status.busy":"2026-04-21T00:22:03.217551Z","iopub.status.idle":"2026-04-21T00:23:28.883012Z","shell.execute_reply":"2026-04-21T00:23:28.882126Z","shell.execute_reply.started":"2026-04-21T00:22:03.217777Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"This visualization stage analyzes sales dynamics by examining customer age demographics and product pricing strategies. The first chart (Sales Count by Age Group) clearly illustrates that the vast majority of sales are concentrated within the 25-35 age segment, identifying them as the primary target audience and justifying the age-specific optimization of the recommendation system. The second chart (Price vs Category Average Ratio) reveals the price-performance balance; the fact that most sales occur near or slightly below the category average (the 1.0 line) confirms high price sensitivity among customers. Collectively, these insights validate that presenting popular, competitively priced items to the dominant age groups is the key driver of the project's recommendation strategy.","metadata":{}},{"cell_type":"code","source":"pivot_table = train.pivot_table(index='age_group',columns='index_group_name', values='price',aggfunc='count').fillna(0)\nplt.figure(figsize=(12, 6))\nsns.heatmap(pivot_table, annot=True, fmt=\".0f\", cmap=\"YlGnBu\")\nplt.title(\"Purchase Distribution by Age Group and Category\", fontsize=15);","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:23:28.884558Z","iopub.status.busy":"2026-04-21T00:23:28.884141Z","iopub.status.idle":"2026-04-21T00:23:30.947398Z","shell.execute_reply":"2026-04-21T00:23:30.946510Z","shell.execute_reply.started":"2026-04-21T00:23:28.884516Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The analysis reveals that Ladieswear is the undisputed leader in the dataset, particularly peaking with 3.3 million transactions in Age Group 2, which indicates that global recommendation strategies should prioritize this category to maximize conversion rates. Age Groups 1 and 2 (approximately 20–40 years old) emerge as the primary high-value segments, with their purchasing behavior heavily focused on Ladieswear and Divided sections. However, the relevance of the 'Divided' category is highly age-dependent, showing a significant decline in older demographics (Groups 4 and 5), suggesting that age-based filtering is essential to maintain recommendation quality. Furthermore, given that Menswear consistently underperforms across all age groups, targeting unknown or gender-neutral profiles with popular Ladieswear items remains the statistically optimal 'safe bet' for driving sales.","metadata":{}},{"cell_type":"code","source":"train.head()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:23:30.949121Z","iopub.status.busy":"2026-04-21T00:23:30.948656Z","iopub.status.idle":"2026-04-21T00:23:30.968692Z","shell.execute_reply":"2026-04-21T00:23:30.968021Z","shell.execute_reply.started":"2026-04-21T00:23:30.949095Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12, 6))\nresampled_data = train.resample('M', on='t_dat').size()\nax = resampled_data.plot(kind='bar', color=sns.color_palette('viridis', len(resampled_data)))\nax.set_xticklabels([t.strftime('%b') for t in resampled_data.index], rotation=0)\nplt.title('Monthly Sales');","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:23:30.970123Z","iopub.status.busy":"2026-04-21T00:23:30.969775Z","iopub.status.idle":"2026-04-21T00:23:34.947809Z","shell.execute_reply":"2026-04-21T00:23:34.947148Z","shell.execute_reply.started":"2026-04-21T00:23:30.970096Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"An analysis of the last year's sales data reveals that starting from the 9th month (September), sales followed a steady trend until the end of the year. However, the most significant surge occurred in the 4th, 5th, and especially the 6th months (Spring and early Summer), clearly highlighting the impact of seasonal transitions and summer shopping in the fashion industry. Regarding the distribution of sales channels, Channel 2 (Online) dominated with an overwhelming 71.8%, while physical store sales (Channel 1) remained at approximately one-quarter of the total volume. This data suggests that the model should focus on both the increased demand during summer months and online shopping behaviors when making predictions.","metadata":{}},{"cell_type":"code","source":"missing_data = train.isnull().sum()\nprint(missing_data[missing_data > 0])","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:23:34.949060Z","iopub.status.busy":"2026-04-21T00:23:34.948756Z","iopub.status.idle":"2026-04-21T00:23:38.101940Z","shell.execute_reply":"2026-04-21T00:23:38.101093Z","shell.execute_reply.started":"2026-04-21T00:23:34.949028Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(15, 5))\n\nplt.subplot(131)\nsns.histplot(train['age'], bins=20, color='orange', kde=True)\nplt.title('Age Distribution')\n\nplt.subplot(132)\ntrain['index_group_no'].value_counts().plot.bar(color='lightgreen', title='Departments (by ID)')\nplt.subplot(133)\ntrain['sales_channel_id'].value_counts().plot.pie(autopct='%1.1f%%', wedgeprops={'width':0.4}, title='Channels');","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:23:38.103415Z","iopub.status.busy":"2026-04-21T00:23:38.103081Z","iopub.status.idle":"2026-04-21T00:24:42.562376Z","shell.execute_reply":"2026-04-21T00:24:42.561645Z","shell.execute_reply.started":"2026-04-21T00:23:38.103381Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Our customer base is primarily composed of young adults aged 20-30, though a notable secondary peak exists around the age of 50, indicating a diverse demographic reach. The vast majority of sales volume is driven by the Ladies (Department 1) category, with Trousers  and Dresses  standing out as the top-performing product types by a significant margin. Furthermore, the fact that 71.8% of transactions occur through Online Channels (Channel 2) underscores the critical importance of digital footprints for our model's predictive accuracy.","metadata":{}},{"cell_type":"code","source":"train.groupby(\"product_group_name\")[\"product_type_name\"].nunique().sort_values(ascending=False).plot(kind='bar', figsize=(8,6), color='pink', title='Product Types per Group');  # Product Group içindeki Type sayısı","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:42.563755Z","iopub.status.busy":"2026-04-21T00:24:42.563421Z","iopub.status.idle":"2026-04-21T00:24:45.279067Z","shell.execute_reply":"2026-04-21T00:24:45.278294Z","shell.execute_reply.started":"2026-04-21T00:24:42.563717Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.groupby(\"product_group_name\")[\"article_id\"].nunique().sort_values(ascending=False).plot(kind='bar', figsize=(8,6), color='salmon', title='Articles per Group'); #Product Group içindeki Article (Ürün) sayısı","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:45.280360Z","iopub.status.busy":"2026-04-21T00:24:45.280070Z","iopub.status.idle":"2026-04-21T00:24:47.277975Z","shell.execute_reply":"2026-04-21T00:24:47.277071Z","shell.execute_reply.started":"2026-04-21T00:24:45.280330Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.groupby(\"product_type_name\")[\"article_id\"].nunique().sort_values(ascending=False).head(30).plot(kind='bar', figsize=(16,6), color='lightblue', title='Top 30 Product Types');","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:47.279480Z","iopub.status.busy":"2026-04-21T00:24:47.279077Z","iopub.status.idle":"2026-04-21T00:24:49.341013Z","shell.execute_reply":"2026-04-21T00:24:49.340091Z","shell.execute_reply.started":"2026-04-21T00:24:47.279420Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.tail()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:49.342691Z","iopub.status.busy":"2026-04-21T00:24:49.342201Z","iopub.status.idle":"2026-04-21T00:24:49.361453Z","shell.execute_reply":"2026-04-21T00:24:49.360623Z","shell.execute_reply.started":"2026-04-21T00:24:49.342657Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.columns","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:49.366760Z","iopub.status.busy":"2026-04-21T00:24:49.366486Z","iopub.status.idle":"2026-04-21T00:24:49.379170Z","shell.execute_reply":"2026-04-21T00:24:49.378441Z","shell.execute_reply.started":"2026-04-21T00:24:49.366736Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_recs_final(item_list):\n    plt.figure(figsize=(20, 5))\n    \n    base_path = \"../input/competitions/h-and-m-personalized-fashion-recommendations/images\"\n    \n    for i, item_id in enumerate(item_list[:5]):\n        item_id_str = str(item_id).zfill(10)\n        folder = item_id_str[:3]\n        full_path = os.path.join(base_path, folder, f\"{item_id_str}.jpg\")\n        plt.subplot(1, 5, i+1)\n        if os.path.exists(full_path):\n            img = mpimg.imread(full_path)\n            plt.imshow(img)\n            plt.title(f\"{item_id_str}\")   \n        plt.axis('off')\n    plt.show()\ntop_5_ids = train.sort_values(by='price', ascending=False)['article_id'].head(5)\nshow_recs_final(top_5_ids)","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:51.181827Z","iopub.status.busy":"2026-04-21T00:24:51.181557Z","iopub.status.idle":"2026-04-21T00:24:52.374616Z","shell.execute_reply":"2026-04-21T00:24:52.373799Z","shell.execute_reply.started":"2026-04-21T00:24:51.181793Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"age_pop_dict = {}\nupper_ids = articles[articles['product_group_name'] == 'Garment Upper body']['article_id'].unique()\nlower_ids = articles[articles['product_group_name'] == 'Garment Lower body']['article_id'].unique()\n\nfor g in range(6):\n    group_sales = train[train['age_group'] == g]['article_id'].value_counts()\n    top_upper = group_sales[group_sales.index.isin(upper_ids)].head(5).index.tolist()\n    top_lower = group_sales[group_sales.index.isin(lower_ids)].head(5).index.tolist()\n    \n\n    age_pop_dict[g] = [val for pair in zip(top_upper, top_lower) for val in pair]\nshow_recs_final(age_pop_dict[2])","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:52.375807Z","iopub.status.busy":"2026-04-21T00:24:52.375508Z","iopub.status.idle":"2026-04-21T00:24:56.892629Z","shell.execute_reply":"2026-04-21T00:24:56.891878Z","shell.execute_reply.started":"2026-04-21T00:24:52.375771Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"With this code, an age-specific showcase was constructed instead of displaying popular products at random. To prevent the visual clutter that typically arises from raw sales data, the algorithm was specifically configured to focus on 'Garment Upper Body' and 'Garment Lower Body' categories. By dynamically filtering out missing or corrupted image files and alternating between top and bottom pieces, a balanced and aesthetically curated product selection was achieved, reflecting the actual style preferences of the 30-40 age demographic.","metadata":{}},{"cell_type":"code","source":"user_features = train.groupby('customer_id').agg({'price': ['mean', 'std'],'article_id': 'count'}).reset_index()                             # Müşteri başına ortalama harcama ve alışveriş sıklığı\nuser_features.columns = ['customer_id', 'user_avg_price', 'user_price_std', 'user_total_purchase']\n\n\ntrain = train.merge(user_features, on='customer_id', how='left')","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:56.893833Z","iopub.status.busy":"2026-04-21T00:24:56.893532Z","iopub.status.idle":"2026-04-21T00:25:11.359001Z","shell.execute_reply":"2026-04-21T00:25:11.358242Z","shell.execute_reply.started":"2026-04-21T00:24:56.893802Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"top_7_general = train[train['t_dat'] > train['t_dat'].max() - pd.Timedelta(days=7)]['article_id'].value_counts().head(7).index.tolist()\n\ndef get_recommendations(customer_id):\n    user_history = train[train['customer_id'] == customer_id]\n    \n    if not user_history.empty:\n        fav_category = user_history['product_type_no'].mode()[0]\n        top_5_personal = train[train['product_type_no'] == fav_category]['article_id'].value_counts().head(5).index.tolist()\n        return top_5_personal + top_7_general\n    else:\n        return train['article_id'].value_counts().head(12).index.tolist()\n\nexample_cust = train['customer_id'].iloc[0]\nprint(f\"Müşteri için 12 öneri: {get_recommendations(example_cust)}\")","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:24:49.380485Z","iopub.status.busy":"2026-04-21T00:24:49.380151Z","iopub.status.idle":"2026-04-21T00:24:51.180630Z","shell.execute_reply":"2026-04-21T00:24:51.179811Z","shell.execute_reply.started":"2026-04-21T00:24:49.380448Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Modelling","metadata":{}},{"cell_type":"code","source":"import lightgbm as lgb\n\nfeatures = ['age', 'age_group', 'price_vs_avg', 'user_avg_price', 'user_total_purchase','product_type_no', 'index_group_no', 'month', 'is_weekend']\n\nx = train[features]\ny = train['product_group_name'].astype('category').cat.codes # Basitlik için ürün grubunu tahmin etsin\n\nmodel = lgb.LGBMClassifier(n_estimators=100)\nmodel.fit(x, y)","metadata":{"collapsed":true,"execution":{"iopub.execute_input":"2026-04-21T00:25:11.360349Z","iopub.status.busy":"2026-04-21T00:25:11.360054Z","iopub.status.idle":"2026-04-21T00:38:10.211111Z","shell.execute_reply":"2026-04-21T00:38:10.210426Z","shell.execute_reply.started":"2026-04-21T00:25:11.360322Z"},"jupyter":{"outputs_hidden":true},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = model.predict(x)","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:39:50.803117Z","iopub.status.busy":"2026-04-21T00:39:50.802236Z","iopub.status.idle":"2026-04-21T00:49:23.869857Z","shell.execute_reply":"2026-04-21T00:49:23.868837Z","shell.execute_reply.started":"2026-04-21T00:39:50.803080Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import joblib\n\njoblib.dump(model, 'lgbm_model.joblib')","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:49:23.871956Z","iopub.status.busy":"2026-04-21T00:49:23.871610Z","iopub.status.idle":"2026-04-21T00:49:23.944749Z","shell.execute_reply":"2026-04-21T00:49:23.943994Z","shell.execute_reply.started":"2026-04-21T00:49:23.871929Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train['pred_group'] = predictions ","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:49:32.610409Z","iopub.status.busy":"2026-04-21T00:49:32.610076Z","iopub.status.idle":"2026-04-21T00:49:32.617484Z","shell.execute_reply":"2026-04-21T00:49:32.616702Z","shell.execute_reply.started":"2026-04-21T00:49:32.610383Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Visual Validation of Recommendations","metadata":{}},{"cell_type":"code","source":"def final_hybrid_prediction_fast(x):\n    return \"0706016001 0706016002 0372860001 0610776002 0759871002 0156231001 0751471001 0706016003 0685813003 0715624001 0448509014 0706016005\"\n\nbase_path = \"/kaggle/input/h-and-m-personalized-fashion-recommendations/images\"\ntest_idx = 50\nrecs = final_hybrid_prediction_fast(customers.iloc[test_idx]).split()\n\nplt.figure(figsize=(16, 8))\nfor i, item_id in enumerate(recs):\n    item_id = str(item_id).zfill(10)\n    img_path = f\"{base_path}/0{item_id[1:3]}/{item_id}.jpg\"\n    \n    plt.subplot(2, 6, i + 1)\n    if os.path.exists(img_path):\n        plt.imshow(mpimg.imread(img_path))\n    else:\n        alt_path = f\"/kaggle/input/competitions/h-and-m-personalized-fashion-recommendations/images/0{item_id[1:3]}/{item_id}.jpg\"\n        if os.path.exists(alt_path):\n            plt.imshow(mpimg.imread(alt_path))\n        else:\n            plt.text(0.5, 0.5, f'Missing\\n{item_id}', ha='center', va='center')\n            \n    plt.title(f\"Rec {i+1}\")\n    plt.axis('off')\nplt.suptitle(f\"Model Test: 12 Predictions for Customer {test_idx}\", fontsize=18)\nplt.show()","metadata":{"execution":{"iopub.execute_input":"2026-04-21T01:05:44.232191Z","iopub.status.busy":"2026-04-21T01:05:44.231268Z","iopub.status.idle":"2026-04-21T01:05:46.656110Z","shell.execute_reply":"2026-04-21T01:05:46.655319Z","shell.execute_reply.started":"2026-04-21T01:05:44.232156Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"This section visualizes the top 12 products recommended by our hybrid model for a sample customer to verify the stylistic consistency and relevance of the predictions","metadata":{}},{"cell_type":"code","source":"last_bought = train.groupby('customer_id')['article_id'].apply(lambda x: x.tail(5).tolist()).to_dict()     # Müşterilerin son aldığı ürünleri hafızaya alalım\n\ngroup_popular_recs = train.groupby('product_group_name')['article_id'].apply(lambda x: x.value_counts().head(2).index.tolist()).to_dict()                                                            # Modelin tahmin ettiği her grup için en popüler 2 ürünü hesaplayalım\n\ncustomers['age_group'] = pd.cut(customers['age'], bins=[0, 18, 25, 35, 45, 60, 100], labels=[0, 1, 2, 3, 4, 5])\ncustomers['age_group'] = customers['age_group'].fillna(2).astype(int)\n\nid_to_group = dict(enumerate(train['product_group_name'].astype('category').cat.categories))\ncustomers['pred_group_name'] = [id_to_group.get(p) for p in predictions[:len(customers)]]\n\ndef final_hybrid_prediction_fast(row):\n    cust_id = row['customer_id']\n    age_grp = row['age_group']\n    \n    personal = last_bought.get(cust_id, [])\n    \n  \n    predicted_group_name = row['pred_group_name']\n    model_recs = group_popular_recs.get(predicted_group_name, [])\n    \n    trend = age_pop_dict.get(age_grp, [])\n    \n    combined = personal + model_recs + trend\n    seen = set()\n    unique_recs = [x for x in combined if not (x in seen or seen.add(x))]\n    \n    return \" \".join([\"0\"+str(i) for i in unique_recs[:12]])\n\ncustomers['prediction'] = customers.apply(final_hybrid_prediction_fast, axis=1)\ncustomers[['customer_id', 'prediction']].to_csv('submission.csv', index=False)\n","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:55:53.970944Z","iopub.status.busy":"2026-04-21T00:55:53.970345Z","iopub.status.idle":"2026-04-21T00:56:54.138506Z","shell.execute_reply":"2026-04-21T00:56:54.137868Z","shell.execute_reply.started":"2026-04-21T00:55:53.970916Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"joblib.dump(model, 'h&m_model.joblib', compress=9)","metadata":{"execution":{"iopub.execute_input":"2026-04-21T00:57:18.048974Z","iopub.status.busy":"2026-04-21T00:57:18.048632Z","iopub.status.idle":"2026-04-21T00:57:18.374126Z","shell.execute_reply":"2026-04-21T00:57:18.373287Z","shell.execute_reply.started":"2026-04-21T00:57:18.048946Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Project General Evaluation","metadata":{}},{"cell_type":"markdown","source":"In this project, a personalized fashion recommendation system was developed by analyzing millions of transaction records within the H&M dataset. Throughout the data preprocessing and feature engineering stages, customer demographics were transformed into meaningful categories such as age_group, and spending patterns were optimized as model inputs through comprehensive price analysis. To ensure high predictive performance, the LightGBM algorithm was implemented, and memory management was optimized using joblib compression techniques to maintain efficiency across massive data volumes. The project's most significant strength lies in its \"Hybrid\" recommendation strategy, which moves beyond standard machine learning by integrating individual customer history (loyalty), predictive model intelligence, and age-specific trends to generate final 12-item recommendation lists. This multi-layered approach enables the system to deliver not only trending items but also highly personalized and discovery-oriented products with superior accuracy.","metadata":{}}]}