Mathew K Analytics

Lesson 12 · Python for Retail E-commerce Analytics

Understanding Product and Category Data for Retail E-commerce Analytics

We will explore how retail businesses structure and analyze product and category data. Analyzing these data is essential for tracking sales, managing…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Understanding Product and Category Data in Retail Analytics#

  • We will explore how retail businesses structure and analyze product and category data.
  • Analyzing these data is essential for tracking sales, managing inventory, and making effective business decisions.
  • You will learn how to summarize sales by product and category, uncover best sellers, and avoid common analysis pitfalls.
  • By the end, you will generate actionable insights for improving retail business performance.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Core Concepts: Retail Analytics Data Foundations#

  • Retail datasets usually capture transactions, products, customers, and categories.
  • Revenue, quantity sold, and price are key metrics for products and product categories.
  • Beginners often confuse product-level sales with category-level sales, or miscalculate revenue.
  • Common pitfalls include missing values, incorrect groupings, and not cleaning category names.
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00502/online_retail_II.xlsx'
df_retail = pd.read_excel(url, sheet_name='Year 2010-2011')
df_retail['InvoiceDate'] = pd.to_datetime(df_retail['InvoiceDate'])
print(df_retail.shape)
print(df_retail.head(3))
(541910, 8)
  Invoice StockCode                         Description  Quantity  \
0  536365    85123A  WHITE HANGING HEART T-LIGHT HOLDER         6   
1  536365     71053                 WHITE METAL LANTERN         6   
2  536365    84406B      CREAM CUPID HEARTS COAT HANGER         8   

          InvoiceDate  Price  Customer ID         Country  
0 2010-12-01 08:26:00   2.55      17850.0  United Kingdom  
1 2010-12-01 08:26:00   3.39      17850.0  United Kingdom  
2 2010-12-01 08:26:00   2.75      17850.0  United Kingdom  
missing_descriptions = df_retail['Description'].isnull().sum()
print(f'Missing product descriptions: {missing_descriptions}')
Missing product descriptions: 1454
np.random.seed(42)
categories = ['Electronics','Clothing','Home','Sports','Beauty']
product_ids = list(range(1001,1101))
product_categories = np.random.choice(categories,100)
product_prices = np.round(np.random.uniform(5,500,100),2)
df_catalog = pd.DataFrame({
    'ProductID': product_ids,
    'Category': product_categories,
    'Price': product_prices
})
print(df_catalog.shape)
print(df_catalog.head(3))
(100, 3)
   ProductID Category   Price
0       1001   Sports  457.91
1       1002   Beauty  425.77
2       1003     Home  227.48
category_counts = df_catalog['Category'].value_counts()
print(category_counts)
Category
Sports         26
Clothing       21
Beauty         19
Electronics    18
Home           16
Name: count, dtype: int64
np.random.seed(42)
n_orders = 1000
order_ids = list(range(1, n_orders+1))
customer_ids = np.random.randint(1000, 1500, n_orders)
product_ids = np.random.randint(1001, 1100, n_orders)
quantities = np.random.randint(1, 5, n_orders)
order_dates = pd.date_range('2023-01-01', periods=n_orders, freq='h')
df_orders = pd.DataFrame({
    'OrderID': order_ids,
    'CustomerID': customer_ids,
    'ProductID': product_ids,
    'Quantity': quantities,
    'OrderDate': order_dates
})
print(df_orders.shape)
print(df_orders.head(3))
(1000, 5)
   OrderID  CustomerID  ProductID  Quantity           OrderDate
0        1        1102       1049         3 2023-01-01 00:00:00
1        2        1435       1011         1 2023-01-01 01:00:00
2        3        1348       1085         3 2023-01-01 02:00:00
df_orders_merged = df_orders.merge(df_catalog, on='ProductID', how='left')
print(df_orders_merged[['OrderID', 'ProductID', 'Category', 'Quantity']].head(5))
   OrderID  ProductID     Category  Quantity
0        1       1049     Clothing         3
1        2       1011       Sports         1
2        3       1085  Electronics         3
3        4       1026         Home         1
4        5       1063       Beauty         2
qty_by_product = df_orders_merged.groupby('ProductID')['Quantity'].sum().sort_values(ascending=False)
print(qty_by_product.head(5))
ProductID
1098    54
1026    51
1017    50
1059    48
1040    45
Name: Quantity, dtype: int32
df_orders_merged['Revenue'] = df_orders_merged['Quantity'] * df_orders_merged['Price']
revenue_by_product = df_orders_merged.groupby('ProductID')['Revenue'].sum().sort_values(ascending=False)
print(revenue_by_product.head(5))
ProductID
1040    19899.90
1001    14195.21
1025    13272.64
1033    13224.90
1094    12576.70
Name: Revenue, dtype: float64
order_revenue = df_orders_merged.groupby('OrderID')['Revenue'].sum()
aov = order_revenue.mean()
print(f'Average Order Value (AOV): ${aov:.2f}')
Average Order Value (AOV): $593.73
category_revenue = df_orders_merged.groupby('Category')['Revenue'].sum().sort_values(ascending=False)
mean_category_revenue = df_orders_merged.groupby('Category')['Revenue'].mean().sort_values(ascending=False)
print('Total revenue by category:')
print(category_revenue)
print(' ')
print('Mean order revenue by category:')
print(mean_category_revenue)
Total revenue by category:
Category
Sports         147783.07
Beauty         134105.61
Clothing       121487.00
Home            98098.72
Electronics     92256.84
Name: Revenue, dtype: float64
 
