K-means (05-01) te obligó a decidir K antes de ver un solo resultado. El clustering jerárquico invierte el orden: primero construye todas las agrupaciones posibles —desde cada cliente aislado hasta un único grupo con todos— y después tú eliges a qué nivel cortar. El resultado es una estructura en árbol, el dendrograma, que muestra qué clientes se parecen más, en qué orden se fusionan los grupos y a qué "distancia" ocurre cada fusión. En esta lección aprenderás la mecánica aglomerativa paso a paso (con un ejemplo a mano), las medidas de enlace que determinan la forma de los clusters, cómo construir y leer un dendrograma con scipy, cómo aplicar AgglomerativeClustering a los clientes de MercaFresh y comparar sus segmentos con los de K-means, y cuándo el precio computacional del jerárquico merece la pena.

Contenido

  1. Aglomerativo vs. divisivo
  2. Medidas de enlace: cómo se mide la distancia entre grupos
  3. Ejemplo a mano: las primeras fusiones
  4. El dendrograma: construcción y lectura
  5. Cortar el árbol: de jerarquía a segmentos
  6. Clientes MercaFresh con AgglomerativeClustering
  7. Comparación con los segmentos de K-means
  8. Ventajas, coste computacional y cuándo preferirlo

Aglomerativo vs. divisivo

Hay dos maneras de construir una jerarquía de grupos:

Estrategia Dirección Idea Uso en la práctica
Aglomerativa (bottom-up) De $n$ clusters a 1 Cada punto empieza solo; en cada paso se fusionan los dos clusters más cercanos La estándar: es la que implementan scipy y scikit-learn
Divisiva (top-down) De 1 cluster a $n$ Todos los puntos empiezan juntos; en cada paso se parte el cluster más heterogéneo Rara: decidir la mejor partición de un grupo es mucho más costoso que la mejor fusión

Nos centraremos en la aglomerativa. Su algoritmo es de una simplicidad notable:

  1. Empieza con $n$ clusters de un punto cada uno.
  2. Calcula la distancia entre todos los pares de clusters.
  3. Fusiona los dos clusters más cercanos.
  4. Repite 2-3 hasta que quede un único cluster.

Cada fusión queda registrada con su distancia, y esa secuencia de fusiones es la jerarquía. Sin inicialización aleatoria, sin iterar hasta converger: el resultado es determinista (a igualdad de datos y parámetros, siempre sale lo mismo — a diferencia de K-means y su random_state).

Medidas de enlace: cómo se mide la distancia entre grupos

El paso 2 esconde la única decisión de diseño importante: la distancia entre dos puntos es la euclídea de siempre (04-05), pero ¿qué es la distancia entre dos grupos de puntos? Cada respuesta es una medida de enlace (linkage), y cambia el carácter del clustering:

Enlace Distancia entre clusters A y B Tendencia Riesgo típico
Single La mínima entre un punto de A y uno de B Clusters alargados, en cadena; detecta formas irregulares Chaining: une grupos distintos a través de un puente de puntos intermedios
Complete La máxima entre un punto de A y uno de B Clusters compactos y de diámetro similar Muy sensible a outliers (un punto lejano infla la distancia máxima)
Average La media de todas las distancias punto a punto entre A y B Compromiso entre single y complete Menos interpretable geométricamente
Ward El incremento de varianza interna que causaría la fusión Clusters esféricos y equilibrados, muy parecidos a K-means Solo tiene sentido con distancia euclídea

Dos apuntes prácticos:

  • Ward es el defecto sensato para segmentación de clientes: minimiza en cada fusión el mismo tipo de criterio (varianza intra-cluster) que K-means minimiza globalmente con la inercia, así que produce grupos comparables y estables.
  • Single linkage es el diferente de la familia: donde Ward y complete ven esferas, single sigue cadenas de vecinos y puede recuperar formas serpenteantes. Esa idea de "conectar por proximidad local" reaparecerá, llevada al extremo y bien resuelta, en DBSCAN (05-04).

Como todos los enlaces se apoyan en distancias, la regla de 03-05 sigue vigente: escala las features antes o las unidades decidirán por ti.

Ejemplo a mano: las primeras fusiones

Tomemos 5 clientes de MercaFresh con una sola feature, recencia_dias, y enlace single (el más cómodo de calcular a mano):

A=3, B=5, C=8, D=40, E=45

Paso 1. Distancias entre pares: AB=2, AC=5, BC=3, DE=5, CD=32, y el resto mayores. La mínima es AB=2 → fusionamos {A,B} a distancia 2.

