W3docs

Clustering Gerarchico

Impara il clustering gerarchico in Python con scikit-learn e SciPy: metodi di linkage, dendrogrammi e confronto con K-Means.

Il clustering gerarchico è un algoritmo di machine learning non supervisionato che raggruppa i punti dati in un albero di cluster annidati, senza richiedere di specificare il numero di cluster in anticipo. Questa pagina spiega come funziona l'algoritmo, la differenza tra approcci agglomerativi e divisivi, come scegliere un metodo di linkage e come implementare e visualizzare il clustering gerarchico in Python usando sia scikit-learn che SciPy.

Cos'è il Clustering Gerarchico?

Il clustering gerarchico costruisce una gerarchia di cluster unendo o dividendo iterativamente gruppi di punti dati in base a una misura di distanza (o similarità). Il risultato è rappresentato come un dendrogramma — un diagramma ad albero in cui l'asse y mostra la distanza a cui i cluster vengono uniti e le foglie rappresentano i singoli punti dati.

A differenza di K-Means, il clustering gerarchico non richiede di scegliere il numero di cluster prima di eseguire l'algoritmo. Si esegue l'algoritmo una volta e poi si "taglia" il dendrogramma a una soglia di distanza scelta per produrre qualsiasi numero di cluster.

Quando usare il clustering gerarchico

Usa il clustering gerarchico quando:

  • Non conosci in anticipo il numero di cluster e vuoi esplorare diverse opzioni da una singola esecuzione.
  • Il tuo dataset ha dimensioni piccole o medie (migliaia di righe, non milioni) — il costo di memoria O(n²) dell'algoritmo lo rende impraticabile per dataset molto grandi.
  • Hai bisogno di relazioni tra cluster interpretabili (il dendrogramma mostra chiaramente la cronologia delle fusioni).
  • I tuoi cluster potrebbero non essere sferici — i metodi gerarchici con linkage completo o medio gestiscono forme non globulari meglio di K-Means.

Clustering Agglomerativo vs Divisivo

Esistono due strategie principali:

StrategiaDirezioneCome funziona
Agglomerativo (dal basso verso l'alto)Inizia con ogni punto come proprio cluster, poi unisce ripetutamente i due cluster più vicini.Il più comune; usato da AgglomerativeClustering di scikit-learn e da linkage di SciPy.
Divisivo (dall'alto verso il basso)Inizia con tutti i punti in un unico cluster, poi divide ricorsivamente il cluster più grande.Raramente usato in pratica; computazionalmente costoso.

Questa pagina si concentra sul clustering agglomerativo, che è ciò che la maggior parte dei professionisti intende quando parla di "clustering gerarchico."

Come Funziona l'Algoritmo

Il clustering agglomerativo segue questi passaggi:

  1. Tratta ognuno degli n punti dati come proprio cluster (n cluster in totale).
  2. Calcola la matrice delle distanze — le distanze a coppie tra tutti i cluster.
  3. Unisce i due cluster con la distanza minore in un nuovo cluster.
  4. Aggiorna la matrice delle distanze per riflettere il nuovo cluster.
  5. Ripete i passi 3–4 fino a quando rimane un solo cluster.

L'output finale è un dendrogramma che codifica tutti gli n-1 passaggi di fusione.

Metodi di Linkage

Il metodo di linkage controlla come viene calcolata la distanza tra due cluster a ogni passaggio di fusione. La scelta influisce significativamente sulla forma e sulla qualità dei cluster.

MetodoDistanza tra i cluster A e BIdeale per
WardAumento della varianza totale intra-cluster dopo la fusioneCluster compatti di dimensioni approssimativamente uguali (scelta predefinita)
CompleteDistanza massima tra qualsiasi punto in A e qualsiasi punto in BCluster compatti; evita il chaining
AverageDistanza media tra tutte le coppie (uno da A, uno da B)Compromesso equilibrato tra single e complete
SingleDistanza minima tra qualsiasi punto in A e qualsiasi punto in BRilevamento di cluster allungati o di forma irregolare; soggetto al "chaining"

Il linkage Ward è il punto di partenza più diffuso. Se i tuoi cluster sono allungati o non convessi, prova il linkage average o single.

Normalizzazione delle Feature Prima del Clustering

Poiché il clustering gerarchico si basa su calcoli di distanza, le feature con intervalli numerici elevati dominano la matrice delle distanze e distorcono i risultati. Normalizza sempre le tue feature prima.

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

StandardScaler centra ogni feature con media 0 e varianza unitaria. Consulta il capitolo sulla normalizzazione per alternative come MinMaxScaler e RobustScaler.

Implementare il Clustering Gerarchico con scikit-learn

La classe AgglomerativeClustering in scikit-learn adatta il modello e assegna le etichette dei cluster in un unico passaggio. Non produce un dendrogramma — usa SciPy (mostrato nella sezione successiva) per quello.

Passaggio 1 — Generare e normalizzare i dati

from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler

# 150 points in 3 natural clusters
X, y_true = make_blobs(n_samples=150, centers=3, cluster_std=0.6, random_state=42)

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

make_blobs crea un dataset sintetico riproducibile, quindi questo esempio può essere eseguito senza alcun file CSV.

Passaggio 2 — Adattare il clustering agglomerativo

from sklearn.cluster import AgglomerativeClustering

hc = AgglomerativeClustering(n_clusters=3, linkage='ward')
labels = hc.fit_predict(X_scaled)

print(labels[:10])
# e.g. [2 2 0 1 0 1 2 2 0 1]

fit_predict adatta il modello e restituisce l'etichetta del cluster (0, 1 o 2) per ogni punto dati in una singola chiamata.

Passaggio 3 — Visualizzare i cluster

import matplotlib.pyplot as plt

colors = ['red', 'blue', 'green']
for cluster_id in range(3):
    mask = labels == cluster_id
    plt.scatter(X_scaled[mask, 0], X_scaled[mask, 1],
                s=60, label=f'Cluster {cluster_id + 1}')

plt.title('Agglomerative Clustering (Ward linkage, k=3)')
plt.xlabel('Feature 1 (scaled)')
plt.ylabel('Feature 2 (scaled)')
plt.legend()
plt.tight_layout()
plt.show()

Dendrogrammi con SciPy

Il dendrogramma è l'output più distintivo del clustering gerarchico. Ti permette di scegliere visivamente il numero di cluster tagliando l'albero a diverse altezze. Il modulo scipy.cluster.hierarchy di SciPy fornisce sia linkage (per costruire l'albero) che dendrogram (per tracciarlo).

Costruire e tracciare un dendrogramma

from scipy.cluster.hierarchy import dendrogram, linkage
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt

# Use a smaller dataset so the dendrogram is readable
X, _ = make_blobs(n_samples=30, centers=3, cluster_std=0.6, random_state=42)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Build the linkage matrix
Z = linkage(X_scaled, method='ward')

# Plot
plt.figure(figsize=(12, 5))
dendrogram(Z, leaf_rotation=90, leaf_font_size=8)
plt.title('Dendrogram (Ward linkage)')
plt.xlabel('Sample index')
plt.ylabel('Merge distance')
plt.tight_layout()
plt.show()

Leggere il dendrogramma

  • Le foglie (in basso) rappresentano i singoli punti dati.
  • Le linee orizzontali rappresentano le fusioni. L'altezza di una linea equivale alla distanza tra i due cluster che vengono uniti.
  • Tagliare l'albero a una determinata altezza fornisce il numero di cluster al di sotto di quel taglio.

Per scegliere k, cerca la linea verticale più alta che non viene attraversata da una linea orizzontale — quel gap suggerisce il numero più naturale di cluster.

Tagliare il dendrogramma per assegnare le etichette

Dopo aver costruito la matrice di linkage Z, usa fcluster per tagliare l'albero al numero desiderato di cluster:

from scipy.cluster.hierarchy import linkage, fcluster
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
import numpy as np

X, _ = make_blobs(n_samples=150, centers=3, cluster_std=0.6, random_state=42)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

Z = linkage(X_scaled, method='ward')

# Cut the tree to get exactly 3 clusters
labels = fcluster(Z, t=3, criterion='maxclust')

print('Unique cluster IDs:', np.unique(labels))   # [1 2 3]  (1-indexed)
print('Sizes:', np.bincount(labels[labels > 0]))   # [50 50 50]

Nota che le etichette di fcluster sono indicizzate da 1 (partono da 1), a differenza delle etichette indicizzate da 0 di scikit-learn.

Confronto dei Metodi di Linkage

Il metodo di linkage modifica significativamente le forme e le dimensioni dei cluster. Ecco come confrontare tutti e quattro i metodi sullo stesso dataset:

from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import make_blobs
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import numpy as np

X, _ = make_blobs(n_samples=150, centers=3, cluster_std=0.6, random_state=42)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

methods = ['ward', 'complete', 'average', 'single']
fig, axes = plt.subplots(1, 4, figsize=(16, 4))

for ax, method in zip(axes, methods):
    hc = AgglomerativeClustering(n_clusters=3, linkage=method)
    labels = hc.fit_predict(X_scaled)
    ax.scatter(X_scaled[:, 0], X_scaled[:, 1], c=labels, cmap='Set1', s=30)
    ax.set_title(f'{method.capitalize()} linkage')
    ax.set_xticks([])
    ax.set_yticks([])

plt.suptitle('Linkage method comparison (k=3)', y=1.02)
plt.tight_layout()
plt.show()

Clustering Gerarchico vs K-Means

CaratteristicaClustering gerarchicoK-Means
Numero di clusterSi sceglie dopo l'esecuzione (ispezionando il dendrogramma)Deve essere specificato prima dell'esecuzione
ScalabilitàSpazio O(n²) — fatica oltre ~10 000 righeO(n·k·i) — scala a milioni
DeterminismoCompletamente deterministicoDipende dall'inizializzazione casuale
Forme dei clusterFlessibile (specialmente con linkage single/average)Assume cluster sferici
InterpretabilitàIl dendrogramma mostra la cronologia completa delle fusioniI centroidi sono facilmente interpretabili

Usa il clustering gerarchico per l'analisi esplorativa su dataset più piccoli; passa a K-Means (o DBSCAN) quando hai milioni di righe o conosci già il numero di cluster.

Errori Comuni

Dimenticare di normalizzare le feature. Senza la normalizzazione delle feature, una feature misurata in migliaia (ad es. il reddito) domina una misurata in cifre singole (ad es. la valutazione per età), producendo cluster fuorvianti.

Usare il linkage Ward con distanze non euclidee. Il linkage Ward è valido solo con la distanza euclidea. Per altre metriche di distanza (ad es. coseno, Manhattan), usa il linkage complete o average e passa metric= esplicitamente.

Interpretare dendrogrammi su dataset molto grandi. Un dendrogramma con 10 000 foglie è illeggibile. Usa p= e truncate_mode='lastp' in dendrogram() di SciPy per mostrare solo le ultime p fusioni.

Trattare gli ID dei cluster come stabili. Gli ID dei cluster (0, 1, 2) sono etichette arbitrarie. Quando si confrontano due esecuzioni, abbina i cluster per contenuto, non per numero.

Conclusione

Il clustering gerarchico è un algoritmo flessibile e interpretabile che non richiede di scegliere k in anticipo. Si costruisce il dendrogramma completo una volta sola e poi lo si taglia a qualsiasi livello. Il linkage Ward con la pre-elaborazione StandardScaler è il punto di partenza più sicuro. Per dataset molto grandi, preferisci K-Means per le prestazioni.

Capitoli correlati:

  • Normalizzazione — perché e come standardizzare le feature prima del clustering
  • K-Means — l'alternativa di clustering piatto per dataset di grandi dimensioni
  • Tutorial SciPy — ulteriori informazioni sulla libreria di calcolo scientifico SciPy
  • Scatter Plot — visualizzazione di dati multidimensionali con Matplotlib
Was this page helpful?