Mathew K Analytics

Lesson 10 · Python for Retail E-commerce Analytics

Loading Retail Transaction Data from CSV and Excel in Python

Learn how to load and prepare real retail transaction data for analysis. Loading data correctly is essential for making smart sales, marketing, and…

⬇ Download notebookOpen in Colab ↗

📓 Full notebook

Download .ipynb

Loading Retail Transaction Data from CSV and Excel#

  • Learn how to load and prepare real retail transaction data for analysis.
  • Loading data correctly is essential for making smart sales, marketing, and inventory decisions.
  • By the end of this lesson, you will be able to confidently load, inspect, and prepare retail datasets from CSV and Excel.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Core Concepts: Retail Data Structure and Metrics#

  • Retail datasets usually have rows for transactions, orders, products, or customers.
  • Columns often include invoice numbers, dates, product codes, quantities, prices, and customer IDs.
  • Sales metrics like revenue use quantity and price, but mixing up which is which is a common beginner mistake.
  • Always check if the data includes taxes, returns, or missing values before analysis.

Beginner Example 1: Load Online Retail Transactions from Excel#

  • We will load a real online retail transactions dataset from the UCI machine learning repository.
  • Excel is commonly used for retail data; pandas makes it easy to use.
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  

Beginner Example 2: Load Retail Product Catalog from CSV#

  • Product catalogs are often stored as CSV files for easy sharing.
  • We simulate a simple product catalog for this example.
# Simulate and save a simple product catalog to CSV
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})
df_catalog.to_csv('product_catalog.csv', index=False)
print(df_catalog.shape)
(100, 3)
# Now read the product catalog CSV into pandas
df_products = pd.read_csv('product_catalog.csv')
print(df_products.head(3))
   ProductID Category   Price
0       1001   Sports  457.91
1       1002   Beauty  425.77
2       1003     Home  227.48

Beginner Example 3: Load Retail Customer Orders from CSV#

  • Orders tables tie together customers and products over time.
  • We will use a simulated retail orders dataset for this exercise.
# Simulate and save simple retail orders to CSV
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})
df_orders.to_csv('customer_orders.csv', index=False)
print(df_orders.shape)
(1000, 5)
# Read customer orders CSV to DataFrame
df_orders_loaded = pd.read_csv('customer_orders.csv', parse_dates=['OrderDate'])
print(df_orders_loaded.head(3))
   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

Intermediate Example 1: Join Orders with Products#

  • Joining dataset tables is crucial for deeper retail analytics.
  • We will combine customer orders with product details to calculate revenue per order.
# Merge orders with product catalog
orders_products = pd.merge(df_orders_loaded, df_products, on='ProductID', how='left')
# Calculate revenue for each order line
orders_products['Revenue'] = orders_products['Quantity'] * orders_products['Price']
print(orders_products.head(3))
   OrderID  CustomerID  ProductID  Quantity           OrderDate     Category  \
0        1        1102       1049         3 2023-01-01 00:00:00     Clothing   
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  Revenue  
0  155.87   467.61  
1  194.55   194.55  
2  409.13  1227.39  

Intermediate Example 2: Summarize Total Sales per Product#

  • Aggregating sales by product helps identify top performers.
  • We will sum total revenue and quantity per product ID.
# Group by ProductID and summarize sales
product_sales_summary = orders_products.groupby('ProductID').agg({'Quantity':'sum','Revenue':'sum'}).reset_index()
product_sales_summary = pd.merge(product_sales_summary, df_products[['ProductID','Category']], on='ProductID', how='left')
print(product_sales_summary.sort_values('Revenue', ascending=False).head())
    ProductID  Quantity   Revenue     Category
39       1040        45  19899.90     Clothing
0        1001        31  14195.21       Sports
24       1025        38  13272.64  Electronics
32       1033        30  13224.90       Sports
93       1094        31  12576.70         Home

Intermediate Example 3: Analyze Sales Trends by Month#

  • Understanding sales trends over time helps spot seasonality or anomalies.
  • We will group all orders by month and sum revenue.
# Extract month and summarize sales
orders_products['Month'] = orders_products['OrderDate'].dt.to_period('M')
monthly_sales = orders_products.groupby('Month')['Revenue'].sum().reset_index()
print(monthly_sales.head())
     Month    Revenue
0  2023-01  445700.41
1  2023-02  148030.83

Advanced Example 1: Identify High-Value Customers#

  • High-value customers drive a large portion of sales.
  • We will aggregate sales by customer and rank them by revenue.
# Aggregate revenue by customer
customer_lifetime = orders_products.groupby('CustomerID')['Revenue'].sum().reset_index()
customer_lifetime = customer_lifetime.sort_values('Revenue', ascending=False)
print(customer_lifetime.head(5))
     CustomerID  Revenue
78         1098  5808.86
45         1053  5621.52
189        1222  4999.08
326        1379  4952.56
75         1095  4803.94

