Hierarchical Clustering
Hierarchical Clustering builds a hierarchy of clusters step by step. The result is a dendrogram that shows the relationships between clusters, without needing to determine the number of clusters in advance.
Agglomerative vs Divisive
| Type | Approach | Complexity |
|---|---|---|
| Agglomerative | Bottom-up: starts from individual points | More common |
| Divisive | Top-down: starts from one large cluster | Less common |
How Agglomerative Works
- Treat each data point as its own cluster
- Find the two most similar clusters
- Merge them into a single cluster
- Repeat until only one cluster remains
Linkage Methods
| Method | Explanation | Characteristics |
|---|---|---|
| Single | Minimum distance between clusters | Chain-like clusters |
| Complete | Maximum distance between clusters | Compact clusters |
| Average | Average distance between clusters | A compromise |
| Ward | Minimizes total variance | Spherical clusters |
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import make_blobs
X, _ = make_blobs(n_samples=200, centers=4, random_state=42)
models = {}
for linkage in ['ward', 'complete', 'average', 'single']:
model = AgglomerativeClustering(n_clusters=4, linkage=linkage)
labels = model.fit_predict(X)
n_clusters = len(set(labels))
print(f"Linkage {linkage:10s} → Cluster: {n_clusters}, "
f"Labels: {sorted(set(labels))}")
Dendrogram
A dendrogram is a visualization of the clustering hierarchy. The height of a join indicates the distance at which clusters were merged.
from scipy.cluster.hierarchy import dendrogram, linkage
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
X, _ = make_blobs(n_samples=100, centers=4, random_state=42)
Z = linkage(X, method='ward')
plt.figure(figsize=(10, 5))
dendrogram(Z, truncate_mode='lastp', p=30, leaf_rotation=90)
plt.title('Dendrogram (Ward Linkage)')
plt.xlabel('Sample Index')
plt.ylabel('Distance')
plt.tight_layout()
plt.show()
Cutting the Dendrogram
To determine the number of clusters, cut the dendrogram at a certain distance:
from scipy.cluster.hierarchy import fcluster
from scipy.cluster.hierarchy import linkage
Z = linkage(X, method='ward')
# Cut at a specific distance
labels_2 = fcluster(Z, t=2, criterion='maxclust')
labels_3 = fcluster(Z, t=3, criterion='maxclust')
labels_4 = fcluster(Z, t=4, criterion='maxclust')
print(f"2 clusters: {len(set(labels_2))}")
print(f"3 clusters: {len(set(labels_3))}")
print(f"4 clusters: {len(set(labels_4))}")
Example: Customer Segmentation
import numpy as np
import pandas as pd
from sklearn.cluster import AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from scipy.cluster.hierarchy import dendrogram, linkage
import matplotlib.pyplot as plt
np.random.seed(42)
df = pd.DataFrame({
'income': np.random.randint(20000, 150000, 100),
'score': np.random.randint(1, 100, 100)
})
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df)
# Dendrogram
Z = linkage(X_scaled, method='ward')
plt.figure(figsize=(10, 5))
dendrogram(Z, truncate_mode='lastp', p=20)
plt.title('Customer Dendrogram')
plt.show()
# Clustering
model = AgglomerativeClustering(n_clusters=3, linkage='ward')
df['cluster'] = model.fit_predict(X_scaled)
print(df.groupby('cluster').mean().round(0))
Pros and Cons
| Pros | Cons |
|---|---|
| No need to determine k in advance | Slow on large data O(n³) |
| Informative dendrogram | Cannot be updated (must recompute) |
| Flexible linkage | Sensitive to noise |
| Good for hierarchical data | Memory intensive |
Comparison with K-Means
| Aspect | K-Means | Hierarchical |
|---|---|---|
| Number of clusters | Must be set in advance | Can be read from the dendrogram |
| Speed | Fast O(n) | Slow O(n³) |
| Scalability | Good for large data | Not for very large data |
| Result | Spherical clusters | Flexible shapes |
The next chapter covers DBSCAN, a density-based clustering algorithm that can find clusters of arbitrary shape.