67  Introducción al clustering y métricas de evaluación

Autor/a

Diego Villalba

Fecha de publicación

16 de octubre de 2026

El aprendizaje supervisado parte de un conjunto de observaciones etiquetadas para aprender una función que generalice a datos nuevos. El aprendizaje no supervisado, en cambio, trabaja sin etiquetas: el objetivo es descubrir estructura interna en los datos, ya sea agrupando observaciones similares, identificando componentes latentes o reduciendo la dimensión de representación (Hastie et al. 2009; Bishop 2006).

El clustering —o agrupamiento— es la tarea no supervisada más estudiada. Dado un conjunto de observaciones \(\mathbf{x}_1,\dots,\mathbf{x}_n\in\mathbb{R}^p\), el objetivo es particionarlas en grupos \(C_1,\dots,C_k\) de modo que las observaciones dentro de cada grupo sean más similares entre sí que con las de otros grupos. Esta noción de similitud no está predefinida: el analista debe elegir una medida de distancia y un criterio de agrupamiento adecuados al problema (Aggarwal y Reddy 2013).

Este capítulo desarrolla los fundamentos del clustering: medidas de distancia y similitud, propiedades formales que deben cumplir, y métricas de evaluación que permiten comparar particiones cuando no se dispone de etiquetas verdaderas. Los capítulos siguientes desarrollan los algoritmos más importantes: K-Means, clustering jerárquico, DBSCAN y reducción de dimensionalidad no lineal.

1 El problema de clustering

Sea \(\mathcal{D}=\{\mathbf{x}_1,\dots,\mathbf{x}_n\}\) un conjunto de \(n\) observaciones en \(\mathbb{R}^p\). Un clustering de \(\mathcal{D}\) en \(k\) grupos es una función de asignación

\[ \ell:\{1,\dots,n\}\to\{1,\dots,k\}, \]

donde \(\ell(i)\) indica el grupo al que pertenece la observación \(\mathbf{x}_i\). El grupo \(C_j\) es el subconjunto de observaciones asignadas a la etiqueta \(j\):

\[ C_j=\{\mathbf{x}_i\mid \ell(i)=j\}. \]

La función de asignación óptima depende del criterio elegido. Distintos algoritmos minimizan distintos objetivos, y no existe un único criterio universal. El número de particiones posibles en \(k\) grupos de \(n\) objetos crece exponencialmente con \(n\), por lo que los algoritmos eficientes explotan heurísticas o restricciones geométricas.

2 Medidas de distancia y similitud

La noción de similitud entre observaciones se formaliza mediante funciones de distancia. Una función \(d:\mathbb{R}^p\times\mathbb{R}^p\to\mathbb{R}_{\geq 0}\) es una distancia si satisface cuatro propiedades.

Definición. Una función \(d\) es una métrica si para todo \(\mathbf{x},\mathbf{y},\mathbf{z}\in\mathbb{R}^p\):

  1. \(d(\mathbf{x},\mathbf{y})\geq 0\) y \(d(\mathbf{x},\mathbf{y})=0\Leftrightarrow\mathbf{x}=\mathbf{y}\) (positividad definida),
  2. \(d(\mathbf{x},\mathbf{y})=d(\mathbf{y},\mathbf{x})\) (simetría),
  3. \(d(\mathbf{x},\mathbf{z})\leq d(\mathbf{x},\mathbf{y})+d(\mathbf{y},\mathbf{z})\) (desigualdad triangular).

2.1 Distancias de Minkowski

La familia de distancias de Minkowski unifica varias métricas clásicas mediante un parámetro \(p\geq 1\).

Definición. La distancia de Minkowski de orden \(p\) entre \(\mathbf{x}=(x_1,\dots,x_d)\) y \(\mathbf{y}=(y_1,\dots,y_d)\) es \[ d_p(\mathbf{x},\mathbf{y}) = \left(\sum_{j=1}^{d}|x_j-y_j|^p\right)^{1/p}. \tag{1}\]

Los casos más comunes son:

  • \(p=1\): distancia Manhattan o \(L^1\), \(d_1(\mathbf{x},\mathbf{y})=\sum_j|x_j-y_j|\).
  • \(p=2\): distancia Euclídea o \(L^2\), \(d_2(\mathbf{x},\mathbf{y})=\sqrt{\sum_j(x_j-y_j)^2}\).
  • \(p\to\infty\): distancia de Chebyshev, \(d_\infty(\mathbf{x},\mathbf{y})=\max_j|x_j-y_j|\).

La elección de \(p\) determina qué tipo de diferencias entre coordenadas se penalizan más. La distancia Manhattan es menos sensible a diferencias grandes en una sola coordenada; la Euclídea las amplifica mediante el cuadrado; la de Chebyshev las ignora excepto en la coordenada de mayor diferencia.

2.2 Distancias para datos no vectoriales

Cuando los datos no son vectores en \(\mathbb{R}^p\), se usan distancias específicas al tipo de dato.

