In [ ]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
El agrupamiento (clustering) consiste en forma grupos de ejemplos similares de acuerdo a alguna medida de semejanza prefijada o disimilitud (distancia), como la distancia Euclídea.
En esta sección, exploraremos la tarea básica de agrupamiento en algunos datasets sintéticos y del mundo real.
Estas son algunas aplicaciones bastante comunes de los algoritmos de clustering:
Vamos a empezar creando un dataset sintético simple de dos dimensiones:
In [ ]:
from sklearn.datasets import make_blobs
X, y = make_blobs(random_state=42)
X.shape
In [ ]:
plt.scatter(X[:, 0], X[:, 1]);
En el scatter anterior, podemos ver tres grupos separados de datos y nos gustaría recuperarlos utilizando agrupamiento (algo así como "descubrir" las etiquetas de clase, que ya damos por sentadas en la tarea de clasificación).
Incluso si los grupos son obvios en los datos, es difícil encontrarlos cuando estos datos están en un espacio de alta dimensionalidad, que no podemos visualizar en un único histograma o scatterplot.
Ahora utilizaremos uno de los algoritmos de clustering más simples, K-means. Este es un algoritmo iterativo que busca aquellos tres centroides (centro de cada uno de los clusters) tales que la distancia desde cada punto a su centroide sea mínima.
La implementación estándar de K-means utiliza la distancia Euclídea, por lo que debemos estar seguros de que todas nuestras variables están en la misma escala, especialmente para datasets reales. Para ello podemos aplicar la estandarización que vimos en el cuaderno anterior.
In [ ]:
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=3, random_state=42)
Podemos obtener las etiquetas de los datos o llamando al método fit
y después accediendo al atributo labels_
del estimador KMeans, o llamando a fit_predict
(que aplica los dos pasos seguidos). En cualquier caso, el resultado contiene el identificador del grupo al que asignamos cada punto.
In [ ]:
labels = kmeans.fit_predict(X)
In [ ]:
labels
In [ ]:
print("¿Hemos acertado en todas las etiquetas?", np.all(y == labels))
print("¿En cuantas hemos fallado?", np.sum(y != labels))
Vamos a visualizar lo que hemos obtenido
In [ ]:
plt.scatter(X[:, 0], X[:, 1], c=labels);
Comparando con las etiquetas reales:
In [ ]:
plt.scatter(X[:, 0], X[:, 1], c=y);
Examinando el resultado de forma gráfica, está claro que podríamos estar satisfechos con los resultados obtenidos, pero, en general, nos gustaría tener una evaluación cuantitativa. ¿Qué tal comparar nuestras etiquetas aprendidas con las etiquetas reales?
In [ ]:
from sklearn.metrics import confusion_matrix, accuracy_score
print('Porcentaje de precisión:', accuracy_score(y, labels))
print(confusion_matrix(y, labels))
In [ ]:
np.mean(y == labels)
Incluso si conseguimos los mismos grupos, los identificadores asignados son arbitrarios y no podemos recuperar los reales. Por tanto, tendremos que usar otro mecanismo de asignación de rendimiento, como el adjusted_rand_score
, el cuál es invariante a cualquier permutación de las etiquetas:
In [ ]:
from sklearn.metrics import adjusted_rand_score
adjusted_rand_score(y, labels)
Una de las desventajas del K-means es que tenemos que especificar el número de clusters, cosa que a menudo no conocemos a priori. Por ejemplo, veamos que pasa si ponemos k=2 para el dataset sintético anterior:
In [ ]:
kmeans = KMeans(n_clusters=2, random_state=42)
labels = kmeans.fit_predict(X)
plt.scatter(X[:, 0], X[:, 1], c=labels);
In [ ]:
kmeans.cluster_centers_
In [ ]:
distortions = []
for i in range(1, 11):
km = KMeans(n_clusters=i,
random_state=0)
km.fit(X)
distortions.append(km.inertia_)
plt.plot(range(1, 11), distortions, marker='o')
plt.xlabel('Número de grupos')
plt.ylabel('Distorsión')
plt.show()
Después, tomamos el valor que lleva al pico del codo. Como podemos ver, ese valor será k=3 para este caso, lo que tiene sentido dado el conocimiento que tenemos del dataset.
El agrupamiento siempre viene con suposiciones: Un algoritmo de agrupamiento encuentra grupos haciendo suposiciones acerca de cómo debería agruparse los ejemplos. Cada algoritmo hace suposiciones distintas y la calidad e interpretabilidad de nuestros resultados dependerá de si estas suposiciones son correctas para nuestro objetivo. Para el K-means, el modelo subyacente supone que todos los grupos tienen la misma varianza, esférica (si usamos distancia Euclídea).
En general, no hay ninguna garantía de que la estructura encontrada por un algoritmo de clustering sea aquello en lo que estás interesado.
Fácilmente, podemos crear un dataset que tenga grupos no isotrópicos, caso en el que KMeans fallará:
In [ ]:
plt.figure(figsize=(12, 12))
n_samples = 1500
random_state = 170
X, y = make_blobs(n_samples=n_samples, random_state=random_state)
# Número de clusters incorrecto
y_pred = KMeans(n_clusters=2, random_state=random_state).fit_predict(X)
plt.subplot(221)
plt.scatter(X[:, 0], X[:, 1], c=y_pred)
plt.title("Número de clusters incorrecto")
# Datos distribuidos de forma anisotrópica
transformation = [[0.60834549, -0.63667341], [-0.40887718, 0.85253229]]
X_aniso = np.dot(X, transformation)
y_pred = KMeans(n_clusters=3, random_state=random_state).fit_predict(X_aniso)
plt.subplot(222)
plt.scatter(X_aniso[:, 0], X_aniso[:, 1], c=y_pred)
plt.title("Clusters con distribución anisotrópica")
# Distinta varianza
X_varied, y_varied = make_blobs(n_samples=n_samples,
cluster_std=[1.0, 2.5, 0.5],
random_state=random_state)
y_pred = KMeans(n_clusters=3, random_state=random_state).fit_predict(X_varied)
plt.subplot(223)
plt.scatter(X_varied[:, 0], X_varied[:, 1], c=y_pred)
plt.title("Distinta varianza")
# Clusters de distinto tamaño
X_filtered = np.vstack((X[y == 0][:500], X[y == 1][:100], X[y == 2][:10]))
y_pred = KMeans(n_clusters=3,
random_state=random_state).fit_predict(X_filtered)
plt.subplot(224)
plt.scatter(X_filtered[:, 0], X_filtered[:, 1], c=y_pred)
plt.title("Distinto tamaño")
A continuación, algunos de los algoritmos de agrupamiento más importantes:
sklearn.cluster.KMeans
: sklearn.cluster.MeanShift
: sklearn.cluster.DBSCAN
: sklearn.cluster.AffinityPropagation
: sklearn.cluster.SpectralClustering
: sklearn.cluster.Ward
: Entre estos, el algoritmo de Ward, el SpectralClustering, el DBSCAN y el método de propagación de afinidad pueden trabajar también con matrices de similitud precomputadas.
In [ ]:
from sklearn.datasets import load_digits
digits = load_digits()
# ...
In [ ]: