Skip to content

Hierarchical Clustering (Dendrograms)

What you’ll learn

  • agglomerative clustering as a sequence of n1n - 1 merges, traced in full on eight points
  • the four linkage rules, each computed by hand from the same distance matrix
  • how to read a dendrogram, and why the vertical axis is the only part that carries information
  • the Lance-Williams recurrence that makes all four rules one algorithm
  • the measured single-versus-Ward trade-off: ARI 1.00 / 0.00 against 0.13 / 0.93
  • how to choose a cut height without committing to kk in advance
  • the cophenetic correlation, and why O(n2)O(n^2) memory ends the party around 30,000 rows

Intuition

k-means asks you for kk before it will tell you anything. Hierarchical clustering refuses to commit. It builds every clustering at once — from nn singleton clusters down to one cluster containing everything — and hands you the whole nested family as a tree. You pick where to cut afterwards, having seen the structure.

The agglomerative (bottom-up) version is a loop of exactly three lines:

  1. Start with every point as its own cluster.
  2. Merge the two closest clusters.
  3. Repeat until one cluster remains.

The only difficulty is step 2, because “closest” between two sets of points is not defined by the distance between two points. That definition is the linkage rule, and it is the entire character of the algorithm.

diagram Diagram mermaid

There is also a divisive (top-down) version that starts with one cluster and splits recursively. It is rarely used — the first split alone is a 2n12^{n-1} search — so “hierarchical clustering” in practice means agglomerative.

The math

Let AA and BB be two clusters and d(,)d(\cdot, \cdot) the point-level distance. The four standard linkage rules define the cluster-level distance D(A,B)D(A, B):

Single linkage — the nearest pair:

Dsingle(A,B)=minaA, bBd(a,b)D_{\text{single}}(A, B) = \min_{a \in A,\ b \in B} d(a, b)

Complete linkage — the furthest pair:

Dcomplete(A,B)=maxaA, bBd(a,b)D_{\text{complete}}(A, B) = \max_{a \in A,\ b \in B} d(a, b)

Average linkage — the mean over all pairs:

Daverage(A,B)=1ABaAbBd(a,b)D_{\text{average}}(A, B) = \frac{1}{|A|\,|B|} \sum_{a \in A} \sum_{b \in B} d(a, b)

Ward linkage minimises the increase in within-cluster sum of squares caused by the merge. That increase has a closed form:

ΔWard(A,B)=ABA+Baˉbˉ2\Delta_{\text{Ward}}(A, B) = \frac{|A|\,|B|}{|A| + |B|} \lVert \bar{\mathbf{a}} - \bar{\mathbf{b}} \rVert^2

where aˉ\bar{\mathbf{a}} and bˉ\bar{\mathbf{b}} are the two centroids. SciPy reports the merge height as 2Δ\sqrt{2\Delta}, so the plotted Ward distance is

DWard(A,B)=2ABA+B  aˉbˉD_{\text{Ward}}(A, B) = \sqrt{\frac{2\,|A|\,|B|}{|A| + |B|}}\; \lVert \bar{\mathbf{a}} - \bar{\mathbf{b}} \rVert

Ward is the direct hierarchical analogue of k-means: both minimise within-cluster sum of squares, one greedily bottom-up and one by alternating optimisation. Ward requires Euclidean distance; the other three accept any metric.

One recurrence, four rules

Naively, every merge would require recomputing distances from scratch. The Lance-Williams recurrence avoids that. After merging AA and BB into ABA \cup B, the distance to any other cluster CC is:

D(AB,C)=αAD(A,C)+αBD(B,C)+βD(A,B)+γD(A,C)D(B,C)D(A \cup B, C) = \alpha_A D(A, C) + \alpha_B D(B, C) + \beta D(A, B) + \gamma \left| D(A, C) - D(B, C) \right|

Writing nAn_A, nBn_B and nCn_C for the three cluster sizes, and nT=nA+nB+nCn_T = n_A + n_B + n_C:

LinkageαA\alpha_AαB\alpha_Bβ\betaγ\gamma
Single12\tfrac{1}{2}12\tfrac{1}{2}012-\tfrac{1}{2}
Complete12\tfrac{1}{2}12\tfrac{1}{2}0+12+\tfrac{1}{2}
AveragenAnA+nB\tfrac{n_A}{n_A + n_B}nBnA+nB\tfrac{n_B}{n_A + n_B}00
WardnA+nCnT\tfrac{n_A + n_C}{n_T}nB+nCnT\tfrac{n_B + n_C}{n_T}nCnT-\tfrac{n_C}{n_T}0