Distancia del coseno. Para vectores \(\mathbf{x},\mathbf{y}\neq\mathbf{0}\), mide el ángulo entre ellos y es frecuente en texto: \[ d_{\cos}(\mathbf{x},\mathbf{y}) = 1 - \frac{\mathbf{x}^T\mathbf{y}}{\|\mathbf{x}\|\|\mathbf{y}\|}. \]

Distancia de Jaccard. Para conjuntos \(A\) y \(B\), mide la proporción de elementos que no comparten: \[ d_J(A,B) = 1 - \frac{|A\cap B|}{|A\cup B|}. \]

Distancia de edición (Levenshtein). Para cadenas de texto o secuencias, cuenta el número mínimo de operaciones de inserción, eliminación y sustitución para transformar una cadena en la otra.

2.3 Importancia de la escala

Las distancias basadas en normas son sensibles a la escala de las variables. Si una variable mide gramos y otra mide kilómetros, la segunda dominará la distancia Euclídea. Por ello, antes de aplicar algoritmos de clustering es habitual estandarizar los datos dividiendo cada variable por su desviación estándar, o aplicar otra normalización adecuada al dominio.

Mostrar código
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.metrics.pairwise import euclidean_distances, manhattan_distances, cosine_distances

rng = np.random.default_rng(42)
X = rng.normal(loc=[0, 0], scale=[1, 100], size=(50, 2))  # escala muy distinta

X_std = StandardScaler().fit_transform(X)

print("Sin escalar — distancia media:", euclidean_distances(X).mean().round(2))
print("Escalado    — distancia media:", euclidean_distances(X_std).mean().round(2))
Sin escalar — distancia media: 81.02
Escalado    — distancia media: 1.76
Explorador de métricas de distancia
Mueve los puntos A y B con los sliders; ajusta p para comparar distancias de Minkowski



3 Evaluación de clustering sin etiquetas verdaderas

En la mayoría de las aplicaciones de clustering no se dispone de una partición verdadera contra la que comparar. Las métricas internas evalúan la calidad de la partición usando únicamente las distancias entre los puntos.

3.1 Índice de silueta

El índice de silueta mide, para cada observación, qué tan bien encaja en su propio grupo comparado con el grupo más cercano (Rousseeuw 1987).

Definición. Para la observación \(\mathbf{x}_i\) asignada al grupo \(C_{\ell(i)}\), se definen: - \(a(i)\): distancia media a todos los demás puntos del mismo grupo, - \(b(i)\): distancia media al grupo más cercano distinto del propio.

La silueta de \(i\) es: \[ s(i) = \frac{b(i)-a(i)}{\max\{a(i),b(i)\}}. \tag{2}\]

El valor de \(s(i)\) está en \([-1,1]\). Un valor cercano a 1 indica que \(\mathbf{x}_i\) está bien asignada: está lejos de los demás grupos. Un valor cercano a 0 indica que está en la frontera. Un valor negativo indica que \(\mathbf{x}_i\) probablemente está mal asignada.

El índice de silueta global es el promedio de \(s(i)\) sobre todas las observaciones: \[ S = \frac{1}{n}\sum_{i=1}^n s(i). \]

3.2 Índice Davies-Bouldin

El índice Davies-Bouldin mide la compacidad de cada grupo relativa a la separación entre grupos (Davies y Bouldin 1979).

Definición. Sea \(\bar{d}_k\) el diámetro promedio del grupo \(k\) (distancia media de sus puntos al centroide) y \(d(k,l)\) la distancia entre los centroides de los grupos \(k\) y \(l\). El índice Davies-Bouldin es: \[ \text{DB} = \frac{1}{K}\sum_{k=1}^{K}\max_{l\neq k}\frac{\bar{d}_k+\bar{d}_l}{d(k,l)}. \tag{3}\]

Un valor menor de DB indica mejor clustering: grupos compactos y bien separados. El índice penaliza tanto la dispersión interna como la falta de separación entre grupos.

3.3 Índice Calinski-Harabasz

El índice Calinski-Harabasz, también llamado criterio de varianza entre/intra grupos, compara la dispersión entre grupos con la dispersión dentro de los grupos (Caliński y Harabasz 1974).

Definición. Sea \(\text{SS}_B\) la suma de cuadrados entre grupos y \(\text{SS}_W\) la suma de cuadrados dentro de grupos. El índice Calinski-Harabasz es: \[ \text{CH} = \frac{\text{SS}_B/(K-1)}{\text{SS}_W/(n-K)}. \tag{4}\]

Un valor mayor de CH indica mejor clustering. Esta métrica favorece grupos compactos y bien separados, similar a Davies-Bouldin pero en sentido opuesto.

Mostrar código
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, davies_bouldin_score, calinski_harabasz_score

X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.8, random_state=42)

results = []
for k in range(2, 8):
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    labels = km.fit_predict(X)
    results.append({
        "k": k,
        "silhouette": silhouette_score(X, labels).round(4),
        "davies_bouldin": davies_bouldin_score(X, labels).round(4),
        "calinski_harabasz": calinski_harabasz_score(X, labels).round(2),
    })

