Clustering

Imagine a pile of customer purchase records lands on your desk — how much each person spent, how often they visited, what they bought — but nobody has told you what "types" of customer exist. There's no column labelled "occasional big spender" or "frequent bargain hunter." Nobody has sorted them for you at all.

Step into the unsupervised world: no labels, just raw data. Clustering is the task of discovering the natural groups hiding inside it anyway — points that sit close together in a bundle, separated from other bundles. The algorithm gets no answer key, no hint about how many groups it should find or what to call them; it has to notice the structure entirely on its own, purely from how the points are arranged.

It's everywhere once you start looking: grouping customers into segments, organising news articles into topics nobody named in advance, spotting friend groups inside a social network, even compressing the millions of colours in a photo down to a tidy palette. Whenever the question is "what natural categories are secretly in this data?", clustering is the tool you reach for.

The core idea: close together, or far apart?

Underneath every clustering method sits one simple idea: define a notion of distance (or, equivalently, similarity) between two data points, then group together whatever is close and separate whatever is far. If each data point is a vector of measurements — say, (spending amount, visit frequency) for a shopper — then "distance" can be the ordinary, geometric straight-line distance you already know: the magnitude of the difference between two points' vectors, built out of the very same dot product machinery you've already met.

For two points p = (p_1, p_2) and q = (q_1, q_2), that distance is:

d(p, q) = \sqrt{(p_1 - q_1)^2 + (p_2 - q_2)^2}

Small d(p, q) means the two points are practically neighbours and belong together; large d(p, q) means they're strangers. Every clustering algorithm is, underneath, just a clever set of rules for turning millions of these little distance checks into a small number of sensible groups.

Straight-line distance isn't the only option, either — it's simply the most natural one to start with. For text documents or user-preference vectors, "closeness" is often measured instead as the angle between two vectors rather than the gap between their tips (the same dot-product machinery gives you that angle too), because two customers who buy the same proportions of things, just at different volumes, should probably count as similar. The exact recipe for "distance" changes from problem to problem; what never changes is the basic move — pick a notion of closeness, then group whatever comes out close together.

Worked example: sorting five points by eye

Before trusting any algorithm, try it by hand. Here are five points on a simple grid:

A quick distance check confirms what your eye probably already saw: A and B are less than a unit and a half apart, and C and D are similarly close to each other — but the two pairs are roughly ten units apart from one another. Point E sits almost exactly halfway between the two pairs, closer to neither. The sensible grouping is \{A, B\}, \{C, D\}, and E either joining whichever pair it's marginally nearer to, or standing as its own small cluster — and notice that this is already a judgement call, with only five points and no algorithm in sight yet.

Now scale that up: a real retailer's shopper data might place thousands of customers by (spending amount, visit frequency). A cluster sitting at (high spending, low frequency) is quietly telling you "occasional big spenders" exist; a cluster at (low spending, high frequency) reveals "frequent small spenders." Nobody typed those labels in anywhere — the algorithm never sees the words "big spender" at all. It only ever sees coordinates and distances; the human reading the result afterwards is the one who attaches a sensible name to each discovered group.

Here's that same idea as runnable code: given two candidate cluster centres, a new shopper is assigned to whichever one it's closer to, using nothing more than the distance formula above.

function distance(a: [number, number], b: [number, number]): number { const dx = a[0] - b[0]; const dy = a[1] - b[1]; return Math.sqrt(dx * dx + dy * dy); } // Two cluster centres discovered so far: "big spenders" and "frequent small spenders". const bigSpenders: [number, number] = [90, 2]; // (spend, visits per month) const frequentSmall: [number, number] = [15, 12]; const shopper: [number, number] = [80, 4]; const dToBig = distance(shopper, bigSpenders); const dToFrequent = distance(shopper, frequentSmall); console.log("distance to big spenders:", dToBig.toFixed(1)); console.log("distance to frequent small spenders:", dToFrequent.toFixed(1)); console.log("assigned cluster:", dToBig < dToFrequent ? "big spenders" : "frequent small spenders");

Run it and the shopper — who spends a lot but visits rarely — lands in the "big spenders" cluster, simply because their coordinates sit closer to that centre than to the other one. Nothing here knows or cares what the word "spender" even means; it's pure arithmetic on coordinates.

How many groups?

These points clearly fall into clumps — but how many? Choose the number of clusters k and the points colour by which group they join. Pick the k that matches the data's real structure; too few merges distinct groups, too many splits one group in half.

Unlike supervised classification, where the number of classes is simply whatever the labels say it is, clustering hands you no such certainty. Nothing in the data itself carries a tag reading "there are exactly 3 groups here" — three tight little sub-clusters might really be three genuine categories, or they might just be one broad, slightly lumpy category that an over-eager choice of k has needlessly sliced into pieces. Picking k is therefore part science, part judgement call, and a whole family of methods exists just to help make that judgement less arbitrary.

Clustering has two traps that catch out almost every newcomer:

Clustering isn't only for spreadsheets. Streaming services cluster viewers by what they actually watch, not by any genre a human editor typed in — which is exactly how a service can notice, say, a cluster of people who love both true-crime documentaries and baking competition shows, a genre-crossing "taste group" no one had ever named before it showed up in the data.

Astronomers do the same thing to the sky itself. Feed a clustering algorithm the positions, brightness and motion of millions of stars from a survey telescope, and it can pick out clumps that turn out to be genuine star clusters, previously-unrecognised streams of stars torn from a dwarf galaxy, or new structure in how galaxies group together across the universe — patterns that were sitting in the data all along, waiting for an algorithm (rather than a human squinting at a star chart) to notice they were even there.

The catch: there's no right answer

Without labels, "correct" is genuinely ambiguous — different notions of similarity give different clusterings, and choosing k is often a judgement call. That freedom is the challenge, and the charm, of unsupervised learning: rather than being told the answer and graded against it, the algorithm — and the person reading its output — must decide for themselves whether a discovered grouping is meaningful. The most popular clustering method makes the whole idea concrete with a beautifully simple loop: k-means.

See it explained