K-means (05-01) forced you to decide K before seeing a single result. Hierarchical clustering reverses the order: it first builds every possible grouping — from each customer standing alone to a single group containing everyone — and then you choose the level at which to cut. The result is a tree structure, the dendrogram, showing which customers resemble each other most, in what order the groups merge, and at what "distance" each merge happens. In this lesson you will learn the agglomerative mechanics step by step (with a worked example by hand), the linkage measures that determine cluster shape, how to build and read a dendrogram with scipy, how to apply AgglomerativeClustering to MercaFresh's customers and compare its segments with K-means', and when the computational price of the hierarchical approach is worth paying.
Contents
- Agglomerative vs. divisive
- Linkage measures: how distance between groups is defined
- A worked example by hand: the first merges
- The dendrogram: construction and reading
- Cutting the tree: from hierarchy to segments
- MercaFresh customers with
AgglomerativeClustering - Comparison with the K-means segments
- Advantages, computational cost and when to prefer it
Agglomerative vs. divisive
There are two ways to build a hierarchy of groups:
| Strategy | Direction | Idea | Use in practice |
|---|---|---|---|
| Agglomerative (bottom-up) | From $n$ clusters to 1 | Each point starts alone; at every step the two closest clusters are merged | The standard: it is what scipy and scikit-learn implement |
| Divisive (top-down) | From 1 cluster to $n$ | All points start together; at every step the most heterogeneous cluster is split | Rare: deciding the best split of a group is far more expensive than the best merge |
We will focus on the agglomerative approach. Its algorithm is remarkably simple:
- Start with $n$ clusters of one point each.
- Compute the distance between every pair of clusters.
- Merge the two closest clusters.
- Repeat 2-3 until a single cluster remains.
Every merge is recorded along with its distance, and that sequence of merges is the hierarchy. No random initialization, no iterating until convergence: the result is deterministic (given the same data and parameters, it always comes out the same — unlike K-means and its random_state).
Linkage measures: how distance between groups is defined
Step 2 hides the one important design decision: the distance between two points is the usual Euclidean one (04-05), but what is the distance between two groups of points? Each answer is a linkage measure, and it changes the character of the clustering:
| Linkage | Distance between clusters A and B | Tendency | Typical risk |
|---|---|---|---|
| Single | The minimum between a point of A and a point of B | Elongated, chain-like clusters; detects irregular shapes | Chaining: joins distinct groups through a bridge of intermediate points |
| Complete | The maximum between a point of A and a point of B | Compact clusters of similar diameter | Very sensitive to outliers (one far-off point inflates the maximum distance) |
| Average | The mean of all point-to-point distances between A and B | A compromise between single and complete | Less geometrically interpretable |
| Ward | The increase in within-cluster variance the merge would cause | Spherical, balanced clusters, very similar to K-means | Only makes sense with Euclidean distance |
Two practical notes:
- Ward is the sensible default for customer segmentation: at every merge it minimizes the same kind of criterion (within-cluster variance) that K-means minimizes globally with the inertia, so it produces comparable, stable groups.
- Single linkage is the odd one out: where Ward and complete see spheres, single follows chains of neighbors and can recover snaking shapes. That idea of "connecting through local proximity" will reappear, taken to its logical conclusion and done properly, in DBSCAN (05-04).
Since every linkage rests on distances, the rule from 03-05 still stands: scale the features first, or the units will decide for you.
A worked example by hand: the first merges
Let's take 5 MercaFresh customers with a single feature, recency_days, and single linkage (the easiest to compute by hand):
Step 1. Pairwise distances: AB=2, AC=5, BC=3, DE=5, CD=32, and the rest larger. The minimum is AB=2 → we merge {A,B} at distance 2.
Step 2. Distances involving the new cluster (single = minimum): d({A,B}, C) = min(5, 3) = 3; d({A,B}, D) = 35; DE = 5. The minimum is 3 → we merge {A,B,C}.
Step 3. d({A,B,C}, D) = 32; d({A,B,C}, E) = 37; DE = 5. The minimum is 5 → we merge {D,E}.
Step 4. Only {A,B,C} and {D,E} remain: they merge at distance min(32, 37) = 32.
The full sequence — (A,B) at 2, (+C) at 3, (D,E) at 5, (everything) at 32 — tells the whole story: there are two natural groups, one of recent customers and one of cold ones, and the enormous distance of the last merge (32 versus 5) is the evidence. That story is exactly what the dendrogram draws.
The dendrogram: construction and reading
A dendrogram is the tree of merges: the leaves are the points, each bridge-shaped join represents a merge, and the height of the bridge is the distance at which it happened. In scipy:
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import linkage, dendrogram
X = np.array([[3], [5], [8], [40], [45]]) # the worked example
Z = linkage(X, method="single") # merge matrix
dendrogram(Z, labels=["A", "B", "C", "D", "E"])
plt.ylabel("Merge distance")
plt.show()What each piece does:
linkage(X, method=...)runs the complete agglomerative algorithm and returnsZ, a matrix with one row per merge: which two clusters joined, at what distance, and how many points the result contains. For our example, its distances are exactly the ones we computed by hand: 2, 3, 5, 32.dendrogram(Z)draws the tree.methodaccepts"single","complete","average"and"ward".
How to read a dendrogram (the skill that matters):
- Low bridges = early merges = very similar points. A and B are practically the same customer.
- High bridges = forced merges between groups that barely resemble each other. The jump from 5 to 32 screams "there are two distinct populations here".
- The number of clusters at a given height = the number of vertical lines crossed by a horizontal drawn at that height. At height 10, our horizontal crosses 2 lines: two clusters.
flowchart TD
R["Final merge (dist. 32)"] --- G1["{A, B, C} (dist. 3)"]
R --- G2["{D, E} (dist. 5)"]
G1 --- AB["{A, B} (dist. 2)"]
G1 --- C["C"]
AB --- A["A"]
AB --- B["B"]
G2 --- D["D"]
G2 --- E["E"]
Cutting the tree: from hierarchy to segments
The full hierarchy is informative, but to act you need a concrete partition: you obtain one by cutting the dendrogram at a height. Common criteria:
- Cut where the distance jump is largest: just below the disproportionately tall bridge. It is the hierarchical equivalent of the elbow from 05-01.
- Cut to get a desired K: if the business wants 4 segments, lower the horizontal until it crosses 4 branches.
- The same tree admits several useful cuts: high up, "active vs. dormant" (2 groups, for an executive report); further down, 4-5 operational segments (for campaigns). That multi-scale view is something K-means does not offer: every K requires retraining from scratch.
In scipy, fcluster(Z, t=10, criterion="distance") returns the labels for the cut at height 10.
MercaFresh customers with AgglomerativeClustering
In scikit-learn the estimator is AgglomerativeClustering. We reuse the X_esc matrix from 05-01 (RFM + inactivity_ratio, scaled with StandardScaler):
from scipy.cluster.hierarchy import linkage, dendrogram
from sklearn.cluster import AgglomerativeClustering
import matplotlib.pyplot as plt
# 1. Exploratory dendrogram with scipy (on SCALED data)
Z = linkage(X_esc, method="ward")
plt.figure(figsize=(10, 4))
dendrogram(Z, truncate_mode="lastp", p=20) # shows only the last 20 merges
plt.ylabel("Distance (Ward)")
plt.show()
# 2. Cut into 4 clusters with scikit-learn
agg = AgglomerativeClustering(n_clusters=4, linkage="ward")
rfm["segment_hier"] = agg.fit_predict(X_esc)
print(rfm["segment_hier"].value_counts())Explanation for beginners:
- With hundreds of customers, a full dendrogram is an unreadable tangle of leaves;
truncate_mode="lastp", p=20draws only the 20 final merges, which are the ones that inform the cutting decision. We look for the stretch where the bridges shoot up: if the big jump happens when going from 4 branches to 3, then 4 clusters is a natural cut. AgglomerativeClusteringasks for eithern_clusters(it cuts the tree for you) ordistance_threshold(it cuts at a height, leaving K free).linkage="ward"is the default and our recommendation for this case.- There is no
random_state: hierarchical clustering is deterministic. - An honest detail: although the dendrogram lets you avoid fixing K up front, at the end of the day you cut somewhere — the difference is that you decide seeing the whole structure, not blindly trying Ks.
Comparison with the K-means segments
Do the 4 hierarchical segments match the 4 K-means segments from 05-01? We can cross them with a contingency table (pd.crosstab, which you already used in 03-04):
import pandas as pd
print(pd.crosstab(rfm["segment"], rfm["segment_hier"],
rownames=["K-means"], colnames=["Hierarchical"]))A typical result:
| K-means \ Hierarchical | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| 0 (VIP) | 171 | 9 | 0 | 0 |
| 1 (Regulars) | 6 | 385 | 0 | 19 |
| 2 (Dormant) | 0 | 0 | 148 | 2 |
| 3 (Occasional) | 0 | 31 | 4 | 225 |
The reading: each row concentrates its mass in one column — both algorithms have essentially discovered the same four groups (remember that cluster numbers are arbitrary; what matters is the correspondence). No coincidence: Ward and K-means optimize very similar variance criteria. The disagreements (the ~70 customers off the diagonal) are boundary points between segments — precisely the ones that would have silhouettes near 0 in 05-01. When two different algorithms agree like this, confidence that the segments are real structure (and not an artifact of the method) rises sharply; if they disagreed completely, weak structure would be the prime suspect.
Advantages, computational cost and when to prefer it
| Aspect | Hierarchical clustering | K-means |
|---|---|---|
| K up front | No: decided while looking at the dendrogram | Yes, before running |
| Result | A full multi-scale hierarchy | One partition for that K |
| Determinism | Total | Depends on initialization (n_init mitigates) |
| Cluster shapes | Depends on the linkage (single allows elongated shapes) | Spherical |
| Cost | $O(n^2)$ memory, $O(n^2)$–$O(n^3)$ time | $O(n \cdot K \cdot i)$: nearly linear |
| Practical scale | Thousands of points | Millions of points |
The cost deserves a pause: step 2 of the algorithm needs the distance matrix between all pairs of points — with $n$ customers that is on the order of $n^2/2$ distances. With MercaFresh's ~1,000 customers, half a million distances: instantaneous. With the 10 million customers of a large chain, $5 \times 10^{13}$ pairs: simply unworkable, while K-means would keep going.
When to prefer hierarchical:
- A small or medium dataset (up to tens of thousands of points).
- You have no idea how many groups exist and want to see the structure before deciding.
- The hierarchy itself has business value: product taxonomies (drinks > soft drinks > colas), groups within groups, reports at different levels of detail.
- You want a reproducible result with no seeds or initializations.
When K-means: large datasets, a reasonably clear K, or when you need to re-segment often and fast.
Common Mistakes and Tips
- Running
linkageon unscaled data. The same cardinal sin as in 05-01: the resulting dendrogram sorts by the highest-magnitude feature. Always scale first. - Drawing the full dendrogram with thousands of points. Unreadable and slow. Use
truncate_mode="lastp"withpbetween 15 and 30: the final merges are the ones that inform the cut. - Using Ward with non-Euclidean distances. Ward is defined over variances, which presuppose Euclidean distance. If you need another distance (Manhattan, cosine), switch to average or complete linkage.
- Expecting single linkage to give compact groups. Its specialty is chains; with noisy data it usually produces one mega-cluster and several stray points. For customer segmentation, Ward or complete.
- Tip: validate the cut with the silhouette from 05-01 (
silhouette_score(X_esc, labels)works with any clustering, not just K-means) and compare two or three candidate cuts.
Exercises
Exercise 1. Repeat the lesson's worked example by hand (A=3, B=5, C=8, D=40, E=45) but with complete linkage. Write out the sequence of merges with their distances. Does the merge order change compared with single? Does the final two-group structure change?
Exercise 2. Generate the Ward dendrogram of the MercaFresh customers (or of a synthetic dataset with make_blobs(n_samples=200, centers=4, random_state=7), scaled). Visually locate the largest distance jump and decide on a number of clusters. Then cut with AgglomerativeClustering at that K and compute the silhouette. Does your visual cut match the best silhouette between K=2 and K=6?
Exercise 3. On the same dataset, compare linkage="ward" and linkage="single" with n_clusters=4: print the value_counts() of each one's labels. What size pattern does each linkage produce, and why?
Solutions
Exercise 1
With complete (maximum instead of minimum):
- Merge 1: the smallest pairwise distance is still AB=2 → {A,B} at 2.
- Merge 2: d({A,B}, C) = max(5, 3) = 5; DE = 5. A tie at 5; scipy merges whichever it finds first — say {A,B,C} at 5 (with complete, the tie order makes no difference to the final result).
- Merge 3: {D,E} at 5.
- Merge 4: d({A,B,C}, {D,E}) = the maximum of all cross distances = d(A,E) = 42 → final merge at 42.
The order is essentially the same and so is the final structure ({A,B,C} vs. {D,E}): with groups this well separated, all linkages agree. The differences between linkages surface with ambiguous data, bridges of intermediate points or outliers — not with clean islands. Notice how the final merge climbs from 32 (single, the distance between the two groups' closest points) to 42 (complete, their farthest ones).
Exercise 2
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import AgglomerativeClustering
from sklearn.metrics import silhouette_score
from scipy.cluster.hierarchy import linkage, dendrogram
X, _ = make_blobs(n_samples=200, centers=4, random_state=7)
X_esc = StandardScaler().fit_transform(X)
dendrogram(linkage(X_esc, method="ward"), truncate_mode="lastp", p=20)
plt.show()
for k in range(2, 7):
lab = AgglomerativeClustering(n_clusters=k, linkage="ward").fit_predict(X_esc)
print(f"K={k} | silhouette = {silhouette_score(X_esc, lab):.3f}")In the dendrogram, the biggest height jump happens when going from 4 branches to 3 (the four blobs are real), and the maximum silhouette also lands at K=4. When the visual and numeric criteria agree, the decision rests on solid ground; when they disagree, it usually signals clusters of unequal density or size — worth a look at the scatter plot.
Exercise 3
Ward produces 4 groups of comparable size (it distributes variance evenly). Single usually produces a very different pattern: one or two huge clusters and others with a handful of points (even 1), because chaining keeps annexing everything reachable through near neighbors and only leaves out the genuinely isolated points. That behavior, which looks like a flaw here, is almost a form of outlier detection — an intuition that DBSCAN (05-04) will turn into a virtue with its explicit notion of noise.
Conclusion
You now command the second family of clustering: the agglomerative approach that merges the two closest groups at each step, the four linkage measures and their effect on cluster shape (Ward as K-means' close relative, single as a chain tracker), the dendrogram as a multi-scale X-ray of the structure, and the cut that turns it into segments. On MercaFresh you also verified something valuable: hierarchical and K-means agree on the same four segments, a sign that the structure is real. And you know the price: the $O(n^2)$ that makes it unworkable at large scale.
So far we have grouped the customers using their 4 RFM features, and we have been able to picture them two at a time. But the final dataset from 03-06 has many more columns, and back in 04-05 a threat was noted for later: the curse of dimensionality, which degrades distances — the basic ingredient of everything we have done in this module. The next lesson attacks that problem head-on: PCA, the technique that compresses many dimensions into a few while keeping as much information as possible, and which will finally let us draw our segments.
Machine Learning Course
Module 1: Introduction to Machine Learning
- What is Machine Learning?
- History and evolution of Machine Learning
- Types of Machine Learning
- Applications of Machine Learning
- The Machine Learning project workflow
Module 2: Foundations of Statistics and Probability
- Basic statistics concepts
- Probability distributions
- Correlation and covariance
- Statistical inference
- Bayes' theorem
Module 3: Data Preprocessing
- Data cleaning
- Handling missing data
- Data transformation
- Encoding categorical variables
- Normalization and standardization
- Feature engineering
Module 4: Supervised Machine Learning Algorithms
- Linear regression
- Logistic regression
- Decision trees
- Support Vector Machines (SVM)
- K-Nearest Neighbors (K-NN)
- Naive Bayes
- Neural networks
Module 5: Unsupervised Machine Learning Algorithms
- Clustering: K-means
- Hierarchical clustering
- Principal Component Analysis (PCA)
- DBSCAN clustering
- Data visualization with t-SNE and UMAP
Module 6: Model Evaluation and Validation
- Data splitting: training, validation and test
- Evaluation metrics
- Cross-validation
- ROC curve and AUC
- Overfitting and underfitting
Module 7: Advanced Techniques and Optimization
- Regularization: Ridge, Lasso and Elastic Net
- Ensemble Learning
- Gradient Boosting
- Deep neural networks (Deep Learning)
- Hyperparameter optimization
Module 8: Model Implementation and Deployment
- Popular frameworks and libraries
- Deploying models to production
- Model maintenance and monitoring
- Ethical and privacy considerations
Module 9: Hands-On Projects
- Project 1: Housing price prediction
- Project 2: Image classification
- Project 3: Sentiment analysis on social media
- Project 4: Fraud detection
- Project 5: Customer segmentation