for r in results:
    print(r)
{'k': 2, 'silhouette': np.float64(0.603), 'davies_bouldin': np.float64(0.5039), 'calinski_harabasz': np.float64(341.93)}
{'k': 3, 'silhouette': np.float64(0.7783), 'davies_bouldin': np.float64(0.3336), 'calinski_harabasz': np.float64(1381.89)}
{'k': 4, 'silhouette': np.float64(0.8335), 'davies_bouldin': np.float64(0.2322), 'calinski_harabasz': np.float64(5285.61)}
{'k': 5, 'silhouette': np.float64(0.6976), 'davies_bouldin': np.float64(0.6251), 'calinski_harabasz': np.float64(4361.43)}
{'k': 6, 'silhouette': np.float64(0.5882), 'davies_bouldin': np.float64(0.84), 'calinski_harabasz': np.float64(3889.12)}
{'k': 7, 'silhouette': np.float64(0.4487), 'davies_bouldin': np.float64(0.9949), 'calinski_harabasz': np.float64(3649.1)}
Índice de silueta — visualizador
Ajusta el número de grupos k y observa cómo cambia la silueta de cada punto

4 Evaluación con etiquetas verdaderas

Cuando se dispone de etiquetas externas —por ejemplo en un experimento controlado— es posible medir cuánto se parece la partición encontrada a la partición de referencia. Estas métricas son externas.

4.1 Índice de Rand ajustado

El índice de Rand considera todos los pares de observaciones y cuenta cuántos pares están en el mismo grupo en ambas particiones, o en grupos distintos en ambas (Hubert y Arabie 1985).

Definición. El índice de Rand ajustado (ARI) corrige el índice de Rand por la coincidencia esperada bajo asignación aleatoria: \[ \text{ARI} = \frac{\text{RI} - \mathbb{E}[\text{RI}]}{\max(\text{RI}) - \mathbb{E}[\text{RI}]}. \tag{5}\]

El ARI está en \([-1,1]\). Un valor de 1 indica concordancia perfecta; un valor de 0 indica lo esperado por azar. Se prefiere sobre el índice de Rand sin ajustar porque no crece artificialmente con \(n\) o \(k\).

Mostrar código
from sklearn.metrics import adjusted_rand_score, normalized_mutual_info_score

X, y_true = make_blobs(n_samples=200, centers=3, cluster_std=1.0, random_state=0)

for k in [2, 3, 4]:
    km = KMeans(n_clusters=k, random_state=0, n_init=10)
    pred = km.fit_predict(X)
    ari = adjusted_rand_score(y_true, pred)
    nmi = normalized_mutual_info_score(y_true, pred)
    print(f"k={k}  ARI={ari:.4f}  NMI={nmi:.4f}")
k=2  ARI=0.5342  NMI=0.6298
k=3  ARI=0.8428  NMI=0.7905
k=4  ARI=0.7432  NMI=0.7358

5 Preprocesamiento antes de clustering

Antes de aplicar cualquier algoritmo de clustering conviene realizar algunas verificaciones y transformaciones.

Estandarización. Variables con rangos muy distintos dominarán la distancia. Usar StandardScaler o RobustScaler antes de aplicar algoritmos basados en distancias euclídeas.

Reducción de dimensión. En espacios de alta dimensión, todas las distancias tienden a igualarse (maldición de la dimensionalidad). Aplicar PCA u otras técnicas antes de clustering puede mejorar los resultados.

Detección de valores atípicos. Los algoritmos como K-Means son sensibles a outliers porque los centroides son promedios. Conviene identificarlos y decidir si excluirlos o usar algoritmos más robustos.

Codificación de variables categóricas. Las distancias estándar no aplican directamente a variables nominales. Se pueden usar codificación one-hot con distancia euclídea, o la distancia de Gower que maneja mezclas de tipos.

Mostrar código
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

rng = np.random.default_rng(0)
X_raw = rng.normal(loc=[0, 0, 100, 100], scale=[1, 1, 50, 50], size=(300, 4))

pipeline = Pipeline([
    ("scaler", StandardScaler()),
    ("pca", PCA(n_components=2)),
    ("km", KMeans(n_clusters=3, random_state=0, n_init=10)),
])

labels = pipeline.fit_predict(X_raw)
print("Distribución de grupos:", np.bincount(labels))
Distribución de grupos: [ 94 109  97]

6 Selección del número de grupos

Una de las decisiones más difíciles en clustering es el número de grupos \(k\). No existe una respuesta única, pero varias heurísticas son útiles:

  • Método del codo: graficar el WCSS o el índice de silueta en función de \(k\) y buscar un punto de inflexión.
  • Índice de silueta: maximizar \(S(k)\) sobre valores candidatos de \(k\).
  • Davies-Bouldin: minimizar DB\((k)\).
  • Calinski-Harabasz: maximizar CH\((k)\).
  • Conocimiento del dominio: en muchos problemas, el número razonable de grupos se puede restringir con información previa.

Ninguna métrica interna puede reemplazar la interpretación del analista. El mejor \(k\) estadístico puede no ser el más útil para el objetivo de negocio.