Mathew K Analytics

Lesson 1 · Pandas Projects

Analyse E-Commerce Orders with Python Pandas | Full Project Tutorial

A complete, standalone tutorial: build three related synthetic tables, merge them, and find top products and top customers with pandas. No prior pandas…

⬇ 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

Pandas for E-commerce: Analyzing Orders, Customers, and Products#

  • A complete, standalone tutorial: build three related synthetic tables, merge them, and find top products and top customers with pandas.
  • No prior pandas experience needed. Let's jump straight in.

Before You Start#

  • Open a new Jupyter Notebook in VS Code and select your Python interpreter as the kernel.
  • If pandas isn't installed yet, open a terminal in VS Code and run: pip install pandas

Part 1: Building the Dataset#

import pandas as pd
import numpy as np
print(pd.__version__)
2.3.0

Building the Customers and Products Tables#

rng = np.random.default_rng(seed=88)

regions = ['North', 'South', 'East', 'West']
customers = pd.DataFrame({
    'customer_id': range(1, 41),
    'region': rng.choice(regions, size=40),
    'signup_date': pd.date_range('2025-01-01', periods=40, freq='9D')
})
customers.head()
customer_id region signup_date
0 1 East 2025-01-01
1 2 North 2025-01-10
2 3 West 2025-01-19
3 4 East 2025-01-28
4 5 East 2025-02-06
products = pd.DataFrame({
    'product_id': range(101, 121),
    'product_name': [f'Product {chr(65 + i % 26)}{i}' for i in range(20)],
    'category': rng.choice(['Electronics', 'Home', 'Apparel', 'Outdoors'], size=20),
    'price': rng.uniform(8, 250, size=20).round(2)
})
products.head()
product_id product_name category price
0 101 Product A0 Electronics 127.33
1 102 Product B1 Home 246.44
2 103 Product C2 Electronics 89.05
3 104 Product D3 Outdoors 166.71
4 105 Product E4 Outdoors 88.22

Generating Orders#

n_orders = 500
order_rows = []
order_dates = pd.date_range('2026-01-01', periods=200, freq='D')

for order_id in range(20001, 20001 + n_orders):
    customer_id = int(rng.choice(customers['customer_id']))
    product_id = int(rng.choice(products['product_id']))
    quantity = int(rng.integers(1, 5))
    order_date = rng.choice(order_dates)
    order_rows.append([order_id, customer_id, product_id, quantity, order_date])

orders = pd.DataFrame(order_rows, columns=['order_id', 'customer_id', 'product_id', 'quantity', 'order_date'])
print(orders.shape)
(500, 5)
customers.to_csv('customers.csv', index=False)
products.to_csv('products.csv', index=False)
orders.to_csv('orders.csv', index=False)
customers = pd.read_csv('customers.csv', parse_dates=['signup_date'])
products = pd.read_csv('products.csv')
orders = pd.read_csv('orders.csv', parse_dates=['order_date'])
orders.head()
order_id customer_id product_id quantity order_date
0 20001 3 109 2 2026-04-06
1 20002 31 119 3 2026-03-14
2 20003 31 110 3 2026-04-05
3 20004 17 107 2 2026-04-13
4 20005 36 104 3 2026-02-18

Part 2: First Look at the Data#

print('customers:', customers.shape)
print('products:', products.shape)
print('orders:', orders.shape)
customers: (40, 3)
products: (20, 4)
orders: (500, 5)
orders.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 500 entries, 0 to 499
Data columns (total 5 columns):
 #   Column       Non-Null Count  Dtype         
---  ------       --------------  -----         
 0   order_id     500 non-null    int64         
 1   customer_id  500 non-null    int64         
 2   product_id   500 non-null    int64         
 3   quantity     500 non-null    int64         
 4   order_date   500 non-null    datetime64[ns]
dtypes: datetime64[ns](1), int64(4)
memory usage: 19.7 KB

Part 3: Merging All Three Tables#

