Mathew K Analytics

Lesson 13 · Python for Retail E-commerce Analytics

Customer and Order Data in Retail Systems: Python Training for E-commerce Analytics

In this lesson, we will explore retail business data focused on customers and orders. Understanding these datasets helps companies boost sales, improve…

⬇ 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

Customer and Order Data in Retail Systems#

  • In this lesson, we will explore retail business data focused on customers and orders.
  • Understanding these datasets helps companies boost sales, improve marketing, and manage inventory.
  • We will analyze real retail datasets and uncover insights like customer purchase patterns, top products, and sales trends.
  • By the end, you will know how to answer real-world questions using retail customer and order data.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Core Retail Analytics Concepts#

  • Retail data includes transactions, orders, products, and customer information.
  • Sales are measured by revenue, quantity, and price per product.
  • Common mistakes are double counting sales, forgetting to aggregate correctly, or missing customer IDs.
  • Clean, structured data leads to the most accurate business insights.
# Beginner Example 1: Load the Online Retail Transactions Dataset
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00502/online_retail_II.xlsx'
online_retail_df = pd.read_excel(url, sheet_name='Year 2010-2011')
online_retail_df['InvoiceDate'] = pd.to_datetime(online_retail_df['InvoiceDate'])
print(online_retail_df.shape)
print(online_retail_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: Loading Synthetic Retail Customer Orders Data
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')
orders_df = pd.DataFrame({'OrderID': order_ids, 'CustomerID': customer_ids, 'ProductID': 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 3: Basic Statistics on Retail Orders
print('Number of unique customers:', orders_df['CustomerID'].nunique())
print('Number of unique products ordered:', orders_df['ProductID'].nunique())
print('Average products per order:', orders_df['Quantity'].mean())
Number of unique customers: 423
Number of unique products ordered: 99
Average products per order: 2.49
# Beginner Example 4: Find the Most Common Product Ordered
top_product_id = orders_df['ProductID'].mode()[0]
top_product_count = (orders_df['ProductID'] == top_product_id).sum()
print(f'The most commonly ordered product ID: {top_product_id}')
print(f'Ordered {top_product_count} times')
The most commonly ordered product ID: 1026
Ordered 19 times
# Beginner Example 5: Total Quantity Ordered for Each Product
product_sales = orders_df.groupby('ProductID')['Quantity'].sum().reset_index()
product_sales = product_sales.sort_values('Quantity', ascending=False)
print(product_sales.head())
    ProductID  Quantity
97       1098        54
25       1026        51
16       1017        50
58       1059        48
39       1040        45
# Intermediate Example 1: Calculate Orders per Customer
orders_per_customer = orders_df.groupby('CustomerID')['OrderID'].nunique().reset_index()
orders_per_customer = orders_per_customer.rename(columns={'OrderID':'NumOrders'})
print(orders_per_customer.head())
print('Average orders per customer:', orders_per_customer['NumOrders'].mean())
   CustomerID  NumOrders
0        1000          3
1        1001          3
2        1003          2
3        1004          4
4        1005          1
Average orders per customer: 2.3640661938534278
# Intermediate Example 2: Find Repeat vs. One-Time Customers
repeat_customers = orders_per_customer[orders_per_customer['NumOrders'] > 1].shape[0]
one_time_customers = orders_per_customer[orders_per_customer['NumOrders'] == 1].shape[0]
print(f'Repeat customers: {repeat_customers}')
print(f'One-time customers: {one_time_customers}')
Repeat customers: 282
One-time customers: 141
# Intermediate Example 3: Add a Product Catalog and Join Price Info
categories = ['Electronics','Clothing','Home','Sports','Beauty']
product_ids_cat = list(range(1001,1101))
product_categories = np.random.choice(categories, 100)
product_prices = np.round(np.random.uniform(5,500,100),2)
catalog_df = pd.DataFrame({'ProductID':product_ids_cat,'Category':product_categories,'Price':product_prices})
orders_merged = orders_df.merge(catalog_df, on='ProductID', how='left')
print(orders_merged.head(3))
   OrderID  CustomerID  ProductID  Quantity           OrderDate     Category  \
0        1        1102       1049         3 2023-01-01 00:00:00       Beauty   
1        2        1435       1011         1 2023-01-01 01:00:00       Sports   
2        3        1348       1085         3 2023-01-01 02:00:00  Electronics   

    Price  
0  233.43  
1  211.00  
2  314.21  
# Intermediate Example 4: Calculate Total Revenue per Order
orders_merged['OrderRevenue'] = orders_merged['Quantity'] * orders_merged['Price']
order_revenue_table = orders_merged.groupby('OrderID')['OrderRevenue'].sum().reset_index()
print(order_revenue_table.head(3))
   OrderID  OrderRevenue
0        1        700.29
1        2        211.00
2        3        942.63
# Intermediate Example 5: Average Order Value by Customer
customer_avg_order_value = orders_merged.groupby('CustomerID')['OrderRevenue'].mean().reset_index()
customer_avg_order_value = customer_avg_order_value.rename(columns={'OrderRevenue':'AvgOrderValue'})
print(customer_avg_order_value.sort_values('AvgOrderValue', ascending=False).head())
     CustomerID  AvgOrderValue
41         1049        1960.36
415        1490        1960.36
284        1327        1937.60
194        1227        1691.84
15         1017        1658.52
# Advanced Example 1: Sales by Product Category
category_sales = orders_merged.groupby('Category')['OrderRevenue'].sum().reset_index()
category_sales = category_sales.sort_values('OrderRevenue', ascending=False)
print(category_sales)
      Category  OrderRevenue
4       Sports     191575.92
1     Clothing     122116.40
0       Beauty     103516.93
3         Home      90647.86
2  Electronics      90631.93
# Advanced Example 2: Time-Based Revenue Trend (Weekly)
orders_merged['Week'] = orders_merged['OrderDate'].dt.isocalendar().week
weekly_sales = orders_merged.groupby('Week')['OrderRevenue'].sum().reset_index()
print(weekly_sales.head())
   Week  OrderRevenue
0     1     107885.84
1     2     104385.76
2     3     104390.98
3     4      88332.38
4     5      90468.55
# Advanced Example 3: Top 5 Customers by Total Revenue
customer_total_revenue = orders_merged.groupby('CustomerID')['OrderRevenue'].sum().reset_index()
top5_customers = customer_total_revenue.sort_values('OrderRevenue', ascending=False).head(5)
print(top5_customers)
     CustomerID  OrderRevenue
45         1053       6249.65
78         1098       6108.68
320        1372       5173.33
356        1416       5021.12
215        1251       4696.86
# Advanced Example 4: Product Category Performance Over Time
orders_merged['Month'] = orders_merged['OrderDate'].dt.strftime('%Y-%m')
cat_month_sales = orders_merged.groupby(['Category','Month'])['OrderRevenue'].sum().reset_index()
print(cat_month_sales.head(10))
      Category    Month  OrderRevenue
0       Beauty  2023-01      75448.52
1       Beauty  2023-02      28068.41
2     Clothing  2023-01     101373.35
3     Clothing  2023-02      20743.05
4  Electronics  2023-01      71078.59
5  Electronics  2023-02      19553.34
6         Home  2023-01      66390.90
7         Home  2023-02      24256.96
8       Sports  2023-01     135892.91
9       Sports  2023-02      55683.01
# Advanced Example 5: Calculate Customer Retention Rate
first_purchase = orders_merged.groupby('CustomerID')['OrderDate'].min().reset_index()
latest_purchase = orders_merged.groupby('CustomerID')['OrderDate'].max().reset_index()
retained_customers = (first_purchase['OrderDate'] != latest_purchase['OrderDate']).sum()
total_customers = orders_merged['CustomerID'].nunique()
retention_rate = retained_customers / total_customers
print(f'Customer retention rate: {retention_rate:.2%}')
Customer retention rate: 66.67%
# Error Example 1: Handling Missing Product Prices
orders_merged['Price'] = orders_merged['Price'].fillna(orders_merged['Price'].mean())
print('Any missing prices after fill:', orders_merged['Price'].isnull().any())
Any missing prices after fill: False
# Error Example 2: Prevent Incorrect Grouping by OrderID and ProductID
mixed_group = orders_merged.groupby(['OrderID', 'ProductID'])['Quantity'].sum().reset_index()
print(mixed_group.head())
   OrderID  ProductID  Quantity
0        1       1049         3
1        2       1011         1
2        3       1085         3
3        4       1026         1
4        5       1063         2
# Error Example 3: Demonstrate Incorrect Revenue Calculation
incorrect_revenue = orders_merged.groupby('OrderID')['Price'].sum().reset_index()
print(incorrect_revenue.head())
   OrderID   Price
0        1  233.43
1        2  211.00
2        3  314.21
3        4  276.24
4        5  359.58
# Best Practice 1: Identify High-Value Customer Segments
customer_total = orders_merged.groupby('CustomerID')['OrderRevenue'].sum().reset_index()
high_value = customer_total[customer_total['OrderRevenue'] > customer_total['OrderRevenue'].quantile(0.75)]
print('Number of high-value customers:', high_value.shape[0])
Number of high-value customers: 106
# Best Practice 2: Find Top-Performing Products per Category
top_products_per_category = orders_merged.groupby(['Category', 'ProductID'])['OrderRevenue'].sum().reset_index()
top_products_per_category = top_products_per_category.sort_values(['Category','OrderRevenue'], ascending=[True, False]).groupby('Category').head(1)
print(top_products_per_category)
       Category  ProductID  OrderRevenue
3        Beauty       1038      16530.36
20     Clothing       1017      13499.00
43  Electronics       1039      10271.34
60         Home       1026      14088.24
80       Sports       1040      21505.05
# Best Practice 3: Market BasketMost Common Product Pairings
pair_counts = orders_merged.groupby('OrderID')['ProductID'].apply(list)
from collections import Counter
def all_pairs(product_list):
    return [(min(a,b), max(a,b)) for idx,a in enumerate(product_list) for b in product_list[idx+1:]]
all_pairs_list = pair_counts.apply(all_pairs).explode().dropna()
pair_counter = Counter(all_pairs_list)
common_pairs = pair_counter.most_common(5)
print(common_pairs)
[]
# End-to-End Retail Analytics Problem: Identify Top-Selling Products for Inventory Restock
# Step 1: Sum order quantity for each product
top_products = orders_merged.groupby('ProductID')['Quantity'].sum().reset_index()
# Step 2: Attach product category and price from catalog
top_products = top_products.merge(catalog_df, on='ProductID', how='left')
# Step 3: Get the top 5 products by ordered quantity
top_products = top_products.sort_values('Quantity', ascending=False).head(5)
print('Top 5 products to consider for restocking:')
print(top_products[['ProductID','Category','Price','Quantity']])
Top 5 products to consider for restocking:
    ProductID  Category   Price  Quantity
97       1098    Sports  302.93        54
25       1026      Home  276.24        51
16       1017  Clothing  269.98        50
58       1059    Sports  258.55        48
39       1040    Sports  477.89        45
 

Found this useful?

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