Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
⚙️ Week 8 – Advanced ML  ·  Day 46

Day 46: K-Means Clustering: Customer Segments

⏱ 2 hours📶 Intermediate🎯 100% self-study
Day 46 / 60

✅ Today you will learn

  • Clustering: groups without labels
  • K-Means step by step
  • Scale first, then choose k (elbow, silhouette)
  • Turn clusters into marketing personas
  • When to use DBSCAN or hierarchical clustering

🏢 In the real world

A Pune mall has 200 loyalty-card members with age, income and a spending score. Marketing wants to stop sending the same SMS to everyone. Nobody has labelled “types” of customers – clustering discovers them, and each group gets its own offer.

Look at the data first

Python
import pandas as pd
import matplotlib.pyplot as plt
m = pd.read_csv("mall_customers.csv")
print(m.head()); print(m.describe().round(1).loc[["mean", "min", "max"]])
plt.figure(figsize=(7.5, 4.5))
plt.scatter(m["annual_income_lakh"], m["spending_score"], s=22, color="#64748b")
plt.xlabel("annual income (₹ lakh)"); plt.ylabel("spending score (1–100)")
plt.title("Can you see the groups?"); plt.show()
customer_id gender age annual_income_lakh spending_score 0 M301 Female 56 4.6 52 1 M302 Male 35 6.6 43 2 M303 Male 28 8.5 91 3 M304 Male 58 2.5 20 4 M305 Male 53 2.4 12 age annual_income_lakh spending_score mean 38.2 5.7 48.2 min 18.0 1.0 1.0 max 64.0 10.2 96.0
Scatter of mall customers income versus spending score
Your eye already sees about five groups – K-Means will find them mathematically.

How K-Means works

  1. 1Pick k random centres
  2. 2Assign every customer to the nearest centre
  3. 3Move each centre to the average of its customers
  4. 4Repeat until centres stop moving
Assign → update → repeat
Python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
m = pd.read_csv("mall_customers.csv")
X = m[["annual_income_lakh", "spending_score"]].to_numpy()
Xs = (X - X.mean(0)) / X.std(0)
rng = np.random.default_rng(3)
centres = Xs[rng.choice(len(Xs), 5, replace=False)]
fig, ax = plt.subplots(1, 4, figsize=(14, 3.4), sharey=True)
for it in range(4):
    lab = np.argmin(((Xs[:, None, :] - centres[None]) ** 2).sum(-1), axis=1)
    ax[it].scatter(Xs[:, 0], Xs[:, 1], c=lab, cmap="tab10", s=10)
    ax[it].scatter(centres[:, 0], centres[:, 1], c="black", marker="X", s=120)
    ax[it].set_title(f"iteration {it + 1}")
    centres = np.array([Xs[lab == k].mean(0) for k in range(5)])
plt.tight_layout(); plt.show()
Four K-Means iterations with centres moving to cluster middles
Black crosses = centres. After a few rounds they settle in the middle of each group.

K-Means with scikit-learn

Python
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
m = pd.read_csv("mall_customers.csv")
X = StandardScaler().fit_transform(m[["annual_income_lakh", "spending_score"]])
km = KMeans(n_clusters=5, n_init=10, random_state=0).fit(X)
m["cluster"] = km.labels_
print(m["cluster"].value_counts().sort_index().to_dict())
print("inertia (within-cluster spread):", round(km.inertia_, 1))
{0: 26, 1: 60, 2: 40, 3: 35, 4: 39} inertia (within-cluster spread): 27.3
⚠️ Always scale

K-Means uses distances. Income (2–10 lakh) and score (1–100) are on different scales – without scaling, the spending score alone would decide the clusters.

Choosing k

Python
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
m = pd.read_csv("mall_customers.csv")
X = StandardScaler().fit_transform(m[["annual_income_lakh", "spending_score"]])
ks = range(2, 11); inertia, sil = [], []
for k in ks:
    km = KMeans(n_clusters=k, n_init=10, random_state=0).fit(X)
    inertia.append(km.inertia_); sil.append(silhouette_score(X, km.labels_))