Single and complete differ only in the sign of γ\gamma — half the difference, added or subtracted. That one sign is the difference between chaining and compactness.

Worked example by hand

Eight points, labelled A through H:

ABCDEFGH
x1.01.51.25.05.46.09.09.4
y1.01.22.05.05.65.11.01.6

The Euclidean distance matrix, rounded to three decimals:

ABCDEFGH
A00.5391.0205.6576.3666.4668.0008.421
B0.53900.8545.1665.8805.9557.5037.910
C1.0200.85404.8415.5325.7147.8648.210
D5.6575.1664.84100.7211.0055.6575.561
E6.3665.8805.5320.72100.7815.8415.657
F6.4665.9555.7141.0050.78105.0804.880
G8.0007.5037.8645.6575.8415.08000.721
H8.4217.9108.2105.5615.6574.8800.7210

Merge 1. The smallest off-diagonal entry is d(A,B)=0.5385d(A, B) = 0.5385. Merge them into {A,B}\{A, B\}.

Now the linkage rules diverge. The new cluster {A,B}\{A, B\} needs a distance to C. From the matrix, d(A,C)=1.0198d(A, C) = 1.0198 and d(B,C)=0.8544d(B, C) = 0.8544, so:

RuleComputationD({A,B},C)D(\{A,B\}, C)
Singlemin(1.0198, 0.8544)\min(1.0198,\ 0.8544)0.8544
Completemax(1.0198, 0.8544)\max(1.0198,\ 0.8544)1.0198
Average(1.0198+0.8544)/2(1.0198 + 0.8544)/20.9371
Ward2213×ABC\sqrt{\tfrac{2 \cdot 2 \cdot 1}{3}} \times \lVert \overline{AB} - C \rVert1.0408

For the Ward figure: AB=(1.25, 1.10)\overline{AB} = (1.25,\ 1.10) and C=(1.2, 2.0)C = (1.2,\ 2.0), so the centroid gap is 0.052+0.92=0.9014\sqrt{0.05^2 + 0.9^2} = 0.9014, and 4/3×0.9014=1.0408\sqrt{4/3} \times 0.9014 = 1.0408.

Every one of these matches SciPy exactly.

Merges 2 and 3 are the same under all four rules, because both involve singletons: d(D,E)=0.7211d(D, E) = 0.7211 and d(G,H)=0.7211d(G, H) = 0.7211.

Here is the complete Ward trace:

MergeJoinsHeightResulting cluster
1A + B0.5385{A,B}\{A, B\}
2D + E0.7211{D,E}\{D, E\}
3G + H0.7211{G,H}\{G, H\}
4F + {D,E}\{D, E\}0.9522{D,E,F}\{D, E, F\}
5C + {A,B}\{A, B\}1.0408{A,B,C}\{A, B, C\}
6{G,H}\{G, H\} + {D,E,F}\{D, E, F\}8.4013{D,E,F,G,H}\{D, E, F, G, H\}
7{A,B,C}\{A, B, C\} + rest11.9220everything

Notice the gap. The first five merges all happen below height 1.05; the sixth jumps to 8.40. That jump is the signal, and cutting anywhere between 1.05 and 8.40 gives three clusters: {A,B,C}\{A, B, C\}, {D,E,F}\{D, E, F\}, {G,H}\{G, H\} — exactly the three visual groups.

figureEight points and their Ward treematplotlib
Eight labelled points in three visual groups beside their Ward dendrogram, with a dashed line at height 4 crossing three vertical branches.Eight labelled points in three visual groups beside their Ward dendrogram, with a dashed line at height 4 crossing three vertical branches.
The leaves are the points; the height of each horizontal bar is the distance at which that merge happened. The dashed cut at height 4 crosses three branches, so it returns three clusters — the same three groups you can see on the left.

Reading a dendrogram

Three rules cover almost every mistake people make with these plots.

The vertical axis is the only quantitative axis. Height means merge distance. Read it.

Horizontal position is arbitrary. At each merge, either subtree can be drawn on the left. The same tree has 2n12^{n-1} valid drawings. Two leaves being drawn side by side means nothing unless they are joined by a low bar.

A cut is a horizontal line. The number of vertical branches it crosses is the number of clusters you get. Cut low for many small clusters, high for few large ones.

The long vertical runs are the interesting part. A branch that survives a long height interval without merging is a cluster that stayed distinct across a wide range of thresholds — evidence of real separation.

