Mathew K Analytics

Lesson 41 · Python for Retail E-commerce Analytics

Introduction to Machine Learning in Retail: Practical Training for E-commerce Analytics

We will explore how machine learning can be applied to retail data. This is important for improving sales strategies, optimizing inventory, and enhancing…

⬇ 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

Introduction to Machine Learning in Retail#

  • We will explore how machine learning can be applied to retail data.
  • This is important for improving sales strategies, optimizing inventory, and enhancing customer experiences.
  • By the end, you will know how to analyze sales, customer behavior, and product performance using real datasets.
  • We will go from simple retail metrics to more advanced analytics and business insights.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')

Core Concepts: Retail Data and Metrics#

  • Retail datasets capture transactions, products, customers, and time-based sales.
  • Key metrics include sales revenue, units sold (quantity), and average price.
  • Transactions datasets show what customers buy, when, and for how much.
  • Beginners should avoid confusing product quantity with transaction count.
  • Revenue is always calculated as Quantity x Price.
# Beginner Example 1: Load Online Retail Transactions dataset
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00502/online_retail_II.xlsx'
ori_df = pd.read_excel(url, sheet_name='Year 2010-2011')
ori_df['InvoiceDate'] = pd.to_datetime(ori_df['InvoiceDate'])
print(ori_df.shape)
print(ori_df.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  
# Beginner Example 2: Load a simulated Retail Product Catalog
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)
prodcat_df = pd.DataFrame({'ProductID':product_ids,'Category':product_categories,'Price':product_prices})
print(prodcat_df.shape)
print(prodcat_df.head(3))
(100, 3)
   ProductID Category   Price