Paso 2. Distancias con el nuevo cluster (single = mínimo): d({A,B}, C) = min(5, 3) = 3; d({A,B}, D) = 35; DE = 5. La mínima es 3 → fusionamos {A,B,C}.

Paso 3. d({A,B,C}, D) = 32; d({A,B,C}, E) = 37; DE = 5. La mínima es 5 → fusionamos {D,E}.

Paso 4. Solo quedan {A,B,C} y {D,E}: se fusionan a distancia min(32, 37) = 32.

La secuencia completa — (A,B) a 2, (+C) a 3, (D,E) a 5, (todo) a 32 — cuenta la historia entera: hay dos grupos naturales, uno de clientes recientes y otro de fríos, y la enorme distancia de la última fusión (32 frente a 5) es la evidencia. Esa historia es exactamente lo que el dendrograma dibuja.

El dendrograma: construcción y lectura

Un dendrograma es el árbol de fusiones: las hojas son los puntos, cada unión en forma de puente representa una fusión, y la altura del puente es la distancia a la que ocurrió. En 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]])          # el ejemplo a mano
Z = linkage(X, method="single")                     # matriz de fusiones
dendrogram(Z, labels=["A", "B", "C", "D", "E"])
plt.ylabel("Distancia de fusión")
plt.show()

Qué hace cada pieza:

  • linkage(X, method=...) ejecuta el algoritmo aglomerativo completo y devuelve Z, una matriz con una fila por fusión: qué dos clusters se unieron, a qué distancia y cuántos puntos suma el resultado. Para nuestro ejemplo, sus distancias son exactamente las que calculamos a mano: 2, 3, 5, 32.
  • dendrogram(Z) dibuja el árbol. method acepta "single", "complete", "average" y "ward".

Cómo leer un dendrograma (la habilidad importante):

  • Puentes bajos = fusiones tempranas = puntos muy similares. A y B son casi el mismo cliente.
  • Puentes altos = fusiones forzadas entre grupos que se parecen poco. El salto de 5 a 32 grita "aquí hay dos poblaciones distintas".
  • El número de clusters a una altura dada = número de líneas verticales que corta una horizontal trazada a esa altura. A altura 10, nuestra horizontal corta 2 líneas: dos clusters.
flowchart TD
    R["Fusión final (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"]

Cortar el árbol: de jerarquía a segmentos

La jerarquía completa es informativa, pero para actuar necesitas una partición concreta: se obtiene cortando el dendrograma a una altura. Criterios habituales:

  • Cortar donde el salto de distancias es mayor: justo debajo del puente desproporcionadamente alto. Es el equivalente jerárquico del codo de 05-01.
  • Cortar para obtener un K deseado: si negocio quiere 4 segmentos, se baja la horizontal hasta que corte 4 ramas.
  • Un mismo árbol admite varios cortes útiles: a gran altura, "activos vs. dormidos" (2 grupos, para un informe ejecutivo); más abajo, 4-5 segmentos operativos (para campañas). Esa multi-escala es algo que K-means no ofrece: cada K exige reentrenar desde cero.

En scipy, fcluster(Z, t=10, criterion="distance") devuelve las etiquetas del corte a altura 10.

Clientes MercaFresh con AgglomerativeClustering

En scikit-learn el estimador es AgglomerativeClustering. Reutilizamos la matriz X_esc de 05-01 (RFM + ratio_inactividad, escaladas con StandardScaler):

from scipy.cluster.hierarchy import linkage, dendrogram
from sklearn.cluster import AgglomerativeClustering
import matplotlib.pyplot as plt

# 1. Dendrograma exploratorio con scipy (sobre datos ESCALADOS)
Z = linkage(X_esc, method="ward")
plt.figure(figsize=(10, 4))
dendrogram(Z, truncate_mode="lastp", p=20)   # muestra solo las 20 últimas fusiones
plt.ylabel("Distancia (Ward)")
plt.show()

# 2. Corte en 4 clusters con scikit-learn
agg = AgglomerativeClustering(n_clusters=4, linkage="ward")
rfm["segmento_jer"] = agg.fit_predict(X_esc)
print(rfm["segmento_jer"].value_counts())

