2015-03-18 15 views
21

Sto provando a creare un dendrogramma utilizzando l'attributo children_ fornito da AgglomerativeClustering, ma finora sono sfortunato. Non riesco a utilizzare scipy.cluster poiché il clustering agglomerato fornito in scipy non dispone di alcune opzioni che sono importanti per me (ad esempio l'opzione per specificare la quantità di cluster). Sarei davvero grato per qualsiasi consiglio là fuori.Diagramma dendrogramma utilizzando sklearn.AgglomerativeClustering

import sklearn.cluster 
    clstr = cluster.AgglomerativeClustering(n_clusters=2) 
    clusterer.children_ 
+1

Si prega di inviare un codice di esempio per ingrandire le possibilità di ottenere buone risposte –

+0

grazie, fatto –

+4

Questo risponde alla tua domanda? [collegamento] (http://stackoverflow.com/questions/26851553/sklearn-agglomerative-clustering-linkage-matrix) – rawkintrevo

risposta

3

Mi sono imbattuto nello stesso identico problema qualche tempo fa. Il modo in cui sono riuscito a tracciare il maledetto dendogramma stava usando il pacchetto software ete3. Questo pacchetto è in grado di tracciare in modo flessibile gli alberi con varie opzioni. L'unica difficoltà era quella di convertire l'emissione children_ nello Newick Tree format che può essere letto e compreso da ete3. Inoltre, ho bisogno di calcolare manualmente lo span del dendrite perché quella informazione non è stata fornita con lo children_. Ecco uno snippet del codice che ho usato. Calcola l'albero di Newick e quindi mostra la struttura dei dati dell'albero ete3. Per maggiori dettagli su come trama, date un'occhiata here

import numpy as np 
from sklearn.cluster import AgglomerativeClustering 
import ete3 

def build_Newick_tree(children,n_leaves,X,leaf_labels,spanner): 
    """ 
    build_Newick_tree(children,n_leaves,X,leaf_labels,spanner) 

    Get a string representation (Newick tree) from the sklearn 
    AgglomerativeClustering.fit output. 

    Input: 
     children: AgglomerativeClustering.children_ 
     n_leaves: AgglomerativeClustering.n_leaves_ 
     X: parameters supplied to AgglomerativeClustering.fit 
     leaf_labels: The label of each parameter array in X 
     spanner: Callable that computes the dendrite's span 

    Output: 
     ntree: A str with the Newick tree representation 

    """ 
    return go_down_tree(children,n_leaves,X,leaf_labels,len(children)+n_leaves-1,spanner)[0]+';' 

def go_down_tree(children,n_leaves,X,leaf_labels,nodename,spanner): 
    """ 
    go_down_tree(children,n_leaves,X,leaf_labels,nodename,spanner) 

    Iterative function that traverses the subtree that descends from 
    nodename and returns the Newick representation of the subtree. 

    Input: 
     children: AgglomerativeClustering.children_ 
     n_leaves: AgglomerativeClustering.n_leaves_ 
     X: parameters supplied to AgglomerativeClustering.fit 
     leaf_labels: The label of each parameter array in X 
     nodename: An int that is the intermediate node name whos 
      children are located in children[nodename-n_leaves]. 
     spanner: Callable that computes the dendrite's span 

    Output: 
     ntree: A str with the Newick tree representation 

    """ 
    nodeindex = nodename-n_leaves 
    if nodename<n_leaves: 
     return leaf_labels[nodeindex],np.array([X[nodeindex]]) 
    else: 
     node_children = children[nodeindex] 
     branch0,branch0samples = go_down_tree(children,n_leaves,X,leaf_labels,node_children[0]) 
     branch1,branch1samples = go_down_tree(children,n_leaves,X,leaf_labels,node_children[1]) 
     node = np.vstack((branch0samples,branch1samples)) 
     branch0span = spanner(branch0samples) 
     branch1span = spanner(branch1samples) 
     nodespan = spanner(node) 
     branch0distance = nodespan-branch0span 
     branch1distance = nodespan-branch1span 
     nodename = '({branch0}:{branch0distance},{branch1}:{branch1distance})'.format(branch0=branch0,branch0distance=branch0distance,branch1=branch1,branch1distance=branch1distance) 
     return nodename,node 

def get_cluster_spanner(aggClusterer): 
    """ 
    spanner = get_cluster_spanner(aggClusterer) 

    Input: 
     aggClusterer: sklearn.cluster.AgglomerativeClustering instance 

    Get a callable that computes a given cluster's span. To compute 
    a cluster's span, call spanner(cluster) 

    The cluster must be a 2D numpy array, where the axis=0 holds 
    separate cluster members and the axis=1 holds the different 
    variables. 

    """ 
    if aggClusterer.linkage=='ward': 
     if aggClusterer.affinity=='euclidean': 
      spanner = lambda x:np.sum((x-aggClusterer.pooling_func(x,axis=0))**2) 
    elif aggClusterer.linkage=='complete': 
     if aggClusterer.affinity=='euclidean': 
      spanner = lambda x:np.max(np.sum((x[:,None,:]-x[None,:,:])**2,axis=2)) 
     elif aggClusterer.affinity=='l1' or aggClusterer.affinity=='manhattan': 
      spanner = lambda x:np.max(np.sum(np.abs(x[:,None,:]-x[None,:,:]),axis=2)) 
     elif aggClusterer.affinity=='l2': 
      spanner = lambda x:np.max(np.sqrt(np.sum((x[:,None,:]-x[None,:,:])**2,axis=2))) 
     elif aggClusterer.affinity=='cosine': 
      spanner = lambda x:np.max(np.sum((x[:,None,:]*x[None,:,:]))/(np.sqrt(np.sum(x[:,None,:]*x[:,None,:],axis=2,keepdims=True))*np.sqrt(np.sum(x[None,:,:]*x[None,:,:],axis=2,keepdims=True)))) 
     else: 
      raise AttributeError('Unknown affinity attribute value {0}.'.format(aggClusterer.affinity)) 
    elif aggClusterer.linkage=='average': 
     if aggClusterer.affinity=='euclidean': 
      spanner = lambda x:np.mean(np.sum((x[:,None,:]-x[None,:,:])**2,axis=2)) 
     elif aggClusterer.affinity=='l1' or aggClusterer.affinity=='manhattan': 
      spanner = lambda x:np.mean(np.sum(np.abs(x[:,None,:]-x[None,:,:]),axis=2)) 
     elif aggClusterer.affinity=='l2': 
      spanner = lambda x:np.mean(np.sqrt(np.sum((x[:,None,:]-x[None,:,:])**2,axis=2))) 
     elif aggClusterer.affinity=='cosine': 
      spanner = lambda x:np.mean(np.sum((x[:,None,:]*x[None,:,:]))/(np.sqrt(np.sum(x[:,None,:]*x[:,None,:],axis=2,keepdims=True))*np.sqrt(np.sum(x[None,:,:]*x[None,:,:],axis=2,keepdims=True)))) 
     else: 
      raise AttributeError('Unknown affinity attribute value {0}.'.format(aggClusterer.affinity)) 
    else: 
     raise AttributeError('Unknown linkage attribute value {0}.'.format(aggClusterer.linkage)) 
    return spanner 

clusterer = AgglomerativeClustering(n_clusters=2,compute_full_tree=True) # You can set compute_full_tree to 'auto', but I left it this way to get the entire tree plotted 
clusterer.fit(X) # X for whatever you want to fit 
spanner = get_cluster_spanner(clusterer) 
newick_tree = build_Newick_tree(clusterer.children_,clusterer.n_leaves_,X,leaf_labels,spanner) # leaf_labels is a list of labels for each entry in X 
tree = ete3.Tree(newick_tree) 
tree.show() 
2

Utilizzare il SciPy attuazione agglomerative clustering, invece. Ecco un esempio.

from scipy.cluster.hierarchy import dendrogram, linkage 

data = [[0., 0.], [0.1, -0.1], [1., 1.], [1.1, 1.1]] 

Z = linkage(data) 

dendrogram(Z) 

È possibile trovare la documentazione relativa linkagehere e la documentazione per dendrogramhere.

+0

Questa risposta è utile perché indica un modo alternativo di creare e visualizzare un clustering gerarchico tramite scipy, quindi lo ha svalutato. Tuttavia questo non risponde alla domanda originale, che riguardava la visualizzazione del dendrogramma di un cluster creato da * scikit-learn *. Sarebbe fantastico se tu aggiungessi una funzione che prendesse l'output di scikit-learn e creasse una struttura di dati come Z. – conradlee

0

Per coloro che desiderano uscire da Python e utilizzare la robusta libreria D3, non è molto difficile utilizzare le API d3.cluster() (o, credo, d3.tree()) per ottenere un risultato piacevole e personalizzabile.

Vedere lo jsfiddle per una dimostrazione.

L'array children_ funziona facilmente come un array JS e l'unico passaggio intermedio è utilizzare d3.stratify() per trasformarlo in una rappresentazione gerarchica. In particolare, abbiamo bisogno di ogni nodo di avere un id e un parentId:

var N = 272; // Your n_samples/corpus size. 
var root = d3.stratify() 
    .id((d,i) => i + N) 
    .parentId((d, i) => { 
    var parIndex = data.findIndex(e => e.includes(i + N)); 
    if (parIndex < 0) { 
     return; // The root should have an undefined parentId. 
    } 
    return parIndex + N; 
    })(data); // Your children_ 

Si finisce con almeno O (n^2) il comportamento qui a causa della linea di findIndex, ma probabilmente non importa fino a quando il n_samples diventa enorme, nel qual caso è possibile precomporre un indice più efficiente.

Oltre a questo, è praticamente l'uso di spina e chug di d3.cluster(). Vedi mbostock's canonical block o il mio JSFiddle.

N.B. Per il mio caso d'uso, bastava semplicemente mostrare i nodi non foglia; è un po 'più complicato visualizzare i campioni/foglie, poiché questi potrebbero non essere tutti nell'array children_ esplicitamente.

1

Ecco uno simple function per prendere un modello di clustering gerarchico da sklearn e tracciarlo usando la funzione scipy dendrogram. Sembra che le funzioni grafiche non siano spesso supportate direttamente da sklearn. Puoi trovare una discussione interessante su quella relativa alla richiesta pull per questo frammento di codice plot_dendrogramhere.

mi piacerebbe chiarire che il caso d'uso che si descrive (numero di cluster definire) è disponibile in SciPy: dopo aver eseguito il raggruppamento gerarchico utilizzando SciPy di ​​linkage è possibile tagliare la gerarchia a qualunque numero di cluster che si desidera utilizzare fcluster con numero di cluster specificato nell'argomento t e nell'argomento criterion='maxclust'.