fig, ax = plt.subplots(1, 2, figsize=(11, 3.6))
ax[0].plot(ks, inertia, marker="o", color="#4f46e5"); ax[0].set_title("Elbow: inertia drops slowly after the bend"); ax[0].set_xlabel("k")
ax[1].plot(ks, sil, marker="o", color="#16a34a"); ax[1].set_title("Silhouette: higher is better"); ax[1].set_xlabel("k")
best = list(ks)[sil.index(max(sil))]; ax[1].axvline(best, color="#dc2626", ls="--")
plt.tight_layout(); plt.show()
print("best k by silhouette:", best, "| scores:", [round(s, 3) for s in sil])
best k by silhouette: 5 | scores: [0.4, 0.5, 0.606, 0.681, 0.592, 0.532, 0.46, 0.454, 0.459]
Elbow curve and silhouette scores for k from 2 to 10
Both methods point to k = 5.
🧠 Silhouette in one line

For each customer: how close to its own cluster vs the next nearest cluster, from −1 (wrong group) to +1 (perfect). The average tells you how well separated the clusters are.

From clusters to personas

Python
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
m = pd.read_csv("mall_customers.csv")
X = StandardScaler().fit_transform(m[["annual_income_lakh", "spending_score"]])
m["cluster"] = KMeans(n_clusters=5, n_init=10, random_state=0).fit_predict(X)
prof = m.groupby("cluster")[["age", "annual_income_lakh", "spending_score"]].mean().round(1)
prof["customers"] = m["cluster"].value_counts().sort_index()
def name(r):
    hi_inc, hi_sp = r.annual_income_lakh > 6.5, r.spending_score > 60
    lo_inc, lo_sp = r.annual_income_lakh < 4, r.spending_score < 40
    if hi_inc and hi_sp: return "Premium spenders"
    if hi_inc and lo_sp: return "Careful earners"
    if lo_inc and hi_sp: return "Young trend-seekers"
    if lo_inc and lo_sp: return "Budget shoppers"
    return "Average regulars"
prof["persona"] = prof.apply(name, axis=1)
print(prof.to_string())
m["persona"] = m["cluster"].map(prof["persona"])
plt.figure(figsize=(8, 4.8))
for p, g in m.groupby("persona"):
    plt.scatter(g["annual_income_lakh"], g["spending_score"], s=22, label=p)
plt.xlabel("annual income (₹ lakh)"); plt.ylabel("spending score"); plt.legend(fontsize=8)
plt.title("Five customer personas"); plt.show()
age annual_income_lakh spending_score customers persona cluster 0 21.7 2.7 77.7 26 Young trend-seekers 1 40.2 5.5 48.7 60 Average regulars 2 48.2 2.5 19.3 40 Budget shoppers 3 43.0 8.4 18.5 35 Careful earners 4 31.4 9.0 83.8 39 Premium spenders
Mall customers coloured by five named personas
Give clusters human names – marketing acts on personas, not on “cluster 3”.
💎
Premium spendersHigh income, high spend → early access, premium brands, loyalty tier.
🧐
Careful earnersHigh income, low spend → quality and value messaging, personal shopper.
✨
Young trend-seekersLow income, high spend → EMI, student offers, Instagram campaigns.
🛒
Budget shoppersLow income, low spend → combo deals and weekday discounts.
One message per segment

Other clustering methods

Python
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import DBSCAN, AgglomerativeClustering
from sklearn.metrics import silhouette_score
m = pd.read_csv("mall_customers.csv")
X = StandardScaler().fit_transform(m[["annual_income_lakh", "spending_score"]])
db = DBSCAN(eps=0.35, min_samples=5).fit(X)
print("DBSCAN clusters:", len(set(db.labels_)) - (1 if -1 in db.labels_ else 0), "| noise points:", (db.labels_ == -1).sum())
ag = AgglomerativeClustering(n_clusters=5, linkage="ward").fit(X)
print("Hierarchical (ward) silhouette:", round(silhouette_score(X, ag.labels_), 3))
DBSCAN clusters: 4 | noise points: 5 Hierarchical (ward) silhouette: 0.679
MethodStrengthWeakness
K-MeansFast, simple, scales to millionsNeeds k; assumes round, similar-size clusters
DBSCANFinds any shape, flags outliers (noise)Sensitive to eps; struggles with varying density
HierarchicalDendrogram shows nested groupsSlow on large data

