Agrupamiento Jerárquico
Aprende agrupamiento jerárquico en Python con scikit-learn y SciPy: métodos de enlace, dendrogramas y cuándo elegirlo sobre K-Means.
El agrupamiento jerárquico es un algoritmo de aprendizaje automático no supervisado que agrupa puntos de datos en un árbol de clústeres anidados, sin necesidad de especificar el número de clústeres de antemano. Esta página explica cómo funciona el algoritmo, la diferencia entre los enfoques aglomerativo y divisivo, cómo elegir un método de enlace, y cómo implementar y visualizar el agrupamiento jerárquico en Python usando scikit-learn y SciPy.
¿Qué es el Agrupamiento Jerárquico?
El agrupamiento jerárquico construye una jerarquía de clústeres fusionando o dividiendo iterativamente grupos de puntos de datos según una medida de distancia (o similitud). El resultado se representa como un dendrograma — un diagrama en forma de árbol donde el eje y muestra la distancia a la que se fusionan los clústeres y las hojas representan puntos de datos individuales.
A diferencia de K-Means, el agrupamiento jerárquico no requiere elegir el número de clústeres antes de ejecutar el algoritmo. Se ejecuta el algoritmo una vez y luego se "corta" el dendrograma en un umbral de distancia elegido para producir cualquier número de clústeres.
Cuándo usar el agrupamiento jerárquico
Usa el agrupamiento jerárquico cuando:
- No conoces el número de clústeres de antemano y deseas explorar varias opciones desde una sola ejecución.
- Tu conjunto de datos es de tamaño pequeño a mediano (miles de filas, no millones) — el costo de memoria O(n²) del algoritmo lo hace poco práctico para conjuntos de datos muy grandes.
- Necesitas relaciones de clústeres interpretables (el dendrograma revela claramente el historial de fusiones).
- Tus clústeres pueden no ser esféricos — los métodos jerárquicos con enlace completo o promedio manejan formas no globulares mejor que K-Means.
Agrupamiento Aglomerativo vs. Divisivo
Existen dos estrategias principales:
| Estrategia | Dirección | Cómo funciona |
|---|---|---|
| Aglomerativo (ascendente) | Comienza con cada punto como su propio clúster y luego fusiona repetidamente los dos clústeres más cercanos. | El más común; usado por AgglomerativeClustering de scikit-learn y linkage de SciPy. |
| Divisivo (descendente) | Comienza con todos los puntos en un clúster y luego divide recursivamente el clúster más grande. | Raramente usado en la práctica; computacionalmente costoso. |
Esta página se centra en el agrupamiento aglomerativo, que es lo que la mayoría de los profesionales entienden cuando dicen "agrupamiento jerárquico".
Cómo Funciona el Algoritmo
El agrupamiento aglomerativo sigue estos pasos:
- Trata cada uno de los n puntos de datos como su propio clúster (n clústeres en total).
- Calcula la matriz de distancias — las distancias por pares entre todos los clústeres.
- Fusiona los dos clústeres con la distancia más pequeña en un nuevo clúster.
- Actualiza la matriz de distancias para reflejar el nuevo clúster.
- Repite los pasos 3–4 hasta que solo quede un clúster.
La salida final es un dendrograma que codifica todos los n-1 pasos de fusión.
Métodos de Enlace
El método de enlace controla cómo se calcula la distancia entre dos clústeres en cada paso de fusión. La elección afecta significativamente la forma y la calidad de los clústeres.
| Método | Distancia entre clústeres A y B | Ideal para |
|---|---|---|
| Ward | Incremento en la varianza total intra-clúster después de fusionar | Clústeres compactos de tamaño aproximadamente igual (opción predeterminada) |
| Complete | Distancia máxima entre cualquier punto de A y cualquier punto de B | Clústeres compactos; evita el encadenamiento |
| Average | Distancia media entre todos los pares (uno de A, uno de B) | Equilibrio entre enlace simple y completo |
| Single | Distancia mínima entre cualquier punto de A y cualquier punto de B | Detección de clústeres alargados o de forma irregular; propenso al "encadenamiento" |
El enlace Ward es el punto de partida más utilizado. Si tus clústeres son alargados o no convexos, prueba el enlace average o single.
Escalado de Características Antes del Agrupamiento
Dado que el agrupamiento jerárquico depende de cálculos de distancia, las características con rangos numéricos grandes dominan la matriz de distancias y sesgan los resultados. Siempre escala tus características primero.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)StandardScaler centra cada característica a media 0 y varianza unitaria. Consulta el capítulo de Escala para conocer alternativas como MinMaxScaler y RobustScaler.
Implementar Agrupamiento Jerárquico con scikit-learn
La clase AgglomerativeClustering en scikit-learn ajusta el modelo y asigna etiquetas de clúster en un solo paso. No produce un dendrograma — usa SciPy (mostrado en la siguiente sección) para eso.
Paso 1 — Generar y escalar datos
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 conjunto de datos sintético reproducible, por lo que este ejemplo se ejecuta sin ningún archivo CSV.
Paso 2 — Ajustar el agrupamiento aglomerativo
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 ajusta el modelo y devuelve la etiqueta de clúster (0, 1 o 2) para cada punto de datos en una sola llamada.
Paso 3 — Visualizar los clústeres
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()Dendrogramas con SciPy
El dendrograma es la salida más distintiva del agrupamiento jerárquico. Permite elegir visualmente el número de clústeres cortando el árbol a diferentes alturas. El módulo scipy.cluster.hierarchy de SciPy proporciona tanto linkage (para construir el árbol) como dendrogram (para graficarlo).
Construir y graficar un dendrograma
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()Cómo leer el dendrograma
- Las hojas (parte inferior) representan puntos de datos individuales.
- Las líneas horizontales representan fusiones. La altura de una línea es igual a la distancia entre los dos clústeres que se fusionan.
- Cortar el árbol a una altura determinada proporciona el número de clústeres por debajo de ese corte.
Para elegir k, busca la línea vertical más alta que no esté cruzada por una línea horizontal — esa brecha sugiere el número más natural de clústeres.
Cortar el dendrograma para asignar etiquetas
Después de construir la matriz de enlace Z, usa fcluster para cortar el árbol en el número deseado de clústeres:
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]Ten en cuenta que las etiquetas de fcluster están indexadas desde 1 (comienzan en 1), a diferencia de las etiquetas indexadas desde 0 de scikit-learn.
Comparar Métodos de Enlace
El método de enlace cambia significativamente las formas y tamaños de los clústeres. Así se pueden comparar los cuatro métodos en el mismo conjunto de datos:
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()Agrupamiento Jerárquico vs K-Means
| Característica | Agrupamiento jerárquico | K-Means |
|---|---|---|
| Número de clústeres | Se elige después de ejecutar (inspeccionar el dendrograma) | Debe especificarse antes de ejecutar |
| Escalabilidad | Espacio O(n²) — tiene dificultades por encima de ~10 000 filas | O(n·k·i) — escala a millones |
| Determinismo | Completamente determinista | Depende de la inicialización aleatoria |
| Formas de clústeres | Flexible (especialmente con enlace single/average) | Asume clústeres esféricos |
| Interpretabilidad | El dendrograma muestra el historial completo de fusiones | Los centroides son fácilmente interpretables |
Usa el agrupamiento jerárquico para el análisis exploratorio en conjuntos de datos más pequeños; cambia a K-Means (o DBSCAN) cuando tengas millones de filas o ya conozcas el número de clústeres.
Errores Comunes
Olvidar escalar las características. Sin escalado de características, una característica medida en miles (por ejemplo, ingresos) domina a una medida en dígitos simples (por ejemplo, calificación de edad), produciendo clústeres engañosos.
Usar enlace Ward con distancias no euclidianas. El enlace Ward solo es válido con distancia euclidiana. Para otras métricas de distancia (por ejemplo, coseno, Manhattan), usa enlace completo o promedio y pasa metric= explícitamente.
Interpretar dendrogramas en conjuntos de datos muy grandes. Un dendrograma con 10 000 hojas es ilegible. Usa p= y truncate_mode='lastp' en dendrogram() de SciPy para mostrar solo las últimas p fusiones.
Tratar los IDs de clúster como estables. Los IDs de clúster (0, 1, 2) son etiquetas arbitrarias. Al comparar dos ejecuciones, empareja los clústeres por contenido, no por número.
Conclusión
El agrupamiento jerárquico es un algoritmo flexible e interpretable que no requiere elegir k de antemano. Se construye el dendrograma completo una vez y luego se corta en cualquier nivel. El enlace Ward con preprocesamiento StandardScaler es el predeterminado más seguro. Para conjuntos de datos muy grandes, prefiere K-Means por rendimiento.
Capítulos relacionados:
- Scale — por qué y cómo estandarizar características antes del agrupamiento
- K-Means — la alternativa de agrupamiento plano para conjuntos de datos grandes
- SciPy Tutorial — más sobre la biblioteca de computación científica SciPy
- Scatter Plot — visualización de datos multidimensionales con Matplotlib