Mathew K Analytics

Lesson 11 · Python for Retail E-commerce Analytics

Structure of Retail Transaction Datasets for Data Analytics Training

Retail analytics starts with understanding how transaction data is organized. A clear dataset structure helps answer questions about sales, customers, and…

⬇ 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

Structure of Retail Transaction Datasets#

  • Retail analytics starts with understanding how transaction data is organized.
  • A clear dataset structure helps answer questions about sales, customers, and inventory.
  • Knowing the data layout makes it easier to calculate revenue, identify trends, and make better business decisions.
  • In this lesson, we will practice exploring, cleaning, and analyzing retail transaction datasets for real-world e-commerce insights.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Core Concepts: Retail Data Structures#

  • Retail datasets track transactions, products, customers, and orders.
  • Metrics like revenue come from sales price times quantity.
  • Orders usually have multiple line items (one per product per order).
  • Common beginner mistakes include not handling missing values, misinterpreting quantities or prices, and using the wrong groupings for aggregations.
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00502/online_retail_II.xlsx'
df_transactions = pd.read_excel(url, sheet_name='Year 2010-2011')
df_transactions['InvoiceDate'] = pd.to_datetime(df_transactions['InvoiceDate'])
print(df_transactions.shape)
print(df_transactions.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  
print('Available columns:', list(df_transactions.columns))
Available columns: ['Invoice', 'StockCode', 'Description', 'Quantity', 'InvoiceDate', 'Price', 'Customer ID', 'Country']
df_products = pd.DataFrame({
    'ProductID': range(1001, 1101),
    'Category': np.random.choice(['Electronics', 'Clothing', 'Home', 'Sports', 'Beauty'], 100),
    'Price': np.round(np.random.uniform(5, 500, 100), 2)
})
print(df_products.shape)
print(df_products.head(3))
(100, 3)
   ProductID  Category   Price
0       1001  Clothing  220.65
1       1002    Beauty  449.54
2       1003  Clothing  132.63
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
sales_summary = df_transactions.groupby('Country').agg({'Invoice':'nunique'}).rename(columns={'Invoice':'NumInvoices'})
sales_summary = sales_summary.sort_values('NumInvoices', ascending=False)
print(sales_summary.head(5))
                NumInvoices
Country                    
United Kingdom        23494
Germany                 603
France                  461
EIRE                    360
Belgium                 119
revenue = (df_transactions['Quantity'] * df_transactions['Price']).sum()
print(f'Total revenue in dataset: {revenue:.2f} GBP')
Total revenue in dataset: 9747765.93 GBP
category_sales = df_orders.merge(df_products, left_on='ProductID', right_on='ProductID')
cat_rev = (category_sales['Quantity'] * category_sales['Price']).groupby(category_sales['Category']).sum().sort_values(ascending=False)
print(cat_rev)
Category
Clothing       141513.56
Sports         125838.00
Electronics    117369.40
Beauty         112760.61
Home           108117.78
dtype: float64
customer_sales = df_orders.groupby('CustomerID')['Quantity'].sum().sort_values(ascending=False).head(5)
print(customer_sales)
CustomerID
1098    24
1416    20
1053    19
1057    17
1372    17
Name: Quantity, dtype: int32
orders_per_day = df_orders.groupby(df_orders['OrderDate'].dt.date)['OrderID'].nunique()
print(orders_per_day.head(7))
OrderDate
2023-01-01    24
2023-01-02    24
2023-01-03    24
2023-01-04    24
2023-01-05    24
2023-01-06    24
2023-01-07    24
Name: OrderID, dtype: int64
# Advanced: Find top revenue-generating customers
order_revenue = df_orders.merge(df_products, left_on='ProductID', right_on='ProductID')
order_revenue['Revenue'] = order_revenue['Quantity'] * order_revenue['Price']
top_customers = order_revenue.groupby('CustomerID')['Revenue'].sum().sort_values(ascending=False).head(5)
print(top_customers)
CustomerID
1416    6798.84
1098    5245.05
1038    5199.38
1379    5003.82
1159    4910.80
Name: Revenue, dtype: float64
# Advanced: Calculate average order value (AOV)
order_totals = order_revenue.groupby('OrderID')['Revenue'].sum()
aov = order_totals.mean()
print(f'Average order value: {aov:.2f} GBP')
Average order value: 605.60 GBP
# Advanced: Weekly revenue trends
order_revenue['Week'] = order_revenue['OrderDate'].dt.isocalendar().week
weekly_rev = order_revenue.groupby('Week')['Revenue'].sum()
print(weekly_rev.head(3))
Week
1    109782.58
2    101406.50
3     93371.31
Name: Revenue, dtype: float64
# Error handling: Check for missing prices
missing_prices = df_products['Price'].isnull().sum()
print(f'Products missing price: {missing_prices}')
Products missing price: 0
# Error handling: What if orders try to use products with no catalog entry?
invalid_products = df_orders[~df_orders['ProductID'].isin(df_products['ProductID'])]
print('Orders with invalid ProductIDs:', len(invalid_products))
Orders with invalid ProductIDs: 0
# Debugging: Revenue by category using wrong grouping (mistake example)
wrong_group = order_revenue.groupby('ProductID')['Revenue'].sum()
print('Number of grouped rows:', len(wrong_group))
Number of grouped rows: 99
# Best practice: Segment customers by quantity purchased
df_orders['Customer_Segment'] = pd.qcut(df_orders['Quantity'].cumsum(), 3, labels=['Low', 'Medium', 'High'])
print(df_orders[['CustomerID', 'Quantity', 'Customer_Segment']].head(7))
   CustomerID  Quantity Customer_Segment
0        1102         3              Low
1        1435         1              Low
2        1348         3              Low
3        1270         1              Low
4        1106         2              Low
5        1071         1              Low
6        1188         3              Low
# Best practice: Find top performing products for inventory planning
top_products = order_revenue.groupby('ProductID')['Quantity'].sum().sort_values(ascending=False).head(10)
print(top_products)
ProductID
1098    54
1026    51
1017    50
1059    48
1040    45
1051    43
1038    42
1062    40
1029    39
1005    39
Name: Quantity, dtype: int32
# Best practice: Simple market basket analysis - count most common product pairs
from itertools import combinations
basket = df_orders.groupby('OrderID')['ProductID'].apply(list)
pair_counts = {}
for products in basket:
    for pair in combinations(sorted(products), 2):
        pair_counts[pair] = pair_counts.get(pair, 0) + 1
sorted_pairs = sorted(pair_counts.items(), key=lambda x: x[1], reverse=True)[:5]
print(sorted_pairs)
[]
# Best practice: Basic demand forecasting using moving average
weekly = order_revenue.groupby('Week')['Quantity'].sum()
weekly_ma = weekly.rolling(4).mean()
print(weekly_ma.dropna().head(5))
Week
4     416.50
5     413.00
6     393.50
52    303.75
Name: Quantity, dtype: float64
# Best practice: Find monthly sales seasonality
order_revenue['Month'] = order_revenue['OrderDate'].dt.month
monthly_sales = order_revenue.groupby('Month')['Revenue'].sum()
print(monthly_sales)
Month
1    449906.89
2    155692.46
Name: Revenue, dtype: float64
# End-to-end example: Identify top-selling products and recommend stocking
top3 = top_products.head(3).index.tolist()
products_to_stock = df_products[df_products['ProductID'].isin(top3)]
print('Top 3 products to prioritize for stock:')
print(products_to_stock)
Top 3 products to prioritize for stock:
    ProductID     Category   Price
16       1017     Clothing   74.77
25       1026  Electronics  452.20
97       1098     Clothing  352.86
 

Found this useful?

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