Explicación para principiantes:

  • Con cientos de clientes, un dendrograma completo es una maraña de hojas ilegible; truncate_mode="lastp", p=20 dibuja solo las 20 fusiones finales, que son las que informan la decisión de corte. Buscamos el tramo donde los puentes pegan el estirón: si el salto grande ocurre al pasar de 4 a 3 ramas, 4 clusters es un corte natural.
  • AgglomerativeClustering pide o n_clusters (corta el árbol por ti) o distance_threshold (corta a una altura, dejando K libre). linkage="ward" es el defecto y nuestro consejo para este caso.
  • No hay random_state: el jerárquico es determinista.
  • Un detalle honesto: aunque el dendrograma permite no fijar K a priori, al final del día cortas en algún sitio — la diferencia es que decides viendo toda la estructura, no a ciegas probando Ks.

Comparación con los segmentos de K-means

¿Coinciden los 4 segmentos jerárquicos con los 4 de K-means de 05-01? Podemos cruzarlos con una tabla de contingencia (pd.crosstab, que ya usaste en 03-04):

import pandas as pd
print(pd.crosstab(rfm["segmento"], rfm["segmento_jer"],
                  rownames=["K-means"], colnames=["Jerárquico"]))

Un resultado típico:

K-means \ Jerárquico 0 1 2 3
0 (VIP) 171 9 0 0
1 (Habituales) 6 385 0 19
2 (Dormidos) 0 0 148 2
3 (Ocasionales) 0 31 4 225

La lectura: cada fila concentra su masa en una columna — ambos algoritmos han descubierto esencialmente los mismos cuatro grupos (recuerda que los números de cluster son arbitrarios; lo que importa es la correspondencia). No es casualidad: Ward y K-means optimizan criterios de varianza muy parecidos. Los desacuerdos (los ~70 clientes fuera de la diagonal) son puntos fronterizos entre segmentos — precisamente los que tendrían silhouette cercano a 0 en 05-01. Cuando dos algoritmos distintos coinciden así, la confianza en que los segmentos son estructura real (y no un artefacto del método) sube mucho; si discreparan por completo, tocaría sospechar de una estructura débil.

Ventajas, coste computacional y cuándo preferirlo

Aspecto Clustering jerárquico K-means
K a priori No: se decide viendo el dendrograma Sí, antes de ejecutar
Resultado Jerarquía completa multi-escala Una partición para ese K
Determinismo Total Depende de la inicialización (n_init mitiga)
Formas de cluster Según enlace (single permite formas alargadas) Esféricas
Coste $O(n^2)$ memoria, $O(n^2)$–$O(n^3)$ tiempo $O(n \cdot K \cdot i)$: casi lineal
Escala práctica Miles de puntos Millones de puntos

El coste merece detenerse: el paso 2 del algoritmo necesita la matriz de distancias entre todos los pares de puntos — con $n$ clientes son del orden de $n^2/2$ distancias. Con los ~1.000 clientes de MercaFresh, medio millón de distancias: instantáneo. Con 10 millones de clientes de una gran cadena, $5 \times 10^{13}$ pares: sencillamente inviable, mientras K-means seguiría funcionando.

Cuándo preferir el jerárquico:

  • Dataset pequeño o mediano (hasta decenas de miles de puntos).
  • No tienes ni idea de cuántos grupos hay y quieres ver la estructura antes de decidir.
  • La jerarquía en sí tiene valor de negocio: taxonomías de productos (bebidas > refrescos > colas), grupos dentro de grupos, informes a distintos niveles de detalle.
  • Quieres un resultado reproducible sin semillas ni inicializaciones.

Cuándo K-means: datasets grandes, K razonablemente claro, o cuando necesitas re-segmentar a menudo y rápido.

Errores Comunes y Consejos

  • Ejecutar linkage sobre datos sin escalar. El mismo pecado capital de 05-01: el dendrograma resultante ordena por la feature de mayor magnitud. Escala siempre antes.
  • Dibujar el dendrograma completo con miles de puntos. Ilegible y lento. Usa truncate_mode="lastp" con p entre 15 y 30: las fusiones finales son las que informan el corte.
  • Usar Ward con distancias no euclídeas. Ward está definido sobre varianzas, que presuponen euclídea. Si necesitas otra distancia (Manhattan, coseno), cambia a average o complete linkage.
  • Esperar que single linkage dé grupos compactos. Su especialidad son las cadenas; con datos ruidosos suele producir un mega-cluster y varios puntos sueltos. Para segmentación de clientes, Ward o complete.
  • Consejo: valida el corte con el silhouette de 05-01 (silhouette_score(X_esc, etiquetas) funciona con cualquier clustering, no solo K-means) y compara dos o tres cortes candidatos.

Ejercicios

