DS701 Session 5 — Mon Sep 21, 2026
Describe the agglomerative hierarchical clustering procedure in three steps, and say what the height at which two branches join in a dendrogram represents.
Single linkage defines the distance between two clusters as the distance between their closest pair of points; complete linkage uses their farthest pair. Explain, from these definitions alone, why single linkage tends to produce long chains that can connect two well-separated groups through a few noise points, and why complete linkage tends toward compact clusters of similar diameter.
A client asks you to segment their customers but cannot tell you how many segments they want. Explain why hierarchical clustering is a natural fit here compared to \(k\)-means, and how you would still get a concrete number of segments out of it.
Last session: a strict partition — every point in exactly one of \(k\) clusters, \(k\) chosen in advance.

The output is a linkage matrix of shape \((n-1, 4)\) — one row per merge:
\[ [\ \text{idx}_1,\ \text{idx}_2,\ \text{distance},\ \text{count}\ ] \]
Indices \(0 \dots n-1\) are original points; indices \(n, n+1, \dots\) are the clusters created by earlier rows. scipy.cluster.hierarchy.linkage produces it; dendrogram draws it; fcluster cuts it.
Distance between two clusters \(C_i, C_j\) — three natural choices, plus one borrowed from \(k\)-means:

Single \[\min_{x\in C_i,\,y\in C_j} d(x,y)\]

Complete \[\max_{x\in C_i,\,y\in C_j} d(x,y)\]

Average \[\frac{1}{|C_i||C_j|}\sum_{x\in C_i,\,y\in C_j} d(x,y)\]
Ward: the increase in within-cluster sum of squares if \(C_i\) and \(C_j\) were merged — merge the pair that hurts the \(k\)-means objective least. A hierarchical, greedy \(k\)-means.
| linkage | good at | fails when | shape bias |
|---|---|---|---|
| single | connected, odd shapes (moons, rings); unequal sizes | a few noise points bridge two groups → chaining | none |
| complete | balanced, similar-diameter clusters; ignores bridges | one far outlier dominates the max | compact / spherical |
| average | a compromise; less noise- and outlier-sensitive | — | elliptical |
| ward | like \(k\)-means: robust, tidy blobs | non-convex shapes; needs Euclidean features | elliptical |

No linkage wins every row — the linkage is a modelling choice about what a cluster should look like.

Two ways to flatten a hierarchy:
fcluster(Z, h, criterion="distance"): every merge below \(h\) is done. The red and green lines are two such cuts.fcluster(Z, k, criterion="maxclust"): at most \(k\) clusters.Heights are only comparable within one dendrogram — single-linkage heights are nearest-neighbour distances, Ward’s accumulate squared error.
| \(k\)-means (partitional) | agglomerative (hierarchical) | |
|---|---|---|
| needs \(k\) up front | yes | no — cut afterwards |
| output | one partition | a tree of nested partitions |
| input | points in \(\mathbb{R}^d\) (needs means) | feature matrix or any distance / similarity matrix |
| cluster shape | spherical Voronoi cells | depends on linkage |
| cost | cheap, iterative | \(O(n^2)\) memory, \(O(n^2 \log n)\) time — fine for thousands, not millions |
| repeatability | random init | deterministic |
Reach for a dendrogram when you do not know \(k\), when the nesting itself is the answer (taxonomies), or when all you have is a distance matrix. Today’s activity: the same data, both tools, and three kinds of mess — find out for yourself which survives what.
Goal: build and read dendrograms for four linkages on a real dataset, cut them into flat clusters, then inject a unit change, two outliers and a chaining bridge and watch which linkages survive — and whether \(k\)-means would have been fine all along.
numpy, pandas, matplotlib, scipy, scikit-learn.The activity notebook goes live on the day of the lecture. Colab is optional — you can also open it on GitHub and run it locally.
Full lecture notes: Hierarchical Clustering