See it move

sketch Merging the two closest, over and over p5.js
Eight leaves along the bottom. Each beat merges the closest remaining pair and draws the bar at the height where it happened, building the tree from the leaves upward.

The next sketch is the one that matters: the same eight points under all four linkage rules, with the merge order and heights recomputed for each.

sketch Four linkage rules, one distance matrix p5.js
Eight fixed points. Click to cycle through single, complete, average and Ward linkage; the merge order and the heights change even though the points do not.

Watch the tallest merge as you cycle: 4.879 for single, 8.421 for complete, 6.632 for average, 11.922 for Ward. The tree’s shape changes; the data never does.

The linkage trade-off, measured

The four rules are not four flavours of the same thing. They encode genuinely different beliefs about what a cluster is, and the consequences are stark.

figureThe same four rules on two datasetsmatplotlib
A two by four grid. The top row shows the two-moons dataset clustered by single, complete, average and Ward linkage; the bottom row shows three blobs clustered the same four ways. Adjusted Rand indices are printed under each panel.A two by four grid. The top row shows the two-moons dataset clustered by single, complete, average and Ward linkage; the bottom row shows three blobs clustered the same four ways. Adjusted Rand indices are printed under each panel.
Single linkage recovers the moons perfectly (ARI 1.00) and completely fails on blobs (ARI 0.00, everything in one cluster plus two singletons). Ward is the exact reverse: 0.13 on moons, 0.93 on blobs. There is no rule that wins both.
LinkageMoons (ARI)Blobs (ARI)Character
Single1.0000-0.0000Chains along dense paths; one bridging point merges two clusters
Complete0.32780.5802Compact, roughly equal-diameter clusters; sensitive to outliers
Average0.20690.5679A compromise; the most common default outside Ward
Ward0.12500.9272Equal-variance spherical clusters; the k-means of trees

Single linkage’s failure on blobs is worth understanding. It merges whenever any two points are close, so a thin trail of points between two blobs is enough to fuse them — the chaining effect. On the moons, chaining is exactly right: each moon is a connected dense path. On blobs with a few scattered points, chaining swallows everything.

figureFour rules, four trees, one datasetmatplotlib
Four dendrograms of the same sixty points under single, complete, average and Ward linkage. The single-linkage tree is lopsided with many low merges; the Ward tree is balanced with a tall root.Four dendrograms of the same sixty points under single, complete, average and Ward linkage. The single-linkage tree is lopsided with many low merges; the Ward tree is balanced with a tall root.
Single linkage produces a lopsided comb — points join the growing chain one at a time. Complete and Ward produce balanced trees with a clear tall split at the root. The vertical scales differ by an order of magnitude: Ward's root is at 101 where single's is at 11.

Choosing where to cut

Two approaches, and they answer different questions.

By kk. Pass n_clusters=4n_clusters=4 and let the algorithm cut wherever it must. Use this when the number is fixed by something external.

By height. Pass a distance_thresholddistance_threshold and let the number of clusters fall out. Use this when you want the data to decide.

To pick a height, plot the merge distances and find where they stop being cheap. On 200 points containing 4 blobs, the last eight Ward merges are at heights:

5.777, 5.982, 6.110, 6.386, 6.788, 39.524, 40.748, 109.2075.777,\ 5.982,\ 6.110,\ 6.386,\ 6.788,\ \mathbf{39.524},\ \mathbf{40.748},\ \mathbf{109.207}

Five merges under 7, then a jump to 39.5. Anything between about 7 and 39 gives the same answer, and cutting at 20 returns exactly 4 clusters.

figureWhere the merges stop being cheapmatplotlib
A line plot of merge distance against clusters remaining, flat near 6 until 4 clusters remain then jumping to 40 and 110, beside the four-cluster scatter that a cut at height 20 produces.A line plot of merge distance against clusters remaining, flat near 6 until 4 clusters remain then jumping to 40 and 110, beside the four-cluster scatter that a cut at height 20 produces.
The plateau below 7 is merges joining nearby points inside a blob. The jump to 39.5 is the first merge that joins two whole blobs. Cutting anywhere in the gap returns 4 clusters, which is what the data contains.

Cophenetic correlation

How faithfully does the tree represent the original distances? The cophenetic distance between two points is the height at which they first end up in the same cluster. Correlate those with the original pairwise distances:

c=corr(d(i,j), coph(i,j))c = \operatorname{corr}\big(\, d(i, j),\ \text{coph}(i, j) \,\big)