full_orders = orders.merge(customers, on='customer_id', how='left').merge(products, on='product_id', how='left')
full_orders['revenue'] = full_orders['quantity'] * full_orders['price']
full_orders[['order_id', 'region', 'product_name', 'category', 'quantity', 'price', 'revenue']].head()
order_id region product_name category quantity price revenue
0 20001 West Product I8 Outdoors 2 190.83 381.66
1 20002 East Product S18 Electronics 3 136.96 410.88
2 20003 East Product J9 Electronics 3 175.23 525.69
3 20004 South Product G6 Home 2 40.80 81.60
4 20005 East Product D3 Outdoors 3 166.71 500.13
print(full_orders.shape)
full_orders.isna().sum().sum()
(500, 11)
np.int64(0)

Part 4: Top Products and Top Customers#

top_products = full_orders.groupby('product_name')['revenue'].sum().sort_values(ascending=False).head(5)
top_products.round(2)
product_name
Product B1     16757.92
Product D3     14170.35
Product N13    13357.52
Product Q16    12217.72
Product I8     12022.29
Name: revenue, dtype: float64
customer_totals = full_orders.groupby(['customer_id', 'region'])['revenue'].agg(['sum', 'count']).reset_index()
customer_totals.columns = ['customer_id', 'region', 'total_spent', 'order_count']
top_customers = customer_totals.sort_values('total_spent', ascending=False).head(5)
top_customers.round(2)
customer_id region total_spent order_count
30 31 East 7586.79 22
21 22 South 5995.97 19
2 3 West 5988.10 16
38 39 South 5615.47 19
22 23 South 5458.51 16

Part 5: Simple Customer Segmentation#

def segment(row):
    if row['order_count'] >= 15 and row['total_spent'] >= 800:
        return 'VIP'
    elif row['order_count'] >= 5:
        return 'Regular'
    else:
        return 'Occasional'

customer_totals['segment'] = customer_totals.apply(segment, axis=1)
customer_totals['segment'].value_counts()
segment
Regular    28
VIP        12
Name: count, dtype: int64
segment_summary = customer_totals.groupby('segment')['total_spent'].agg(['mean', 'sum']).round(2)
segment_summary
mean sum
segment
Regular 3249.10 90974.78
VIP 5289.62 63475.45

Part 6: Regional Analysis#

region_summary = full_orders.groupby('region').agg(
    total_revenue=('revenue', 'sum'),
    orders=('order_id', 'count'),
    avg_order_value=('revenue', 'mean')
).round(2).sort_values('total_revenue', ascending=False)
region_summary
total_revenue orders avg_order_value
region
East 58475.28 189 309.39
South 40081.92 138 290.45
North 32654.11 101 323.31
West 23238.92 72 322.76

Part 7: Visualizing the Results#

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(8, 5))
top_products.plot(kind='barh', ax=ax, color='steelblue')
ax.set_title('Top 5 Products by Revenue')
ax.set_xlabel('Revenue ($)')
ax.invert_yaxis()
plt.tight_layout()
plt.savefig('top_products.png', dpi=150)
plt.close(fig)
print('Saved top_products.png')
Saved top_products.png
fig, ax = plt.subplots(figsize=(7, 5))
customer_totals['segment'].value_counts().reindex(['VIP', 'Regular', 'Occasional']).plot(kind='bar', ax=ax, color=['gold', 'steelblue', 'lightgray'])
ax.set_title('Customers by Segment')
ax.set_ylabel('Number of Customers')
plt.xticks(rotation=0)
plt.tight_layout()
plt.savefig('customer_segments.png', dpi=150)
plt.close(fig)
print('Saved customer_segments.png')
Saved customer_segments.png

Wrap-Up: What You Learned#

  • Building three related synthetic tables and saving them as separate CSV files, like a real store's database export.
  • Chaining two merge calls together to flatten multiple tables into one analysis-ready table.
  • Ranking top products and top customers by revenue with groupby.
  • Simple rule-based customer segmentation with a custom function and apply(axis=1).
  • Regional performance summaries with a multi-statistic agg.
  • Presentation-ready horizontal and vertical bar charts with matplotlib.
  • You went from three raw synthetic tables to a full e-commerce report with top sellers and customer segments. If you want the next dataset in this series to land in your feed automatically, subscribing is the move see you in the next one.

Found this useful?

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