A customer segmentation model that groups shoppers into clusters by spending behaviour — with the Elbow Method, 2D scatter plots, and cluster profiling.
The dataset has 200 customers with age, income, and spending score.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Download from: https://www.kaggle.com/datasets/vjchoudhary7/customer-segmentation-tutorial-in-python
df = pd.read_csv('Mall_Customers.csv')
print(df.head())
print(df.describe())
# Feature engineering: use annual income and spending score
X = df[['Annual Income (k$)', 'Spending Score (1-100)']].values
Run K-Means for K=1 to 10 and plot inertia — the "elbow" shows the best K.
from sklearn.cluster import KMeans
inertias = []
K_range = range(1, 11)
for k in K_range:
km = KMeans(n_clusters=k, random_state=42, n_init=10)
km.fit(X)
inertias.append(km.inertia_)
plt.figure(figsize=(7,4))
plt.plot(K_range, inertias, 'bo-', linewidth=2)
plt.xlabel('Number of clusters (K)')
plt.ylabel('Inertia (within-cluster sum of squares)')
plt.title('Elbow Method — Finding Optimal K')
plt.xticks(K_range)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('elbow_method.png', dpi=120)
plt.show()
print("Look for the 'elbow' — the point where inertia stops dropping sharply.")
K=5 is the elbow for this dataset — five clear customer segments.
from sklearn.cluster import KMeans
import numpy as np
K = 5
km = KMeans(n_clusters=K, random_state=42, n_init=10)
labels = km.fit_predict(X)
centers = km.cluster_centers_
# Assign cluster labels to the dataframe
df['Cluster'] = labels
COLORS = ['#e74c3c','#3b82f6','#22c55e','#f59e0b','#a855f7']
NAMES = ['Careful', 'Standard', 'Target', 'Careless', 'Sensible']
plt.figure(figsize=(8,6))
for i in range(K):
mask = labels == i
plt.scatter(X[mask,0], X[mask,1], c=COLORS[i], label=NAMES[i], s=60, alpha=0.7)
plt.scatter(centers[:,0], centers[:,1], c='black', marker='X', s=180, zorder=5, label='Centroids')
plt.xlabel('Annual Income (k$)')
plt.ylabel('Spending Score (1-100)')
plt.title('Customer Segmentation — 5 Clusters')
plt.legend()
plt.tight_layout()
plt.savefig('customer_segments.png', dpi=120)
plt.show()
Describe what makes each cluster distinct — this is the business insight.
profile = df.groupby('Cluster').agg({
'Age': 'mean',
'Annual Income (k$)': 'mean',
'Spending Score (1-100)': 'mean',
'CustomerID': 'count',
}).rename(columns={'CustomerID':'Count'}).round(1)
profile['Segment Name'] = NAMES
print(profile)
Age Annual Income (k$) Spending Score Count Segment Name Cluster 0 45.2 26.3 20.9 22 Careful 1 42.7 55.3 49.5 81 Standard 2 32.7 86.5 82.1 39 Target 3 25.3 87.8 17.6 20 Careless 4 40.0 26.3 79.4 38 Sensible
K-Means found five distinct customer personalities automatically, with no labelled data. This is unsupervised learning — the algorithm discovers structure in the data without being told what to look for.
Spotted a bug, broken code, or something that doesn't look right? Tell us what's off and we'll fix it.