Mathew K Analytics

Lesson 14 · Python for Retail E-commerce Analytics

Pricing, Discounts, and Revenue Fields in Python for E-commerce Analytics Training

In this lesson, we will learn how to analyze product pricing, discounts, and revenue fields in retail and e-commerce data. Understanding these concepts…

⬇ 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

Retail Pricing, Discounts, and Revenue Analysis#

  • In this lesson, we will learn how to analyze product pricing, discounts, and revenue fields in retail and e-commerce data.
  • Understanding these concepts helps drive decisions around profitability, marketing, and inventory planning.
  • We will use real-world datasets to calculate and analyze retail revenue including the effects of discounts on business performance.
  • By the end, you will be able to perform hands-on price analytics and extract actionable sales insights.
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')

Key Concepts in Retail Pricing and Revenue Analytics#

  • Retail datasets record transactions (purchases), orders (groups of transactions), products, and sometimes discounts.
  • The main revenue fields are: unit price, quantity, discount, and total (gross or net) revenue.
  • A common mistake is ignoring discounts when estimating actual revenue.
  • Another frequent error is double-counting quantities or mixing up product and order groupings.
  • Understanding these data structures is essential before starting any calculations.
# Example 1: Load an online retail transactions dataset
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(df.shape)
print(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  
# Example 2: Check for missing values in price and quantity
missing_prices = df['Price'].isnull().sum()
missing_quantities = df['Quantity'].isnull().sum()
print('Missing price entries:', missing_prices)
print('Missing quantity entries:', missing_quantities)
Missing price entries: 0
Missing quantity entries: 0
# Example 3: Remove transactions with non-positive quantities or prices
clean_df = df[(df['Quantity'] > 0) & (df['Price'] > 0)].copy()
print('Shape after removing negative values:', clean_df.shape)
Shape after removing negative values: (530105, 8)
# Example 4: Calculate basic revenue field for each line item
clean_df['Revenue'] = clean_df['Quantity'] * clean_df['Price']
print(clean_df[['Invoice', 'StockCode', 'Quantity', 'Price', 'Revenue']].head(5))
  Invoice StockCode  Quantity  Price  Revenue
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
# Example 5: Simulate a discount field (10% for demonstration)
discount_rate = 0.10
clean_df['Discount'] = clean_df['Revenue'] * discount_rate
clean_df['Revenue_after_discount'] = clean_df['Revenue'] - clean_df['Discount']
print(clean_df[['Revenue', 'Discount', 'Revenue_after_discount']].head(5))
   Revenue  Discount  Revenue_after_discount
0    15.30     1.530                  13.770
1    20.34     2.034                  18.306
2    22.00     2.200                  19.800
3    20.34     2.034                  18.306
4    20.34     2.034                  18.306
# Example 6: Compute total gross and net revenue for the whole dataset
gross_revenue = clean_df['Revenue'].sum()
net_revenue = clean_df['Revenue_after_discount'].sum()
print('Total Gross Revenue:', round(gross_revenue, 2))
print('Total Net Revenue after Discounts:', round(net_revenue, 2))
Total Gross Revenue: 10666702.54
Total Net Revenue after Discounts: 9600032.29
# Example 7: Aggregate revenue by product code
product_revenue = clean_df.groupby('StockCode').agg({'Revenue':'sum', 'Revenue_after_discount':'sum'}).reset_index()
print(product_revenue.head(5))
  StockCode  Revenue  Revenue_after_discount
0     10002   759.89                 683.901
1     10080   119.09                 107.181
2     10120    40.53                  36.477
3     10125   994.84                 895.356
4     10133  1544.22                1389.798
# Example 8: Sort and display top 5 products by net revenue
top_products = product_revenue.sort_values(by='Revenue_after_discount', ascending=False).head(5)
print(top_products)
     StockCode    Revenue  Revenue_after_discount
3911       DOT  206248.77              185623.893
1235     22423  174484.74              157036.266
2390     23843  168469.60              151622.640
3539    85123A  104518.80               94066.920
2468     47566   99504.33               89553.897
# Example 9: Simulate an order table with multiple product lines
np.random.seed(42)
orders = clean_df[['Invoice', 'Customer ID', 'InvoiceDate']].drop_duplicates().reset_index(drop=True)
orders['OrderID'] = range(1, len(orders)+1)
clean_df = pd.merge(clean_df, orders[['Invoice', 'OrderID']], on='Invoice', how='left')
print(clean_df[['OrderID', 'Invoice', 'StockCode', 'Revenue_after_discount']].head(5))
   OrderID Invoice StockCode  Revenue_after_discount
0        1  536365    85123A                  13.770
1        1  536365     71053                  18.306
2        1  536365    84406B                  19.800
3        1  536365    84029G                  18.306
4        1  536365    84029E                  18.306
# Example 10: Compute order value (sum revenue lines per order)
order_values = clean_df.groupby('OrderID')['Revenue_after_discount'].sum().reset_index(name='OrderValue')
print(order_values.head(5))
   OrderID  OrderValue
0        1     125.208
1        2      19.980
2        3      63.045
3        4     250.857
4        5      16.065
# Example 11: Calculate average order value (AOV)
avg_order_value = order_values['OrderValue'].mean()
print('Average Order Value:', round(avg_order_value, 2))
Average Order Value: 482.78
# Example 12: Aggregate revenue by customer
customer_revenue = clean_df.groupby('Customer ID')['Revenue_after_discount'].sum().reset_index().rename(columns={'Revenue_after_discount':'TotalRevenue'})
print(customer_revenue.head(5))
   Customer ID  TotalRevenue
0      12346.0     69465.240
1      12347.0      3879.000
2      12348.0      1617.516
3      12349.0      1581.795
4      12350.0       300.960
# Example 13: Identify top 5 customers by net revenue
top_customers = customer_revenue.sort_values(by='TotalRevenue', ascending=False).head(5)
print(top_customers)
      Customer ID  TotalRevenue
1689      14646.0    252185.418
4201      18102.0    233691.570
3728      17450.0    175095.711
3008      16446.0    151625.250
1879      14911.0    130508.352
# Example 14: Detect and handle missing or obviously invalid entries again
missing_customer = clean_df['Customer ID'].isnull().sum()
print('Transactions without customer ID:', missing_customer)
clean_df_valid = clean_df[clean_df['Customer ID'].notnull()]
Transactions without customer ID: 133951
# Example 15: Debug common revenue grouping mistake (grouping by wrong field)
incorrect_group = clean_df.groupby('Invoice')['Revenue_after_discount'].mean()
correct_group = clean_df.groupby('Invoice')['Revenue_after_discount'].sum()
print('Example incorrect (mean):', incorrect_group.head(3).values)
print('Example correct (sum):', correct_group.head(3).values)
Example incorrect (mean): [17.88685714  9.99       20.90475   ]
Example correct (sum): [125.208  19.98  250.857]
# Example 16: Aggregate revenue by country to spot regional sales patterns
country_revenue = clean_df.groupby('Country')['Revenue_after_discount'].sum().sort_values(ascending=False).reset_index()
print(country_revenue.head(5))
          Country  Revenue_after_discount
0  United Kingdom            8.176719e+06
1     Netherlands            2.569017e+05
2            EIRE            2.561744e+05
3         Germany            2.059804e+05
4          France            1.901248e+05
# Example 17: Monthly revenue trend analysis
clean_df['Month'] = clean_df['InvoiceDate'].dt.month
monthly_revenue = clean_df.groupby('Month')['Revenue_after_discount'].sum().reset_index()
print(monthly_revenue)
    Month  Revenue_after_discount
0       1            6.272432e+05
1       2            4.790784e+05
2       3            6.737832e+05
3       4            4.865845e+05
4       5            6.963137e+05
5       6            6.857667e+05
6       7            6.487638e+05
7       8            6.839949e+05
8       9            9.561915e+05
9      10            1.040188e+06
10     11            1.362136e+06
11     12            1.316480e+06

Best Practices in Retail Revenue Analysis#

  • Validate all calculations and business groupings before analysis.
  • Always account for discounts, returns, and missing data when reporting revenue.
  • Aggregate by the right level (product, order, customer, region) to answer the intended business question.
  • Use order and product analytics to optimize promotions, stock, and pricing strategies.
  • These skills are critical for analysts, managers, and anyone driving retail decisions.
# Example 18: End-to-end problem: Identify top-selling products in a specific country after discounts
target_country = 'United Kingdom'
country_df = clean_df[clean_df['Country'] == target_country]
prod_sales = country_df.groupby('StockCode')['Revenue_after_discount'].sum().sort_values(ascending=False).head(5)
print(f'Top products in {target_country}:')
print(prod_sales)
Top products in United Kingdom:
StockCode
DOT       186839.019
23843     151622.640
22423     129036.321
85123A     89150.985
47566      85571.748
Name: Revenue_after_discount, dtype: float64
# Example 19: Segment customers by total spend after discounts (basic RFM segmentation step)
customer_spend = customer_revenue.copy()
customer_spend['Segment'] = pd.qcut(customer_spend['TotalRevenue'], 3, labels=['Low', 'Medium', 'High'])
print(customer_spend.groupby('Segment').size())
Segment
Low       1446
Medium    1446
High      1446
dtype: int64
# Example 20: Calculate the impact of a category-specific discount (simulate 20% on 'Discount Product')
sample_product = clean_df['StockCode'].value_counts().index[0]
clean_df['CategoryDiscount'] = np.where(clean_df['StockCode'] == sample_product, clean_df['Revenue'] * 0.2, 0)
clean_df['Revenue_after_category_discount'] = clean_df['Revenue_after_discount'] - clean_df['CategoryDiscount']
cat_disc_revenue = clean_df.loc[clean_df['StockCode'] == sample_product, 'Revenue_after_category_discount'].sum()
print(f'Total revenue for top product after new category discount: {cat_disc_revenue:.2f}')
Total revenue for top product after new category discount: 73396.19
# Example 21: Challenge - Compute median quantity per product and discuss implications
median_qty = clean_df.groupby('StockCode')['Quantity'].median().reset_index()
print(median_qty.sort_values('Quantity', ascending=False).head(5))
     StockCode  Quantity
2390     23843   80995.0
3066    47556B    1300.0
23       16049     144.0
18       16033     120.0
35       16259     108.0
 

Found this useful?

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