promptdojo_

Clustering without labels — step 1 of 7

Clustering: structure without labels

Supervised learning needed a label column. Often you don't have one — just a pile of users, documents, or error logs — and the question is "what natural groups are in here?" That's unsupervised learning, and its workhorse is clustering.

k-means, demystified

Run the editor. The whole algorithm is two alternating moves:

  1. Assign each point to its nearest center.
  2. Update each center to the mean of its assigned points.

Repeat until the centers stop moving. Watch the run: from rough guesses (0, 4, 6), the centers move for two iterations and then freeze on the three real groups (~1, ~5, ~9) — that freeze is convergence. The sklearn version is KMeans(n_clusters=3).fit(X) — same two moves, any number of dimensions.

The two problems nobody solves for you

You choose k. The algorithm finds k groups because you asked for k groups — it will happily split 2 real clusters into 5, or mash 5 into 2. The standard aid is the elbow method: plot the total within-cluster distance ("inertia") as k grows and look for where improvement flattens. It's a heuristic, not an oracle; pair it with actually reading samples from each cluster.

Clusters have no names. k-means returns "group 0, group 1, group 2" — the meaning is your job. The professional move: for each cluster, pull ten members and the cluster's feature means, and write one sentence per cluster ("low-login, high-ticket users — the struggling cohort"). A clustering nobody can describe is a random partition with extra steps.

What it's for in real products

Customer segments for targeting, grouping similar support tickets before writing playbooks, deduplicating near-identical documents, spotting "this error log doesn't fit any cluster" anomalies. In every case clustering is a lens for humans first — it proposes structure; you verify it means something.

Where AI specifically gets this wrong

The big one first: unscaled features. Distance treats every unit equally, so an income column in dollars dwarfs logins in counts, and clusters become "income bands" no matter what else varies. Standardize features (mean 0, std 1) before clustering — generated code frequently skips this.

  • Trusting k. Cursor picks n_clusters=8 because you said "segment my users." Eight is a guess wearing confidence.
  • Skipping the read-the-clusters step. A notebook that prints cluster sizes and stops has produced numbers, not insight.