Skip to content
Saed Sayad

k-Means

k-means partitions data into k clusters by iterating assign and update steps. The WCSS objective, the algorithm, and a fully worked example.

5 min read · Updated August 8, 2026

k-means partitions nn objects into kk clusters, each object belonging to the cluster whose mean (centroid) is nearest. It is the default clustering algorithm: fast, simple, and good enough often enough that every alternative is benchmarked against it.

The objective

k-means minimizes the within-cluster sum of squares (WCSS) — the total squared distance from every point to its own centroid μi\mu_i:

V=i=1kxjSi(xjμi)2V = \sum_{i=1}^{k} \sum_{x_j \in S_i} (x_j - \mu_i)^2

Exactly minimizing WCSS is NP-hard, so k-means uses Lloyd’s heuristic: an assign-and-update loop that never increases VV and stops when it stops moving.

The algorithm

  1. Choose kk in advance and pick kk initial centroids (traditionally at random; k-means++ seeds them spread out).
  2. Assign each object to its closest centroid by Euclidean distance.
  3. Update each centroid to the mean of the objects assigned to it.
  4. Repeat steps 2–3 until the assignments stop changing between rounds.
Two clusters of points in Age–Income space, each circled with its centroid marked
A converged k-means run with k = 2: every point belongs to the cluster whose centroid is nearest, and each centroid sits at its cluster's mean.

The loop is efficient — each iteration is O(nk)O(nk) — but it has two structural weaknesses: you must fix kk ahead of time, and the result is sensitive to initialization, often converging to a local optimum. The practical remedies are multiple restarts with different seeds and comparing several values of kk on a quality curve. A larger kk always lowers WCSS but eventually overfits, splitting real clusters into meaningless shards.

Worked example: grouping website visitors by age

Group n=19n = 19 visitors by age alone, with k=2k = 2 and random initial centroids c1=16c_1 = 16, c2=22c_2 = 22:

15, 15, 16, 19, 19, 20, 20, 21, 22, 28, 35, 40, 41, 42, 43, 44, 60, 61, 65

Iteration 1. Compute each age’s distance to both centroids and assign to the nearer one (ties go to cluster 2):

xix_id1d_1d2d_2Cluster
15171
15171
16061
19332
19332
20422
20422
21512
22602
281262
35–652

Updating: cluster 1 = {15, 15, 16} → c1=15.33c_1 = 15.33; cluster 2 = the remaining 16 ages → c2=36.25c_2 = 36.25.

Iterations 2–4. The centroids keep sliding as borderline points switch clusters:

Iterationc1c_1c2c_2Change
0 (init)16.0022.00
115.3336.25yes
218.5645.90yes
319.5047.89yes
419.5047.89none — converged

Between iterations 3 and 4 no assignment changes, so the loop halts. Two groups emerge: ages 15–28 (centroid 19.50) and ages 35–65 (centroid 47.89). Different initial centroids can converge elsewhere — run the algorithm several times and keep the best WCSS to see the clusters fairly.

Try it: k-means stepper

Step the assign–update loop yourself on a 2-D point set and watch the centroids converge:

Static fallback: starting from centroids 16 and 22 on the 19 ages, iteration 1 assigns {15, 15, 16} to cluster 1 and the rest to cluster 2 (new centroids 15.33 / 36.25); iteration 2 pulls the 19–22 ages into cluster 1 (18.56 / 45.90); iteration 3 adds age 28 (19.50 / 47.89); iteration 4 changes nothing. Final clusters: {15–28} and {35–65}, final centroids 19.50 and 47.89.

In practice

sklearn.cluster.KMeans defaults to kmeans++ seeding and n_init restarts — leave both on; they cost little and fix most of the local-optimum problem. Choose kk with the elbow of the WCSS curve, the silhouette score, or the gap statistic, and always scale features first (StandardScaler). For very large data, MiniBatchKMeans approximates the loop on random batches. Remember the geometry: k-means assumes roughly spherical, similar-size clusters — for elongated or interleaved shapes, use hierarchical clustering or a density-based method instead.

Common pitfalls

  • Unscaled features — one large-unit variable will own the Euclidean geometry.
  • A single run with random init — unlucky seeds land in bad local optima; always restart.
  • Reading the WCSS drop as proof of structure — WCSS falls for every larger kk, even on noise; validate with silhouette or stability.
  • Assuming the clusters are spherical and equal-size; k-means carves Voronoi cells and will happily split an elongated cluster in two.
  • Feeding it categorical or ordinal codes as if they were continuous coordinates.

Summary

k-means alternates assigning points to the nearest centroid and updating centroids to cluster means, driving the within-cluster sum of squares downhill until convergence. On the visitor ages, two restarts-free iterations settle on {15–28} and {35–65} with centroids 19.50 and 47.89. Seed well, restart often, scale first, and choose kk with a quality curve rather than a guess.