Ejercicio 1. Repite a mano el ejemplo de la lección (A=3, B=5, C=8, D=40, E=45) pero con enlace complete. Escribe la secuencia de fusiones con sus distancias. ¿Cambia el orden de las fusiones respecto a single? ¿Cambia la estructura final de dos grupos?

Ejercicio 2. Genera el dendrograma Ward de los clientes MercaFresh (o de un dataset sintético con make_blobs(n_samples=200, centers=4, random_state=7), escalado). Localiza visualmente el mayor salto de distancias y decide un número de clusters. Después corta con AgglomerativeClustering a ese K y calcula el silhouette. ¿Coincide tu corte visual con el mejor silhouette entre K=2 y K=6?

Ejercicio 3. Sobre el mismo dataset, compara linkage="ward" y linkage="single" con n_clusters=4: imprime el value_counts() de las etiquetas de cada uno. ¿Qué patrón de tamaños produce cada enlace y por qué?

Soluciones

Ejercicio 1

Con complete (máximo en lugar de mínimo):

  • Fusión 1: la mínima distancia entre pares sigue siendo AB=2 → {A,B} a 2.
  • Fusión 2: d({A,B}, C) = max(5, 3) = 5; DE = 5. Empate a 5; scipy fusiona el primero que encuentra — digamos {A,B,C} a 5 (con complete, da igual el orden del empate para el resultado final).
  • Fusión 3: {D,E} a 5.
  • Fusión 4: d({A,B,C}, {D,E}) = max de todas las distancias cruzadas = d(A,E) = 42 → fusión final a 42.

El orden es esencialmente el mismo y la estructura final también ({A,B,C} vs. {D,E}): con grupos tan separados, todos los enlaces coinciden. Las diferencias entre enlaces afloran con datos ambiguos, puentes de puntos intermedios u outliers — no con islas limpias. Nota cómo la fusión final sube de 32 (single, distancia entre los puntos más próximos de ambos grupos) a 42 (complete, los más lejanos).

Ejercicio 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}")

En el dendrograma, el salto más grande de altura ocurre al pasar de 4 ramas a 3 (los cuatro blobs son reales), y el silhouette máximo aparece también en K=4. Cuando el criterio visual y el numérico coinciden, la decisión está bien fundamentada; si discrepan, suele ser señal de clusters de densidad o tamaño desiguales — merece mirar el scatter.

Ejercicio 3

Ward produce 4 grupos de tamaños comparables (reparte varianza de forma equilibrada). Single suele producir un patrón muy distinto: uno o dos clusters enormes y otros con un puñado de puntos (incluso 1), porque el encadenamiento va anexionando todo lo conectable por vecinos próximos y solo deja fuera los puntos verdaderamente aislados. Ese comportamiento, que aquí parece un defecto, es casi una detección de outliers — una intuición que DBSCAN (05-04) convertirá en virtud con la noción explícita de ruido.

Conclusión

Ya dominas la segunda familia del clustering: el enfoque aglomerativo que fusiona en cada paso los dos grupos más cercanos, las cuatro medidas de enlace y su efecto en la forma de los clusters (Ward como pariente de K-means, single como rastreador de cadenas), el dendrograma como radiografía multi-escala de la estructura y el corte que lo convierte en segmentos. Sobre MercaFresh comprobaste además algo valioso: jerárquico y K-means coinciden en los mismos cuatro segmentos, señal de que la estructura es real. Y conoces el precio: $O(n^2)$ que lo hace inviable a gran escala.

Hasta ahora hemos agrupado a los clientes usando sus 4 features RFM, y las hemos podido imaginar de dos en dos. Pero el dataset final de 03-06 tiene muchas más columnas, y en 04-05 quedó anotada una amenaza: la maldición de la dimensionalidad, que degrada las distancias — el ingrediente básico de todo lo que hemos hecho en este módulo. La siguiente lección ataca ese problema de frente: PCA, la técnica que comprime muchas dimensiones en pocas conservando el máximo de información, y que además nos permitirá por fin dibujar nuestros segmentos.

Curso de Machine Learning

Módulo 1: Introducción al Machine Learning

Módulo 2: Fundamentos de Estadística y Probabilidad

Módulo 3: Preprocesamiento de Datos

Módulo 4: Algoritmos de Machine Learning Supervisado

Módulo 5: Algoritmos de Machine Learning No Supervisado

Módulo 6: Evaluación y Validación de Modelos

Módulo 7: Técnicas Avanzadas y Optimización

Módulo 8: Implementación y Despliegue de Modelos

Módulo 9: Proyectos Prácticos

Módulo 10: Recursos Adicionales

© Copyright 2026. Todos los derechos reservados