Advanced Example 2: Calculate Average Order Value (AOV)#

  • Average Order Value is a key metric for business health.
  • We will compute AOV by dividing total revenue by number of orders.
# Calculate total revenue and unique order count
total_revenue = orders_products['Revenue'].sum()
unique_orders = orders_products['OrderID'].nunique()
aov = total_revenue / unique_orders
print('Average Order Value: {:.2f}'.format(aov))
Average Order Value: 593.73

Advanced Example 3: Analyze Product Category Sales Contribution#

  • Knowing category-level performance helps with targeted promotions.
  • We will aggregate revenue by category and calculate its percent of total sales.
# Aggregate revenue by category
category_sales = orders_products.groupby('Category')['Revenue'].sum().reset_index()
total_category_revenue = category_sales['Revenue'].sum()
category_sales['PercentOfTotal'] = 100 * category_sales['Revenue'] / total_category_revenue
print(category_sales.sort_values('Revenue', ascending=False))
      Category    Revenue  PercentOfTotal
4       Sports  147783.07       24.890567
0       Beauty  134105.61       22.586922
1     Clothing  121487.00       20.461615
3         Home   98098.72       16.522412
2  Electronics   92256.84       15.538485

Error Handling Example 1: Missing Values in Transactions#

  • Real-world retail data often has missing or null values.
  • Let us simulate some missing values and show how to detect them.
# Introduce missing values into revenue
orders_products_with_missing = orders_products.copy()
orders_products_with_missing.loc[orders_products_with_missing.sample(frac=0.01, random_state=42).index, 'Revenue'] = np.nan
# Count missing revenue values
num_missing = orders_products_with_missing['Revenue'].isna().sum()
print(f'Missing revenue entries: {num_missing}')
Missing revenue entries: 10

Error Handling Example 2: Incorrect Grouping in Aggregations#

  • Mistakes during groupby can lead to wrong business conclusions.
  • We will show what happens if you forget to include all necessary group keys.
# Aggregate by category only (incorrect example)
wrong_group = orders_products.groupby('Category')['Revenue'].sum().reset_index()
top_category = wrong_group.sort_values('Revenue', ascending=False).head(1)
print(top_category)
  Category    Revenue
4   Sports  147783.07

Error Handling Example 3: Misinterpreting Quantities vs Revenue#

  • Confusing quantity sold and revenue can lead analysts astray.
  • Always check both metrics when ranking product performance.
# Find product with highest quantity, not revenue
most_sold = product_sales_summary.sort_values('Quantity', ascending=False).head(1)
print('Most sold product by quantity:')
print(most_sold[['ProductID','Quantity','Revenue','Category']])
Most sold product by quantity:
    ProductID  Quantity  Revenue  Category
97       1098        54  10897.2  Clothing

Best Practice: Customer Segmentation for Retail Analysis#

  • Segmenting customers helps tailor marketing and keep your best clients engaged.
  • We will split customers by low, medium, and high spend thresholds.
# Use quantiles to segment customers
spend_quantiles = customer_lifetime['Revenue'].quantile([0.33, 0.66]).values
def segment_customer(spend):
    if spend < spend_quantiles[0]:
        return 'Low'
    elif spend < spend_quantiles[1]:
        return 'Medium'
    else:
        return 'High'
customer_lifetime['Segment'] = customer_lifetime['Revenue'].apply(segment_customer)
print(customer_lifetime['Segment'].value_counts())
Segment
High      144
Low       140
Medium    139
Name: count, dtype: int64

Best Practice: Product Performance Reporting#

  • Regularly report the top and bottom performers in your product catalog.
  • This helps prioritize inventory and promotion strategies.
# Report bottom 5 products by revenue
bottom_products = product_sales_summary.sort_values('Revenue').head(5)
print(bottom_products[['ProductID','Revenue','Category']])
    ProductID  Revenue  Category
46       1047   173.58  Clothing
69       1070   512.64    Sports
59       1060   631.04  Clothing
64       1065   939.60  Clothing
95       1096   946.98      Home

End-to-End RETAIL ANALYTICS Problem: Find Your Top 3 Products and a Business Recommendation#

  • Let us build an insight from transaction data to a business action.
  • We will identify our 3 best sellers and recommend a strategy.
# Find top 3 products by revenue and give advice
top3 = product_sales_summary.sort_values('Revenue', ascending=False).head(3)
print('Top 3 Products by Revenue:')
print(top3[['ProductID','Revenue','Category']])
Top 3 Products by Revenue:
    ProductID   Revenue     Category
39       1040  19899.90     Clothing
0        1001  14195.21       Sports
24       1025  13272.64  Electronics

Next Steps for Retail Data Analysis#

  • Practice loading a dataset from your company or favorite online shop.
  • Explore more business questions using the techniques you learned today.
  • Subscribe to our YouTube channel for weekly retail analytics examples.

Found this useful?

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