0       1001   Sports  457.91
1       1002   Beauty  425.77
2       1003     Home  227.48
# Beginner Example 3: Load Simulated Customer Orders
np.random.seed(42)
n_orders = 1000
order_ids = list(range(1,n_orders+1))
customer_ids = np.random.randint(1000,1500,n_orders)
order_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')
orders_df = pd.DataFrame({'OrderID':order_ids,'CustomerID':customer_ids,'ProductID':order_product_ids,'Quantity':quantities,'OrderDate':order_dates})
print(orders_df.shape)
print(orders_df.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
# Beginner Example 4: Calculate total revenue in online retail transactions
ori_df['Revenue'] = ori_df['Quantity'] * ori_df['Price']
total_revenue = ori_df['Revenue'].sum()
print(f'Total Revenue: {total_revenue:,.2f}')
Total Revenue: 9,747,765.93
# Beginner Example 5: Find the top 5 selling products by quantity
top_products = ori_df.groupby('Description')['Quantity'].sum().sort_values(ascending=False).head(5)
print(top_products)
Description
WORLD WAR 2 GLIDERS ASSTD DESIGNS    53847
JUMBO BAG RED RETROSPOT              47363
ASSORTED COLOUR BIRD ORNAMENT        36381
POPCORN HOLDER                       36334
PACK OF 72 RETROSPOT CAKE CASES      36039
Name: Quantity, dtype: int64
# Beginner Example 6: Average order value (AOV) for simulated orders
merged = pd.merge(orders_df, prodcat_df, how='left', left_on='ProductID', right_on='ProductID')
merged['LineRevenue'] = merged['Quantity'] * merged['Price']
aov = merged.groupby('OrderID')['LineRevenue'].sum().mean()
print(f'Average Order Value (AOV): {aov:,.2f}')
Average Order Value (AOV): 593.73
# Intermediate Example 1: Revenue by product category
merged['Category'] = merged['Category'].fillna('Unknown')
category_revenue = merged.groupby('Category')['LineRevenue'].sum().sort_values(ascending=False)
print(category_revenue)
Category
Sports         147783.07
Beauty         134105.61
Clothing       121487.00
Home            98098.72
Electronics     92256.84
Name: LineRevenue, dtype: float64
# Intermediate Example 2: Identify repeat customers in simulated orders
customer_order_counts = orders_df.groupby('CustomerID')['OrderID'].nunique()
repeat_customers = customer_order_counts[customer_order_counts > 1].count()
total_customers = customer_order_counts.count()
repeat_pct = (repeat_customers / total_customers) * 100
print(f'Repeat customers: {repeat_customers} of {total_customers} ({repeat_pct:.2f}%)')
Repeat customers: 282 of 423 (66.67%)
# Intermediate Example 3: Monthly sales trend over time
ori_df['Month'] = ori_df['InvoiceDate'].dt.to_period('M')
monthly_sales = ori_df.groupby('Month')['Revenue'].sum()
monthly_sales.plot(kind='line', marker='o', figsize=(10,4))
plt.title('Monthly Sales Trend')
plt.xlabel('Month')
plt.ylabel('Revenue')
plt.show()
No description has been provided for this image
# Intermediate Example 4: Top customers by total spend
customer_revenue = merged.groupby('CustomerID')['LineRevenue'].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: LineRevenue, dtype: float64
# Intermediate Example 5: Plot distribution of order sizes (quantity per order)
order_sizes = orders_df.groupby('OrderID')['Quantity'].sum()
sns.histplot(order_sizes, bins=10, kde=True)
plt.title('Distribution of Order Sizes')
plt.xlabel('Items per Order')
plt.ylabel('Number of Orders')
plt.show()
No description has been provided for this image
# Advanced Example 1: Basket analysis - Most frequently co-purchased products
basket_df = ori_df[ori_df['Invoice'].duplicated(keep=False)]
invoice_products = basket_df.groupby('Invoice')['Description'].apply(list)
from collections import Counter
pair_counter = Counter()
for prod_list in invoice_products:
    prod_set = set(prod_list)
    for prod1 in prod_set:
        for prod2 in prod_set:
            if prod1 < prod2:
                pair_counter[(prod1, prod2)] += 1
print(pair_counter.most_common(5))
[(('JUMBO BAG PINK POLKADOT', 'JUMBO BAG RED RETROSPOT'), 833), (('GREEN REGENCY TEACUP AND SAUCER', 'ROSES REGENCY TEACUP AND SAUCER '), 784), (('JUMBO BAG RED RETROSPOT', 'JUMBO STORAGE BAG SUKI'), 733), (('JUMBO BAG RED RETROSPOT', 'JUMBO SHOPPER VINTAGE RED PAISLEY'), 683), (('LUNCH BAG  BLACK SKULL.', 'LUNCH BAG RED RETROSPOT'), 648)]
# Advanced Example 2: Simple product demand forecasting using rolling sales average
ori_df_sorted = ori_df.sort_values('InvoiceDate')
product_demand = ori_df_sorted.groupby(['Description','InvoiceDate'])['Quantity'].sum().reset_index()
forecast_product = product_demand[product_demand['Description']==product_demand['Description'].iloc[0]].copy()
forecast_product['7D_MA'] = forecast_product['Quantity'].rolling(window=7, min_periods=1).mean()
plt.figure(figsize=(12,5))
plt.plot(forecast_product['InvoiceDate'], forecast_product['Quantity'], label='Daily Sales')
plt.plot(forecast_product['InvoiceDate'], forecast_product['7D_MA'], label='7-Day Moving Avg', linewidth=3)
plt.title(f"Demand Forecast for {forecast_product['Description'].iloc[0]}")
plt.xlabel('Date')
plt.ylabel('Units Sold')
plt.legend()
plt.show()
No description has been provided for this image
# Advanced Example 3: Customer segmentation using total spend (K-Means clustering)
from sklearn.cluster import KMeans
spend_df = customer_revenue.reset_index().rename(columns={'LineRevenue':'TotalSpend'})
X = spend_df[['TotalSpend']]
kmeans = KMeans(n_clusters=3, random_state=42)
spend_df['Segment'] = kmeans.fit_predict(X)
sns.boxplot(data=spend_df, x='Segment', y='TotalSpend')
plt.title('Customer Segments by Total Spend')
plt.xlabel('Segment')
plt.ylabel('Total Spend')
plt.show()
No description has been provided for this image
# Error Handling Example 1: Check and handle missing prices in product catalog
missing_price = prodcat_df['Price'].isnull().sum()
if missing_price > 0:
    print(f'Missing product prices: {missing_price}')
    prodcat_df['Price'] = prodcat_df['Price'].fillna(prodcat_df['Price'].median())
    print('Filled missing prices with median. New null count:', prodcat_df['Price'].isnull().sum())
else:
    print('No missing product prices.')
No missing product prices.
# Error Handling Example 2: Preventing double-counting when aggregating quantity
test_sku = ori_df['Description'].iloc[0]
sku_orders = ori_df[ori_df['Description']==test_sku].groupby('Invoice')['Quantity'].sum()
print(sku_orders.head())
Invoice
536365     6
536373     6
536375     6
536390    64
536394    32
Name: Quantity, dtype: int64
# Error Handling Example 3: Checking negative quantities (possible returns or entry errors)
neg_qty = ori_df[ori_df['Quantity'] < 0]
print(f'Negative quantity transactions: {neg_qty.shape[0]}')
print(neg_qty.head(2))
Negative quantity transactions: 10624
     Invoice StockCode                      Description  Quantity  \
141  C536379         D                         Discount        -1   
154  C536383    35004C  SET OF 3 COLOURED  FLYING DUCKS        -1   

            InvoiceDate  Price  Customer ID         Country  Revenue    Month  
141 2010-12-01 09:41:00  27.50      14527.0  United Kingdom   -27.50  2010-12  
154 2010-12-01 09:49:00   4.65      15311.0  United Kingdom    -4.65  2010-12  
# Error Handling Example 4: Detecting incorrect revenue calculations
invalid_revenue = ori_df[(ori_df['Revenue'] != ori_df['Quantity'] * ori_df['Price'])]
print(f'Rows with incorrect revenue: {invalid_revenue.shape[0]}')
Rows with incorrect revenue: 0

Best Practices: Powerful Retail Analytics Patterns#

  • Segmenting customers improves marketing and customer retention.
  • Analyzing product/category performance reveals top drivers of revenue.
  • Basket analysis supports cross-selling and product bundling.
  • Forecasting helps prevent stockouts and reduce excess inventory.
  • Detecting seasonality in data guides promotional timing.
# Pattern Example: Repeat vs. one-time buyers analysis
orders_df['RepeatBuyer'] = orders_df.duplicated(subset=['CustomerID'], keep=False)
repeat_counts = orders_df.groupby('RepeatBuyer')['OrderID'].nunique()
print(repeat_counts)
RepeatBuyer
False    141
True     859
Name: OrderID, dtype: int64
# Pattern Example: Product performance report by category
cat_perf = merged.groupby('Category')['LineRevenue'].agg(['sum','mean','count'])
cat_perf = cat_perf.rename(columns={'sum':'TotalRevenue','mean':'AvgRevenue','count':'NumLines'})
print(cat_perf.sort_values('TotalRevenue', ascending=False))
             TotalRevenue  AvgRevenue  NumLines
Category                                       
Sports          147783.07  545.324982       271
Beauty          134105.61  694.847720       193
Clothing        121487.00  604.412935       201
Home             98098.72  616.973082       159
Electronics      92256.84  524.186591       176
# Pattern Example: Simple seasonality detection for top-selling product
top_prod = top_products.index[0]
tp_sales = ori_df[ori_df['Description']==top_prod].copy()
tp_sales.set_index('InvoiceDate', inplace=True)
monthly_tp = tp_sales.resample('M')['Quantity'].sum()
monthly_tp.plot(marker='o')
plt.title(f'Seasonality for Top Product: {top_prod}')
plt.ylabel('Units Sold')
plt.xlabel('Month')
plt.show()
No description has been provided for this image
# END-TO-END MINI-PROBLEM: Find high-value and low-value customers
spend_per_customer = merged.groupby('CustomerID')['LineRevenue'].sum().sort_values(ascending=False)
top_customers = spend_per_customer.head(3)
low_customers = spend_per_customer.tail(3)
print('Top 3 Customers by Total Spend:')
print(top_customers)
print('\nLowest 3 Customers by Total Spend:')
print(low_customers)
recommendation = 'Focus loyalty campaigns and special offers on high-value customers. Explore ways to increase engagement from low-value or new customers.'
print('Recommendation:', recommendation)
Top 3 Customers by Total Spend:
CustomerID
1098    5808.86
1053    5621.52
1222    4999.08
Name: LineRevenue, dtype: float64

Lowest 3 Customers by Total Spend:
CustomerID
1374    21.04
1117    21.04
1213    10.52
Name: LineRevenue, dtype: float64
Recommendation: Focus loyalty campaigns and special offers on high-value customers. Explore ways to increase engagement from low-value or new customers.
 

Found this useful?

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