On the eight-point example: single 0.9238, complete 0.9311, average 0.9398, Ward 0.9338. Average linkage wins, which is not a coincidence — average linkage optimises something close to this quantity directly. Values above about 0.75 mean the tree is a reasonable summary; well below that, the dendrogram is distorting the geometry and you should not read structure into it.

In code

agglomerative.py
import numpy as np
from sklearn.cluster import AgglomerativeClustering
 
X = np.array([[1.0, 1.0], [1.5, 1.2], [1.2, 2.0], [5.0, 5.0],
              [5.4, 5.6], [6.0, 5.1], [9.0, 1.0], [9.4, 1.6]])
 
# Option A: I know how many clusters I want.
by_k = AgglomerativeClustering(n_clusters=3, linkage="ward").fit(X)
print(by_k.labels_)                       # [0 0 0 1 1 1 2 2]
 
# Option B: I know how far apart clusters must be. n_clusters must be None.
by_height = AgglomerativeClustering(
    n_clusters=None, distance_threshold=4.0, linkage="ward"
).fit(X)
print(by_height.n_clusters_)              # 3
 
# compute_distances=True populates .distances_ so you can plot the tree
tree = AgglomerativeClustering(n_clusters=3, compute_distances=True).fit(X)
print(tree.distances_.round(4))           # [0.5385 0.7211 0.7211 0.9522 1.0408 8.4013 11.922]
agglomerative.py
import numpy as np
from sklearn.cluster import AgglomerativeClustering
 
X = np.array([[1.0, 1.0], [1.5, 1.2], [1.2, 2.0], [5.0, 5.0],
              [5.4, 5.6], [6.0, 5.1], [9.0, 1.0], [9.4, 1.6]])
 
# Option A: I know how many clusters I want.
by_k = AgglomerativeClustering(n_clusters=3, linkage="ward").fit(X)
print(by_k.labels_)                       # [0 0 0 1 1 1 2 2]
 
# Option B: I know how far apart clusters must be. n_clusters must be None.
by_height = AgglomerativeClustering(
    n_clusters=None, distance_threshold=4.0, linkage="ward"
).fit(X)
print(by_height.n_clusters_)              # 3
 
# compute_distances=True populates .distances_ so you can plot the tree
tree = AgglomerativeClustering(n_clusters=3, compute_distances=True).fit(X)
print(tree.distances_.round(4))           # [0.5385 0.7211 0.7211 0.9522 1.0408 8.4013 11.922]

For the dendrogram itself, go to SciPy — scikit-learn does not plot one.

dendrogram.py
import matplotlib.pyplot as plt
import numpy as np
from scipy.cluster.hierarchy import cophenet, dendrogram, fcluster, linkage
from scipy.spatial.distance import pdist
 
X = np.array([[1.0, 1.0], [1.5, 1.2], [1.2, 2.0], [5.0, 5.0],
              [5.4, 5.6], [6.0, 5.1], [9.0, 1.0], [9.4, 1.6]])
labels = list("ABCDEFGH")
 
Z = linkage(X, method="ward")
# Z has one row per merge: [left_id, right_id, height, size_of_new_cluster]
print(Z.round(4))
 
print("cophenetic correlation", round(cophenet(Z, pdist(X))[0], 4))   # 0.9338
print("cut at 4.0 ->", fcluster(Z, t=4.0, criterion="distance"))      # [1 1 1 3 3 3 2 2]
print("exactly 3  ->", fcluster(Z, t=3, criterion="maxclust"))        # [1 1 1 3 3 3 2 2]
 
dendrogram(Z, labels=labels)
plt.axhline(4.0, color="red", linestyle="--")
plt.ylabel("merge distance")
plt.show()
dendrogram.py
import matplotlib.pyplot as plt
import numpy as np
from scipy.cluster.hierarchy import cophenet, dendrogram, fcluster, linkage
from scipy.spatial.distance import pdist
 
X = np.array([[1.0, 1.0], [1.5, 1.2], [1.2, 2.0], [5.0, 5.0],
              [5.4, 5.6], [6.0, 5.1], [9.0, 1.0], [9.4, 1.6]])
labels = list("ABCDEFGH")
 
Z = linkage(X, method="ward")
# Z has one row per merge: [left_id, right_id, height, size_of_new_cluster]
print(Z.round(4))
 