🏅 Job skills unlocked today

✔ Run and explain K-Means✔ Choose k with elbow and silhouette✔ Profile clusters into actionable personas✔ Know when to try DBSCAN or hierarchical clustering

Practice tasks

🏋️ Practice – do it yourself

Task 1: Cluster the mall customers on all three features (age, income, score). Does k = 5 still look best?
Show solution
Python
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
m = pd.read_csv("mall_customers.csv")
X = StandardScaler().fit_transform(m[["age", "annual_income_lakh", "spending_score"]])
print({k: round(silhouette_score(X, KMeans(k, n_init=10, random_state=0).fit_predict(X)), 3) for k in range(2, 9)})
{2: 0.367, 3: 0.397, 4: 0.429, 5: 0.478, 6: 0.461, 7: 0.474, 8: 0.482}
Task 2: Segment the 11 cities of BazaarKart by revenue, AOV and prepaid share into 3 clusters.
Show solution
Python
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
s = pd.read_csv("sales_data.csv"); s["rev"] = s["quantity"] * s["unit_price"] * (1 - s["discount"])
c = s.groupby("city").agg(revenue=("rev", "sum"), aov=("rev", "mean"), prepaid=("payment_mode", lambda p: (p != "Cash on Delivery").mean()))
c["cluster"] = KMeans(3, n_init=10, random_state=0).fit_predict(StandardScaler().fit_transform(c))
print(c.round(2).sort_values("cluster"))
revenue aov prepaid cluster city Ahmedabad 450611.90 3633.97 0.80 0 Hyderabad 572190.15 2860.95 0.78 0 Lucknow 394912.00 3014.60 0.79 0 Patna 459941.15 3650.33 0.79 0 Jaipur 260155.35 2454.30 0.83 1 Chennai 545479.20 3519.22 0.85 1 Pune 403765.60 2823.54 0.85 1 Bengaluru 964019.65 3224.15 0.81 2 Kolkata 839885.10 4351.74 0.82 2 Delhi 945592.45 3489.27 0.79 2 Mumbai 1043230.20 4139.80 0.78 2
Task 3: Write one SMS offer for each of the five personas.

Quick quiz

🧠 Quick Quiz – test yourself

1. Clustering is…
No labels are used.
2. In K-Means, after assigning points, centres move to…
Hence “means”.
3. Inertia always decreases as k increases. So we…
Balance fit and simplicity.
4. Silhouette close to 1 means…
Points are close to their own cluster.
5. Which method labels outliers as noise?
Label −1.

Interview corner

💼 Interview Corner

How do you choose the number of clusters?

Use the elbow of the inertia curve, silhouette scores, and – most importantly – business usefulness: segments must be distinct, large enough and actionable. Check stability with different seeds.

Limitations of K-Means?

Needs k in advance, sensitive to scaling and outliers, assumes spherical similar-size clusters, and can land in local minima (use n_init > 1). Categorical data needs other methods (k-modes) or encoding.

Summary

  • K-Means: assign to nearest centre, move centres, repeat.
  • Scale features; choose k with elbow + silhouette + business sense.
  • Profile clusters into personas and actions.
  • DBSCAN for odd shapes/outliers, hierarchical for nested groups.
➡️ Next

Day 47: PCA – reduce many columns to a few powerful components and visualise high-dimensional data.

Frequently Asked Questions

Can I use clustering results in a supervised model?
Yes – the cluster id (or distance to each centre) is a useful engineered feature, e.g. for churn or response models.
How do I assign a new customer to a cluster?
Scale with the same scaler, then km.predict(new_row) – put both in a Pipeline.
← Data Science (60 Days)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicDay 47: PCA & Dimensionality Reduction Your progress: 46 / 60 (77%) · Lesson 47 / 60 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free