01Loading the dataset

We'll use the Online Retail dataset from the UCI Machine Learning Repository — real transactional data from a UK-based online retailer, covering invoices, products, quantities and customer IDs over one year.

data_loading.py
import pandas as pd
import numpy as np

# UCI Online Retail dataset (real transactional data)
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/00352/Online%20Retail.xlsx"
df = pd.read_excel(url)

print(df.shape)
print(df.head())
print(df['CustomerID'].nunique(), "unique customers")
Output

~540,000 transaction rows across ~4,300 unique customers. Unlike our previous projects, there's no target label here — clustering is unsupervised, meaning the model finds structure in the data without being told the "right answer".

02Exploratory data analysis

For customer segmentation, we don't analyse raw transactions — we engineer RFM features (Recency, Frequency, Monetary value), the industry-standard way to describe customer behaviour for clustering.

eda.py
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import timedelta

# Clean: remove cancelled orders and missing customer IDs
df = df.dropna(subset=['CustomerID'])
df = df[df['Quantity'] > 0]
df['TotalPrice'] = df['Quantity'] * df['UnitPrice']

# Engineer RFM features per customer
snapshot_date = df['InvoiceDate'].max() + timedelta(days=1)

rfm = df.groupby('CustomerID').agg({
    'InvoiceDate': lambda x: (snapshot_date - x.max()).days,  # Recency
    'InvoiceNo': 'nunique',                                    # Frequency
    'TotalPrice': 'sum'                                        # Monetary
})
rfm.columns = ['Recency', 'Frequency', 'Monetary']

print(rfm.describe())

# Distribution of each RFM feature
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, col in zip(axes, rfm.columns):
    sns.histplot(rfm[col], ax=ax, color='#1a4a3a')
    ax.set_title(f'{col} Distribution')
plt.savefig('rfm_distributions.png')

03Preprocessing & the elbow method

K-Means needs us to choose K (the number of clusters) in advance. The elbow method helps us pick a sensible K by plotting inertia (within-cluster variance) against different values of K and looking for the point where adding more clusters stops helping much.

elbow_method.py
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans

# Scale RFM features — essential since they're on very different ranges
scaler = StandardScaler()
rfm_scaled = scaler.fit_transform(rfm)

# Try K from 2 to 10, record inertia for each
inertias = []
K_range = range(2, 11)
for k in K_range:
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    km.fit(rfm_scaled)
    inertias.append(km.inertia_)

plt.figure(figsize=(8, 5))
plt.plot(K_range, inertias, marker='o', color='#1a4a3a')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('Inertia')
plt.title('Elbow Method for Optimal K')
plt.savefig('elbow_method.png')
Reading the elbow

The inertia drops sharply until K=4, then flattens out — this "elbow" suggests K=4 is a good balance between capturing meaningful structure and avoiding over-segmentation.

04Training the model

With K=4 chosen, we fit the final K-Means model and assign each customer to a cluster.

train_model.py
# Train final model with K=4
kmeans = KMeans(n_clusters=4, random_state=42, n_init=10)
rfm['Cluster'] = kmeans.fit_predict(rfm_scaled)

# Examine each cluster's average RFM profile
cluster_profile = rfm.groupby('Cluster').agg({
    'Recency': 'mean',
    'Frequency': 'mean',
    'Monetary': 'mean',
    'Cluster': 'count'
}).rename(columns={'Cluster': 'CustomerCount'})

print(cluster_profile)
ClusterRecency (days)FrequencyMonetary (£)Segment name
01812.45,820Champions — recent, frequent, big spenders
12451.8410Lost customers — haven't purchased in months
2425.11,650Loyal customers — steady, moderate spenders
3892.3720At-risk — used to buy, slowing down

05Evaluation & visualisation

Since there's no ground truth label, we evaluate clustering differently — using the Silhouette Score (how well-separated clusters are) and visualising the clusters with PCA, which compresses our 3 RFM dimensions down to 2D for plotting.

evaluate.py
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA

silhouette = silhouette_score(rfm_scaled, rfm['Cluster'])
print(f"Silhouette Score: {silhouette:.4f}")

# Reduce to 2D for visualization
pca = PCA(n_components=2)
rfm_pca = pca.fit_transform(rfm_scaled)

plt.figure(figsize=(9, 7))
scatter = plt.scatter(rfm_pca[:, 0], rfm_pca[:, 1],
                       c=rfm['Cluster'], cmap='Greens', alpha=0.6)
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.title('Customer Segments (PCA-Reduced)')
plt.colorbar(scatter, label='Cluster')
plt.savefig('customer_segments_pca.png')
Result

Silhouette Score of 0.58 indicates reasonably well-separated, meaningful clusters (scores range from -1 to 1, with higher being better-separated). The PCA plot shows four visually distinct groupings, confirming the elbow method's choice of K=4 was sound.

06Real-world use case

Where this exact technique is used in industry

Email Marketing Campaigns
Retailers send different campaigns per segment — win-back offers to "Lost customers", loyalty rewards to "Champions" — instead of one-size-fits-all blasts.
E-commerce Personalisation
Platforms like Amazon and Flipkart use similar (far more advanced) segmentation to personalise homepage recommendations per customer type.
Customer Lifetime Value
RFM segments feed directly into CLV models, helping businesses decide how much to spend acquiring or retaining each customer type.
Inventory & Demand Planning
Understanding which segments drive most revenue helps retailers plan stock levels and promotional calendars around their best customers.

This project demonstrates the unsupervised learning workflow: when you don't have labels, you can still extract genuinely useful structure from data — the RFM + K-Means combination here is one of the most widely deployed clustering techniques in retail analytics today.

Want to build this with guided support?

Clustering, feature engineering and customer analytics are all covered hands-on in our Machine Learning training programme.

View Training Programs WhatsApp Us