Clustering in the Wild: Segmentation & Compression
Two real jobs for K-Means — segment customers into personas you can act on, and compress an image by clustering its pixel colors.
Last lesson you learned how K-Means works; this one is about what it's for. We'll run two very different applications end to end: customer segmentation, where the clusters become marketing personas, and color quantization, where the centroids become an image's color palette. Same algorithm, wildly different data — that versatility is the point.
Customer segmentation with RFM
The classic recipe for segmenting customers is RFM:
- Recency — days since the last purchase (lower = more engaged)
- Frequency — number of purchases in the period
- Monetary — total amount spent
Three numbers per customer, all computable from a plain transaction log. The workflow is: build the RFM table, scale it (the three features live on completely different ranges), cluster, and then — the step people skip — profile the clusters so they mean something to a human.
Note that we scaled before clustering but kept the original df for
profiling — standardized means like "recency = −1.3" are meaningless to a
marketing team.
Profiling: turning cluster IDs into personas
KMeans hands back anonymous labels 0, 1, 2. The actionable part of
segmentation is a groupby away — average each RFM feature per cluster and
name what you see:
That table is the deliverable. "Cluster 2" convinces nobody; "8,000 customers who used to buy monthly and haven't purchased in 7 months" gets a win-back campaign funded. Two practical notes: use the elbow/silhouette diagnostics from last lesson to pick k, but let interpretability break ties — a k where every cluster has a clean story beats a marginally better score. And if your customer table mixes in categorical columns (region, plan type), plain K-Means can't average them; look at K-Prototypes, which handles numeric and categorical features together.
Color quantization: K-Means as compression
Now the same algorithm on completely different "rows". An RGB image is just a list of pixels, each a point in 3-D color space. Cluster those points with k = 8 and the centroids form an 8-color palette; repaint every pixel with its centroid's color and you've compressed the image:
Eight colors, and the picture is still perfectly recognizable — the gradient
turns into bands (the classic "posterized" look), but the sun and hills keep
their colors because K-Means dedicated centroids to those dense pixel
clusters. Try k = 3 and k = 16 to see the quality/size trade-off.
The compression math: instead of storing 3 bytes per pixel, you store one small palette index per pixel (3 bits for 8 colors) plus the palette itself — roughly an 8× reduction here. This is exactly how GIF's 256-color mode and old 8-bit displays worked.
On a real photograph the effect is more striking. This version is for a notebook (it downloads nothing — the sample image ships with scikit-learn):
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import load_sample_image
img = load_sample_image("china.jpg") / 255.0 # (427, 640, 3)
pixels = img.reshape(-1, 3)
# Fit on a sample of pixels for speed, then label all of them
rng = np.random.default_rng(42)
sample = pixels[rng.choice(len(pixels), 5000, replace=False)]
km = KMeans(n_clusters=16, n_init=3, random_state=42).fit(sample)
quantized = km.cluster_centers_[km.predict(pixels)].reshape(img.shape)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.imshow(img); ax1.set_title("Original (~96,000 colors)"); ax1.axis("off")
ax2.imshow(quantized); ax2.set_title("16 colors"); ax2.axis("off")
plt.show()Fitting on a 5,000-pixel sample and then calling predict on all 273,280
pixels is a common trick — centroids barely move with more data, but fitting
time does.
Other jobs for a fitted K-Means
- Anomaly detection — after fitting,
km.transform(X)gives each point's distance to every centroid. Points far from all centroids fit no known pattern: flag the top 1% of minimum-distances as anomalies (fraud, sensor faults, data-entry errors). - Semi-supervised labeling — with 10,000 unlabeled images and budget to label 50, cluster into 50 groups and hand-label the point nearest each centroid, then propagate that label to the whole cluster. Far better than labeling 50 random images.
- Feature engineering — cluster distances (or the cluster ID itself) make useful input features for a downstream supervised model.
Check your understanding
Q1.In RFM segmentation, why do we cluster on scaled features but profile on the original ones?
Q2.In color quantization with k = 8, what does each row of kmeans.cluster_centers_ represent?
Q3.How does a fitted K-Means model detect anomalies?
Q4.Your customer table has a categorical 'region' column alongside RFM. Why is plain K-Means a poor fit?
Exercise: Segment with k = 4 and hunt for a new persona
Rerun the RFM segmentation from the lesson with k = 4 instead of 3.
Profile the four clusters with groupby and try to name each persona. Then
compare the silhouette scores of k = 3 and k = 4: does the data support a
fourth segment, or did K-Means just split an existing one in half?
Next up: when three features become thirty — PCA, and how to squeeze high-dimensional data down without losing the signal.