print("cophenetic correlation", round(cophenet(Z, pdist(X))[0], 4))   # 0.9338
print("cut at 4.0 ->", fcluster(Z, t=4.0, criterion="distance"))      # [1 1 1 3 3 3 2 2]
print("exactly 3  ->", fcluster(Z, t=3, criterion="maxclust"))        # [1 1 1 3 3 3 2 2]
 
dendrogram(Z, labels=labels)
plt.axhline(4.0, color="red", linestyle="--")
plt.ylabel("merge distance")
plt.show()

linkagelinkage accepts either a raw data matrix or a condensed distance vector from pdistpdist, which is how you use a custom metric:

custom_metric.py
from scipy.cluster.hierarchy import linkage
from scipy.spatial.distance import pdist
 
# Cosine distance for text; note that ward would reject this — it needs Euclidean.
Z = linkage(pdist(X_tfidf, metric="cosine"), method="average")
custom_metric.py
from scipy.cluster.hierarchy import linkage
from scipy.spatial.distance import pdist
 
# Cosine distance for text; note that ward would reject this — it needs Euclidean.
Z = linkage(pdist(X_tfidf, metric="cosine"), method="average")

Connectivity constraints

If you already know which points could plausibly belong together — pixels that touch, cities joined by a road, samples adjacent in time — pass a connectivity matrix. Merges are then only considered between connected clusters, which both encodes the domain knowledge and drops the cost substantially.

connectivity.py
from sklearn.cluster import AgglomerativeClustering
from sklearn.neighbors import kneighbors_graph
 
# Only allow merges between points that are among each other's 10 nearest neighbours.
conn = kneighbors_graph(X, n_neighbors=10, include_self=False)
model = AgglomerativeClustering(n_clusters=4, connectivity=conn, linkage="ward").fit(X)
connectivity.py
from sklearn.cluster import AgglomerativeClustering
from sklearn.neighbors import kneighbors_graph
 
# Only allow merges between points that are among each other's 10 nearest neighbours.
conn = kneighbors_graph(X, n_neighbors=10, include_self=False)
model = AgglomerativeClustering(n_clusters=4, connectivity=conn, linkage="ward").fit(X)

Without a connectivity constraint, Ward on nn points needs the full O(n2)O(n^2) distance structure. At 30,000 rows that is around 900 million float64 entries, roughly 7 GB — the practical ceiling for this algorithm on a laptop.

algorithmAgglomerative (hierarchical) clusteringUnsupervised — hierarchical clustering

APIsklearn.cluster.AgglomerativeClustering

Assumes

  • Your linkage rule matches the cluster shape you expect
  • The distance metric is meaningful (Ward additionally requires Euclidean)
  • n is small enough for an O(n^2) distance structure, or you supply connectivity

Cost

train
O(n^2 log n) in general; O(n^2) for single linkage with the SLINK algorithm
predict
not supported — there is no rule for placing an unseen point
memory
O(n^2) without a connectivity constraint — the binding limit in practice

D(A, B) — cluster-level distance under the linkage rule; coph(i, j) — height at which i and j first join

Hyperparameters that matter

  • linkagedefault 'ward'The whole character. Ward for blobs, single for chains and manifolds, average as a compromise.
  • n_clustersdefault 2Where to cut. Set to None if you are using distance_threshold instead.
  • distance_thresholddefault NoneCut by height rather than by count; n_clusters must be None.
  • metricdefault 'euclidean'Any metric for single/complete/average. Ward accepts euclidean only.
  • connectivitydefault NoneRestricts which merges are allowed. Encodes domain adjacency and cuts the cost.
  • compute_distancesdefault FalsePopulates .distances_ so you can plot the dendrogram from the sklearn object.

Reach for it when

  • You do not know k and want to see the whole nesting before deciding
  • The domain is genuinely hierarchical — taxonomies, org charts, document topics
  • n is a few thousand and a dendrogram would be a useful deliverable
  • You have a custom distance metric and want any linkage but Ward

Look elsewhere when

  • n is above roughly 30,000 with no connectivity constraint — memory will fail
  • You need to assign new points later; refitting is the only option
  • You need robustness to a single bad merge — every merge is permanent

Pitfalls

Reading meaning into left-right order. Any subtree can be flipped. Adjacent leaves are not necessarily similar; only a low joining bar means similar.

Using Ward with a non-Euclidean metric. scikit-learn raises an error; SciPy quietly computes something meaningless. Ward’s derivation is entirely about sums of squares.

Forgetting merges are permanent. Agglomerative clustering never reconsiders. One bad early merge, caused by a single outlier bridging two groups, propagates all the way up. k-means can recover from a bad start over iterations; this cannot.

