Mathew K Analytics

Lesson 46 · Python for Retail E-commerce Analytics

Introduction to Retail Sales Forecasting with Python for E-commerce Analytics

In this lesson, we will learn how to use Python to analyze and forecast sales in retail and e-commerce businesses. Forecasting sales helps companies plan…

⬇ 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 Retail Sales Forecasting#

  • In this lesson, we will learn how to use Python to analyze and forecast sales in retail and e-commerce businesses.
  • Forecasting sales helps companies plan inventory, set promotions, and optimize stock levels.
  • We will use real-world datasets to practice core retail analytics techniques like sales aggregation, product trend analysis, and demand forecasting.
  • You will develop actionable insights to improve sales and marketing strategies in a retail context.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Core Concepts in Retail Sales Analytics#

  • Retail datasets often have transactions (sales events), product catalogs, and customer orders.
  • Sales metrics like quantity, price, and total revenue are fundamental for analysis.
  • A common mistake is to miscalculate sales totals by omitting quantity or multiplying the wrong columns.
  • Always double-check aggregations and groupings, especially across time, product, or customer segments.
  • Clean, well-structured data leads to better and more accurate insights.
# Beginner Example 1: Load retail transactions from UCI
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00502/online_retail_II.xlsx'
df = pd.read_excel(url, sheet_name='Year 2010-2011')
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])
print('Data shape:', df.shape)
print(df.head(3))
Data shape: (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: Basic overview of columns
print('Columns:', df.columns.tolist())
print('Sample values for Invoice, Quantity, and Price:')
print(df[['Invoice', 'Quantity', 'Price']].head())
Columns: ['Invoice', 'StockCode', 'Description', 'Quantity', 'InvoiceDate', 'Price', 'Customer ID', 'Country']
Sample values for Invoice, Quantity, and Price:
  Invoice  Quantity  Price
0  536365         6   2.55
1  536365         6   3.39
2  536365         8   2.75
3  536365         6   3.39
4  536365         6   3.39
# Beginner Example 3: Calculate total sales per transaction line
df['LineTotal'] = df['Quantity'] * df['Price']
print(df[['Invoice', 'StockCode', 'Quantity', 'Price', 'LineTotal']].head())
  Invoice StockCode  Quantity  Price  LineTotal
0  536365    85123A         6   2.55      15.30
1  536365     71053         6   3.39      20.34
2  536365    84406B         8   2.75      22.00
3  536365    84029G         6   3.39      20.34
4  536365    84029E         6   3.39      20.34
# Intermediate Example 1: Aggregate total sales revenue
total_sales = df['LineTotal'].sum()
print('Total sales revenue in dataset:', round(total_sales, 2))
Total sales revenue in dataset: 9747765.93
# Intermediate Example 2: Sales per product
product_sales = df.groupby('Description')['LineTotal'].sum().sort_values(ascending=False)
print('Top 5 best-selling products by revenue:')
print(product_sales.head())
Top 5 best-selling products by revenue:
Description
DOTCOM POSTAGE                        206245.48
REGENCY CAKESTAND 3 TIER              164762.19
WHITE HANGING HEART T-LIGHT HOLDER     99668.47
PARTY BUNTING                          98302.98
JUMBO BAG RED RETROSPOT                92356.03
Name: LineTotal, dtype: float64
# Intermediate Example 3: Time-based sales analysis (monthly)
df['InvoiceMonth'] = df['InvoiceDate'].dt.to_period('M')
monthly_sales = df.groupby('InvoiceMonth')['LineTotal'].sum()
print('Sales by month:')
print(monthly_sales.head())
Sales by month:
InvoiceMonth
2010-12    748957.020
2011-01    560000.260
2011-02    498062.650
2011-03    683267.080
2011-04    493207.121
Freq: M, Name: LineTotal, dtype: float64
# Intermediate Example 4: Filter out cancelled transactions (Credit Notes start with 'C')
original_rows = df.shape[0]
df_clean = df[~df['Invoice'].astype(str).str.startswith('C')]
print('Removed', original_rows - df_clean.shape[0], 'cancelled transaction lines.')
Removed 9288 cancelled transaction lines.
# Intermediate Example 5: Re-calculate total sales after removing cancellations
final_sales = df_clean['LineTotal'].sum()
print('Total sales after removing cancellations:', round(final_sales, 2))
Total sales after removing cancellations: 10644578.42
# Intermediate Example 6: Average order value (AOV)
aov = df_clean.groupby('Invoice')['LineTotal'].sum().mean()
print('Average Order Value (AOV):', round(aov, 2))
Average Order Value (AOV): 482.44
# Advanced Example 1: Identify high-value customers
customer_sales = df_clean.groupby('Customer ID')['LineTotal'].sum().sort_values(ascending=False)
print('Top 5 customers by total spend:')
print(customer_sales.head())
Top 5 customers by total spend:
Customer ID
14646.0    280206.02
18102.0    259657.30
17450.0    194550.79
16446.0    168472.50
14911.0    143825.06
Name: LineTotal, dtype: float64
# Advanced Example 2: Sales per country
country_sales = df_clean.groupby('Country')['LineTotal'].sum().sort_values(ascending=False)
print('Top 5 countries by sales:')
print(country_sales.head())
Top 5 countries by sales:
Country
United Kingdom    9003097.964
Netherlands        285446.340
EIRE               283453.960
Germany            228867.140
France             209733.110
Name: LineTotal, dtype: float64
# Advanced Example 3: Forecasting next month's sales (naive method)
last_month = df_clean['InvoiceDate'].dt.to_period('M').max()
prev_month = last_month - 1
current_month_sales = monthly_sales.get(str(prev_month), 0)
print(f'Forecast for {last_month}:', round(current_month_sales, 2))
Forecast for 2011-12: 1461756.25
# Advanced Example 4: Detecting seasonal patterns (yearly aggregation)
df_clean['Year'] = df_clean['InvoiceDate'].dt.year
yearly_sales = df_clean.groupby('Year')['LineTotal'].sum()
print('Yearly sales totals:')
print(yearly_sales)
Yearly sales totals:
Year
2010     823746.140
2011    9820832.284
Name: LineTotal, dtype: float64
# Error Handling 1: Find missing or null values in key columns
missing = df_clean[['Invoice', 'Quantity', 'Price', 'Customer ID']].isnull().sum()
print('Missing values in key columns:')
print(missing)
Missing values in key columns:
Invoice             0
Quantity            0
Price               0
Customer ID    134697
dtype: int64
# Error Handling 2: Check for negative values in Quantity or Price
neg_qty = (df_clean['Quantity'] < 0).sum()
neg_price = (df_clean['Price'] < 0).sum()
print('Lines with negative quantity:', neg_qty)
print('Lines with negative price:', neg_price)
Lines with negative quantity: 1336
Lines with negative price: 2
# Error Handling 3: Check grouping correctness by product and date
group_check = df_clean.groupby(['Description', 'InvoiceMonth'])['LineTotal'].sum().reset_index()
print(group_check.head())
                      Description InvoiceMonth  LineTotal
0                           20713      2011-10       0.00
1   4 PURPLE FLOCK DINNER CANDLES      2010-12      45.82
2   4 PURPLE FLOCK DINNER CANDLES      2011-01       5.10
3   4 PURPLE FLOCK DINNER CANDLES      2011-02       2.55
4   4 PURPLE FLOCK DINNER CANDLES      2011-04      20.40

Best Practices in Retail Sales Analytics#

  • Segment customers based on revenue, frequency, and product mix for personalized marketing.
  • Analyze product category performance to optimize assortment and inventory.
  • Use market basket analysis to find associated items that can be bundled.
  • Apply demand forecasting methods to plan stock and prevent out-of-stocks.
  • Monitor seasonal trends and promotional impacts to inform future campaigns.
# Best Practice Example: Segment top 10% of customers by spend
threshold = customer_sales.quantile(0.9)
top_customers = customer_sales[customer_sales >= threshold]
print('Number of top 10% customers:', len(top_customers))
print('Sample top customers:')
print(top_customers.head())
Number of top 10% customers: 434
Sample top customers:
Customer ID
14646.0    280206.02
18102.0    259657.30
17450.0    194550.79
16446.0    168472.50
14911.0    143825.06
Name: LineTotal, dtype: float64
# Best Practice Example: Identify top product categories by sales value
category_sales = df_clean.groupby('StockCode')['LineTotal'].sum().sort_values(ascending=False)
print('Top product SKUs by sales:')
print(category_sales.head())
Top product SKUs by sales:
StockCode
DOT       206248.77
22423     174484.74
23843     168469.60
85123A    104518.80
47566      99504.33
Name: LineTotal, dtype: float64
# Best Practice Example: Simple demand forecasting using recent trends
recent_months = monthly_sales.tail(3)
forecast_next = recent_months.mean()
print('Simple demand forecast for next month (last 3-month avg):', round(forecast_next, 2))
Simple demand forecast for next month (last 3-month avg): 988715.64
# Mini Case Study: End-to-end  Find top-selling product and country this quarter
this_quarter = df_clean[df_clean['InvoiceDate'].dt.to_period('Q') == df_clean['InvoiceDate'].dt.to_period('Q').max()]
top_product = this_quarter.groupby('Description')['LineTotal'].sum().sort_values(ascending=False).head(1)
top_country = this_quarter.groupby('Country')['LineTotal'].sum().sort_values(ascending=False).head(1)
print('Top-selling product this quarter:')
print(top_product)
print('Top country by sales this quarter:')
print(top_country)
Top-selling product this quarter:
Description
PAPER CRAFT , LITTLE BIRDIE    168469.6
Name: LineTotal, dtype: float64
Top country by sales this quarter:
Country
United Kingdom    2856943.31
Name: LineTotal, dtype: float64
 

Found this useful?

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