Mean order revenue by category:
Category
Beauty         694.847720
Home           616.973082
Clothing       604.412935
Sports         545.324982
Electronics    524.186591
Name: Revenue, dtype: float64
top_category = category_revenue.idxmax()
top_value = category_revenue.max()
print(f'Top category by total revenue: {top_category} (${top_value:.2f})')
Top category by total revenue: Sports ($147783.07)
df_orders_merged['Month'] = df_orders_merged['OrderDate'].dt.to_period('M')
monthly_revenue = df_orders_merged.groupby('Month')['Revenue'].sum()
print(monthly_revenue.head(6))
Month
2023-01    445700.41
2023-02    148030.83
Freq: M, Name: Revenue, dtype: float64
avg_qty_by_cat = df_orders_merged.groupby('Category')['Quantity'].mean().sort_values(ascending=False)
print(avg_qty_by_cat)
Category
Clothing       2.542289
Sports         2.527675
Beauty         2.523316
Home           2.509434
Electronics    2.318182
Name: Quantity, dtype: float64
customer_revenue = df_orders_merged.groupby('CustomerID')['Revenue'].sum().sort_values(ascending=False)
print(customer_revenue.head(5))
CustomerID
1098    5808.86
1053    5621.52
1222    4999.08
1379    4952.56
1095    4803.94
Name: Revenue, dtype: float64
pairs = df_orders_merged.groupby('OrderID')['ProductID'].apply(list)
from collections import Counter
pair_counts = Counter()
for products in pairs:
    for i in range(len(products)-1):
        pair = tuple(sorted((products[i], products[i+1])))
        pair_counts[pair] += 1
print(pair_counts.most_common(5))
[]
missing_prices = df_catalog['Price'].isnull().sum()
print(f'Missing product prices: {missing_prices}')
Missing product prices: 0
# Incorrect: using count() instead of sum() for quantity by category
bad_qty_by_cat = df_orders_merged.groupby('Category')['Quantity'].count().sort_values(ascending=False)
print(bad_qty_by_cat)
print('Correct total quantity by category:')
print(df_orders_merged.groupby('Category')['Quantity'].sum().sort_values(ascending=False))
Category
Sports         271
Clothing       201
Beauty         193
Electronics    176
Home           159
Name: Quantity, dtype: int64
Correct total quantity by category:
Category
Sports         685
Clothing       511
Beauty         487
Electronics    408
Home           399
Name: Quantity, dtype: int32
# Wrong: Not multiplying by quantity: shows unit price sum, not true revenue
wrong_revenue_by_product = df_orders_merged.groupby('ProductID')['Price'].sum().sort_values(ascending=False)
correct_revenue_by_product = df_orders_merged.groupby('ProductID')['Revenue'].sum().sort_values(ascending=False)
print('Incorrect (just adding prices):')
print(wrong_revenue_by_product.head(3))
print('Correct (quantity x price):')
print(correct_revenue_by_product.head(3))
Incorrect (just adding prices):
ProductID
1040    7959.96
1001    5952.83
1094    5679.80
Name: Price, dtype: float64
Correct (quantity x price):
ProductID
1040    19899.90
1001    14195.21
1025    13272.64
Name: Revenue, dtype: float64
top_10_products = revenue_by_product.head(10).index.tolist()
df_orders_merged['PerformanceTier'] = np.where(df_orders_merged['ProductID'].isin(top_10_products), 'Top 10', 'Other')
performance_revenue = df_orders_merged.groupby('PerformanceTier')['Revenue'].sum()
print(performance_revenue)
PerformanceTier
Other     460487.23
Top 10    133244.01
Name: Revenue, dtype: float64
monthly_cat = df_orders_merged.groupby(['Month','Category'])['Revenue'].sum().unstack().fillna(0)
print(monthly_cat.head(6))
Category     Beauty  Clothing  Electronics      Home     Sports
Month                                                          
2023-01   104983.85  85006.99     67446.18  71163.05  117100.34
2023-02    29121.76  36480.01     24810.66  26935.67   30682.73
latest_months = df_orders_merged['Month'].sort_values().unique()[-3:]
last3_df = df_orders_merged[df_orders_merged['Month'].isin(latest_months)]
forecast = last3_df.groupby('ProductID')['Quantity'].mean().sort_values(ascending=False)
print(forecast.head(5))
ProductID
1028    3.250000
1048    3.142857
1017    3.125000
1015    3.111111
1055    3.083333
Name: Quantity, dtype: float64
current_month = df_orders_merged['Month'].max()
cur_month_df = df_orders_merged[df_orders_merged['Month'] == current_month]
top_cur_cat = cur_month_df.groupby('Category')['Revenue'].sum().idxmax()
top_cur_val = cur_month_df.groupby('Category')['Revenue'].sum().max()
print(f'In {current_month}, the top-selling category is {top_cur_cat} with ${top_cur_val:.2f} in sales.')
print('Business action: Consider additional promotions for this category or expanding its inventory in the coming month.')
In 2023-02, the top-selling category is Clothing with $36480.01 in sales.
Business action: Consider additional promotions for this category or expanding its inventory in the coming month.

You have completed Understanding Product and Category Data!#

  • Try exploring sales by country using the retail transactions dataset.
  • Challenge: Analyze repeat purchase rates by customer ID.
  • Share your findings and code with a teammate or mentor.
  • Keep building your skills subscribe to our YouTube channel for more analytics tutorials.

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.