Hitting the memory wall by surprise. The O(n2)O(n^2) distance structure is fine at 5,000 rows and fatal at 50,000. Add a connectivityconnectivity graph, sample, or switch to DBSCAN.

Choosing single linkage without seeing the data. It is either the best rule available (ARI 1.00 on moons) or catastrophic (ARI 0.00 on blobs). Plot the dendrogram: a lopsided comb with no clear tall split means chaining has already happened.

Expecting predictpredict. There is no such method. The cluster definitions are relative to the training set, so labelling a new point means refitting or, pragmatically, assigning it to the nearest cluster centroid yourself.

Not scaling. Same as everywhere: distances drive every merge.

Compare

Agglomerativek-meansDBSCAN
Needs k up frontno — cut afterwardsyesno
Cluster shapelinkage-dependentspherical onlyany
Deterministicyesno (depends on seed)yes
Handles noisenonoyes
Can label new pointsnoyesno
MemoryO(n2)O(n^2)O(np)O(np)O(np)O(np)
Practical ceiling~30k rowsmillions~1M with an index
Gives a full hierarchyyesnono
quizCheck yourself
  1. Two leaves are drawn next to each other at the bottom of a dendrogram. What does that tell you?

    Show answer

    B — Nothing on its own — horizontal order is arbitrary; only a low joining bar means similar — Either subtree can be drawn on either side at every merge, so an n-leaf tree has 2^(n-1) equally valid drawings. Read the height of the bar that joins two leaves, never their horizontal proximity.

  2. Single linkage scored ARI 1.00 on two moons and -0.00 on three blobs. Why the reversal?

    Show answer

    B — Single linkage merges whenever ANY two points are close, which follows a dense curve perfectly and lets one bridging point fuse two blobs — That is the chaining effect. Each moon is a connected dense path, so chaining traces it exactly. On separated blobs with scattered points, chaining links everything into one cluster and leaves the true structure undiscovered.

  3. You need Ward linkage with cosine distance for a text corpus. What happens?

    Show answer

    B — scikit-learn raises an error, because Ward's objective is defined in terms of Euclidean sums of squares — Ward minimises the increase in within-cluster sum of squares, which is only meaningful under Euclidean geometry. For cosine distance use average or complete linkage.

  4. Your dataset has 50,000 rows and AgglomerativeClustering runs out of memory. What is the cheapest fix that keeps the method?

    Show answer

    B — Pass a kneighbors_graph as the connectivity argument so only nearby merges are considered — The O(n^2) blow-up comes from considering every pair as a merge candidate. A connectivity graph restricts candidates to actual neighbours, which drops both memory and time — and often encodes real domain structure at the same time.

  5. The cophenetic correlation of your tree is 0.42. What does that mean?

    Show answer

    B — The tree distorts the original distances badly, so structure read off it is unreliable — Cophenetic correlation compares each pair's original distance against the height at which they first merge. At 0.42 the tree is a poor summary of the geometry — try a different linkage before interpreting any of its branches.

🧪 Try It Yourself

Exercise 1 – Four linkages, one pair of clusters

Exercise 2 – Read the linkage matrix

Exercise 3 – Cut by height instead of by k

Exercise 4 – The chaining trade-off, measured

Exercise 5 – Which tree is most faithful?

Recap

  • Agglomerative clustering performs n1n - 1 merges and hands you every clustering at once; you cut afterwards.
  • The linkage rule defines set-to-set distance. On one pair from the worked matrix: single 0.8544, complete 1.0198, average 0.9371, Ward 1.0408.
  • All four are instances of the Lance-Williams recurrence; single and complete differ only in the sign of γ\gamma.
  • Ward’s tree on the eight points jumps from 1.04 to 8.40 at merge 6 — that gap is the three real groups.
  • The trade-off is real and measured: single linkage scores 1.00 on moons and 0.00 on blobs; Ward scores 0.13 and 0.93.
  • Cut by distance_thresholddistance_threshold when you want the data to decide kk; look for the plateau in the merge heights.
  • Cophenetic correlation says how faithful the tree is — average linkage won at 0.9398 here.
  • O(n2)O(n^2) memory is the binding constraint: about 30,000 rows without a connectivity graph.

Exercise 6 – The linkage decides what a cluster is

Next

DBSCAN - Density-Based Clustering — an algorithm that needs no kk at all, finds arbitrarily shaped clusters, and is the only method in this phase that can say “this point belongs to nothing”.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did