python - similitud - cómo trazar y anotar dendrogramas de conglomerados jerárquicos en scipy/matplotlib
ejercicios resueltos de k means (2)
Estoy usando dendrogram
desde scipy
para trazar clusters jerárquicos usando matplotlib
siguiente manera:
mat = array([[1, 0.5, 0.9],
[0.5, 1, -0.5],
[0.9, -0.5, 1]])
plt.subplot(1,2,1)
plt.title("mat")
dist_mat = mat
linkage_matrix = linkage(dist_mat,
"single")
print "linkage2:"
print linkage(1-dist_mat, "single")
dendrogram(linkage_matrix,
color_threshold=1,
labels=["a", "b", "c"],
show_leaf_counts=True)
plt.subplot(1,2,2)
plt.title("1 - mat")
dist_mat = 1 - mat
linkage_matrix = linkage(dist_mat,
"single")
dendrogram(linkage_matrix,
color_threshold=1,
labels=["a", "b", "c"],
show_leaf_counts=True)
Mis preguntas son: primero, ¿por qué mat
y 1-mat
dan racimos idénticos aquí? y segundo, ¿cómo puedo anotar la distancia a lo largo de cada rama del árbol usando dendrogram
para poder comparar las distancias entre pares de nodos?
finalmente parece que se show_leaf_counts
indicador show_leaf_counts
, ¿hay alguna manera de activarlo para que se muestre el número de objetos en cada clase? Gracias.
Creo que hay un par de malentendidos en cuanto al uso de las funciones que estás tratando de usar. Aquí hay un fragmento de código completamente funcional para ilustrar mis puntos:
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage
from numpy import array
import numpy as np
mat = array([184, 222, 177, 216, 231,
45, 123, 128, 200,
129, 121, 203,
46, 83,
83])
dist_mat = mat
linkage_matrix = linkage(dist_mat, ''single'')
print linkage_matrix
plt.figure(101)
plt.subplot(1, 2, 1)
plt.title("ascending")
dendrogram(linkage_matrix,
color_threshold=1,
truncate_mode=''lastp'',
labels=array([''a'', ''b'', ''c'', ''d'', ''e'', ''f'']),
distance_sort=''ascending'')
plt.subplot(1, 2, 2)
plt.title("descending")
dendrogram(linkage_matrix,
color_threshold=1,
truncate_mode=''lastp'',
labels=array([''a'', ''b'', ''c'', ''d'', ''e'', ''f'']),
distance_sort=''descending'')
def make_fake_data():
amp = 1000.
x = []
y = []
for i in range(0, 10):
s = 20
x.append(np.random.normal(30, s))
y.append(np.random.normal(30, s))
for i in range(0, 20):
s = 2
x.append(np.random.normal(150, s))
y.append(np.random.normal(150, s))
for i in range(0, 10):
s = 5
x.append(np.random.normal(-20, s))
y.append(np.random.normal(50, s))
plt.figure(1)
plt.title(''fake data'')
plt.scatter(x, y)
d = []
for i in range(len(x) - 1):
for j in range(i+1, len(x) - 1):
d.append(np.sqrt(((x[i]-x[j])**2 + (y[i]-y[j])**2)))
return d
mat = make_fake_data()
plt.figure(102)
plt.title("Three Clusters")
linkage_matrix = linkage(mat, ''single'')
print "three clusters"
print linkage_matrix
dendrogram(linkage_matrix,
truncate_mode=''lastp'',
color_threshold=1,
show_leaf_counts=True)
plt.show()
En primer lugar, el cálculo m -> m - 1 realmente no modificó su resultado ya que la matriz de distancia, que básicamente describe las distancias relativas entre todos los pares únicos, no cambió en su caso específico. (En mi código de ejemplo anterior, todas las distancias son euclidianas, por lo que todas son positivas y consistentes desde puntos en un plano 2d).
Para su segunda pregunta, probablemente necesite desplegar su propia rutina de anotación para hacer lo que quiera, ya que no creo que dendromgram lo soporte de manera nativa ...
Para la última pregunta, show_leaf_counts parece funcionar solo cuando intenta mostrar nodos de hoja no singleton con la opción truncate_mode = ''lastp''. Básicamente, las hojas se agrupan tan juntas que no son fáciles de ver. Entonces tienes la opción de simplemente mostrar una hoja, pero tienes la opción de mostrar (entre paréntesis) cuántos están agrupados en esa hoja.
Espero que esto ayude.
La entrada a linkage()
es una matriz nxm, que representa n puntos en el espacio m-dimensional, o una matriz unidimensional que contiene la matriz de distancia condensada . En su ejemplo, mat
es 3 x 3, por lo que está agrupando tres puntos tridimensionales. La agrupación se basa en la distancia entre estos puntos.
¿Por qué Mat y 1-mat dan racimos idénticos aquí?
Las matrices arrays y 1-mat
producen la misma agrupación porque la agrupación se basa en distancias entre los puntos, y ni una reflexión ( -mat
) ni una traducción ( mat + offset
) de todo el conjunto de datos cambian las distancias relativas entre los puntos .
¿Cómo puedo anotar la distancia a lo largo de cada rama del árbol usando dendrograma para poder comparar las distancias entre pares de nodos?
En el siguiente código, muestro cómo puede usar los datos devueltos por dendrograma para etiquetar los segmentos horizontales del diagrama con la distancia correspondiente. Los valores asociados con las teclas icoord
y dcoord
dan las coordenadas xey de cada U invertida de tres segmentos de la figura. En el dendrograma augmented_dendrogram
estos datos se utilizan para agregar una etiqueta de la distancia (es decir, el valor y) de cada segmento de línea horizontal en dendrograma.
from scipy.cluster.hierarchy import dendrogram
import matplotlib.pyplot as plt
def augmented_dendrogram(*args, **kwargs):
ddata = dendrogram(*args, **kwargs)
if not kwargs.get(''no_plot'', False):
for i, d in zip(ddata[''icoord''], ddata[''dcoord'']):
x = 0.5 * sum(i[1:3])
y = d[1]
plt.plot(x, y, ''ro'')
plt.annotate("%.3g" % y, (x, y), xytext=(0, -8),
textcoords=''offset points'',
va=''top'', ha=''center'')
return ddata
Para su matriz mat
, el dendrograma aumentado es
Entonces el punto ''a'' y ''c'' están separados 1.01 unidades, y el punto ''b'' es 1.57 unidades del clúster [''a'', ''c''].
Parece que se show_leaf_counts
indicador show_leaf_counts
, ¿hay alguna manera de activarlo para que se muestre la cantidad de objetos en cada clase?
La bandera show_leaf_counts
solo se aplica cuando no todos los puntos de datos originales se muestran como hojas. Por ejemplo, cuando trunc_mode = "lastp"
, solo se muestran los últimos p
nodos.
Aquí hay un ejemplo con 100 puntos:
import numpy as np
from scipy.cluster.hierarchy import linkage
import matplotlib.pyplot as plt
from augmented_dendrogram import augmented_dendrogram
# Generate a random sample of `n` points in 2-d.
np.random.seed(12312)
n = 100
x = np.random.multivariate_normal([0, 0], np.array([[4.0, 2.5], [2.5, 1.4]]),
size=(n,))
plt.figure(1, figsize=(6, 5))
plt.clf()
plt.scatter(x[:, 0], x[:, 1])
plt.axis(''equal'')
plt.grid(True)
linkage_matrix = linkage(x, "single")
plt.figure(2, figsize=(10, 4))
plt.clf()
plt.subplot(1, 2, 1)
show_leaf_counts = False
ddata = augmented_dendrogram(linkage_matrix,
color_threshold=1,
p=6,
truncate_mode=''lastp'',
show_leaf_counts=show_leaf_counts,
)
plt.title("show_leaf_counts = %s" % show_leaf_counts)
plt.subplot(1, 2, 2)
show_leaf_counts = True
ddata = augmented_dendrogram(linkage_matrix,
color_threshold=1,
p=6,
truncate_mode=''lastp'',
show_leaf_counts=show_leaf_counts,
)
plt.title("show_leaf_counts = %s" % show_leaf_counts)
plt.show()
Estos son los puntos en el conjunto de datos:
Con p=6
y trunc_mode="lastp"
, el dendrogram
solo muestra la "parte superior" del dendrograma. A continuación, se muestra el efecto de show_leaf_counts
.