2012-08-11 22 views
36

que estoy usando dendrogram de scipy para trazar agrupación jerárquica utilizando matplotlib de la siguiente manera:cómo trazar y anotar dendrogramas de agrupación jerárquica en scipy/matplotlib

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: en primer lugar, ¿por qué mat y dar 1-mat agrupamientos 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 ignora el indicador show_leaf_counts, ¿hay alguna manera de activarlo para que se muestre el número de objetos en cada clase? Gracias. enter image description here

Respuesta

62

La entrada a linkage() es una matriz n x m, que representa n puntos en el espacio m-dimensional , o una matriz unidimensional que contiene el condensed distance matrix. 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 agrupamientos idénticos aquí?

Las matrices mat y 1-mat producen la misma agrupación debido a que la agrupación se basa en las distancias entre los puntos, y ni una reflexión (-mat) ni una traducción (mat + offset) de los datos de todo el conjunto de cambiar las relativas distancias 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, I mostrar cómo se puede utilizar los datos devueltos por dendrograma para etiquetar los horizontales segmentos del diagrama con la distancia correspondiente.Los valores asociados con las claves icoord y dcoord dan las coordenadas xey de cada U invertida de tres segmentos de la figura. En augmented_dendrogram, este dato se usa 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 la matriz de mat, el dendrograma aumentada es

dendrogram for three points

Así punto 'a' y 'c' son 1.01 unidades de distancia, y el punto 'b' es de 1.57 unidades de la cluster ['a', 'c'].

Parece que show_leaf_counts bandera se ignora, es que hay una manera de encenderlo manera que se muestra el número de objetos de cada clase?

La bandera show_leaf_counts solo se aplica cuando no todos los datos originales puntos se muestran como hojas. Por ejemplo, cuando trunc_mode = "lastp", solo se muestran los últimos nodos p.

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:

scatter plot of 100 points

Con p=6 y trunc_mode="lastp", dendrogram sólo muestra la "parte superior" de el dendrograma. A continuación, se muestra el efecto de show_leaf_counts.

Show effect of show_leaf_counts

+0

La primera parte de su respuesta es correcta, pero incompleta. La entrada al enlace también puede ser "Una matriz de distancia condensada o redundante. Una matriz de distancia condensada es una matriz plana que contiene el triangular superior de la matriz de distancia. Esta es la forma en que pdist devuelve" De: https: //docs.scipy. org/doc/scipy-0.18.0/reference/generated/scipy.cluster.hierarchy.linkage.html – Featherlegs

+0

@Featherlegs Gracias por señalarlo. En realidad, el docstring para el enlace fue corregido recientemente para reflejar la realidad del código. El docstring corregido aún no está en una versión. 'linkage' acepta una matriz de 1 d que contiene la matriz de distancia condensada o una matriz de puntos de 2 d. No acepta una matriz de distancia densa. Actualizaré mi respuesta para reflejar esto. –

+0

Aquí está la versión de desarrollo de la documentación 'linkage': http://scipy.github.io/devdocs/generated/scipy.cluster.hierarchy.linkage.html –

13

Creo que hay un par de malentendidos en cuanto al uso de las funciones que está tratando de usar. He aquí un fragmento de código completamente de trabajo 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 cambió el resultado desde la matriz de distancias, que básicamente describe las distancias relativas entre todos pares únicos, no cambiaron en su caso específico. (En mi código de ejemplo anterior, todas las distancias son euclidianas, así 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 forma 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.

+0

Taro, su respuesta apareció justo cuando estaba terminando la mía. No hay respuestas en casi un mes, y luego dos en pocos minutos. Me pregunto qué tan común es ... –

+0

@WarrenWeckesser ¡Jaja, solución buena y completa que usted hizo! Acabo de investigar y escribí el mío, pero realmente no he usado esta función en particular. Es bueno que nuestras respuestas sean en su mayoría consistentes. –

Cuestiones relacionadas