Lesson 17
K-Means Clustering
Group rows without labels using KMeans, read the cluster centres and labels, choose the number of clusters with inertia, and describe each group.
Clustering is unsupervised: there is no answer column, only rows. K-means places a chosen number of centres, gives every row to its nearest centre, moves each centre to the middle of its rows, and repeats until nothing changes. The result is a group number for every row, which you then interpret yourself.
Groups without labels
make_blobs builds invented points in clear clumps, which is perfect for seeing what KMeans does. Note that fit receives only X. Setting n_init runs the algorithm several times from different starts and keeps the best.
import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
points, _ = make_blobs(n_samples=150, centers=3, cluster_std=1.1, random_state=7)
model = KMeans(n_clusters=3, n_init=10, random_state=0).fit(points)
print(model.labels_[:12])
print(np.bincount(model.labels_))Output:
[0 1 0 1 2 1 0 2 1 0 1 1]
[50 50 50]Cluster centres
cluster_centers_ holds one centre per group, with one value per feature. The group numbers are arbitrary: cluster 0 is not first or best.
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
points, _ = make_blobs(n_samples=150, centers=3, cluster_std=1.1, random_state=7)
model = KMeans(n_clusters=3, n_init=10, random_state=0).fit(points)
print(model.cluster_centers_.round(2))Output:
[[ 9.6 0.77]
[-8.39 5.47]
[-1.48 4.53]]Assign new rows
predict gives each new row the number of its nearest centre, and transform shows the distance to every centre.
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
points, _ = make_blobs(n_samples=150, centers=3, cluster_std=1.1, random_state=7)
model = KMeans(n_clusters=3, n_init=10, random_state=0).fit(points)
fresh = [[-8.0, -6.0], [0.0, 0.0]]
print(model.predict(fresh))
print(model.transform(fresh).round(1))Output:
[1 2]
[[18.9 11.5 12.4]
[ 9.6 10. 4.8]]How many clusters
inertia_ adds up each row's squared distance to its centre, so lower is tighter. It always falls as clusters are added; look for the elbow where the fall suddenly slows.
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
points, _ = make_blobs(n_samples=150, centers=3, cluster_std=1.1, random_state=7)
for n in range(1, 7):
model = KMeans(n_clusters=n, n_init=10, random_state=0).fit(points)
print(n, round(model.inertia_, 1))Output:
1 9181.9
2 1544.2
3 328.5
4 276.6
5 233.4
6 197.9The drop from 2 to 3 is still large and the drop from 3 to 4 is small, so 3 is the elbow, matching the three clumps.
Describe groups of learners
On real columns, scale first because K-means measures distance. Then attach the labels to the table and describe each group with groupby. The score was not used for clustering, yet the groups differ in it.
import numpy as np
import pandas as pd
rng = np.random.default_rng(27)
learners = pd.DataFrame({
"hours": rng.uniform(1, 12, 200).round(1),
"practice": rng.integers(0, 41, 200),
"video_minutes": rng.integers(0, 3001, 200),
"track": rng.choice(["Python", "Data", "Web"], 200),
})
bonus = learners["track"].map({"Python": 0, "Data": 6, "Web": -5})
noise = rng.normal(0, 6, 200)
learners["score"] = (
22 + learners["hours"] * 3.4 + learners["practice"] * 0.55 + bonus + noise
).clip(0, 100).round()
learners["passed"] = (learners["score"] >= 60).astype(int)
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
scaled = StandardScaler().fit_transform(learners[["hours", "practice"]])
learners["group"] = KMeans(n_clusters=3, n_init=10, random_state=0).fit_predict(scaled)
print(learners.groupby("group")[["hours", "practice", "score"]].mean().round(1))
print(learners["group"].value_counts().sort_index().tolist())Output:
hours practice score
group
0 3.2 23.3 45.8
1 9.0 31.0 69.7
2 8.4 7.7 56.5
[72, 65, 63]Key takeaways
-
Clustering finds groups in rows without any labels
-
KMeans needs n_clusters; set n_init and random_state for repeatable runs
-
labels_ gives each row's group; cluster_centers_ gives each group's centre
-
inertia_ falls as clusters are added; pick the elbow
-
Scale features first, then describe each group with groupby
-
Previous: Hyperparameter Tuning
Loading playground…
Check your understanding
K-Means Clustering Quiz
What makes clustering unsupervised?
1 / 10