2015-03-18 6 views
21

Ich versuche, ein Dendrogramm mit dem children_ Attribut von AgglomerativeClustering zur Verfügung gestellt, aber bisher bin ich kein Glück. Ich kann scipy.cluster nicht verwenden, da das agglomerative Clustering, das in scipy bereitgestellt wird, einige Optionen fehlen, die für mich wichtig sind (wie die Option, die Anzahl der Cluster anzugeben). Ich wäre wirklich dankbar für einen Rat dort draußen.Plot Dendrogramm mit sklearn.AgglomerativeClustering

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

Bitte senden Sie ein Codebeispiel, um die Chancen auf gute Antworten zu vergrößern –

+0

Danke, getan –

+4

Beantwortet das Ihre Frage? [link] (http://stackoverflow.com/questions/26851553/sklearn-agglomerative-clustering-linkage-matrix) – rawkintrevo

Antwort

3

Ich stieß vor einiger Zeit auf genau das gleiche Problem. Die Art, wie ich das verdammte Dendogramm erstellt habe, war das Softwarepaket ete3. Dieses Paket kann Bäume mit verschiedenen Optionen flexibel plotten. Die einzige Schwierigkeit bestand darin, sklearn 's children_ Ausgabe in die Newick Tree format zu konvertieren, die von ete3 gelesen und verstanden werden kann. Außerdem muss ich die Spanne der Dendriten manuell berechnen, da diese Information nicht mit der children_ bereitgestellt wurde. Hier ist ein Ausschnitt des Codes, den ich benutzt habe. Er berechnet den Newick-Baum und zeigt dann die Baumstruktur ete3 an. Für weitere Informationen, wie zum Plotten, werfen Sie einen Blick 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

Verwenden Sie die scipy Implementierung von angehäufter Bündelung statt. Hier ist ein Beispiel.

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) 

können Sie Dokumentation für dendrogramhere für linkagehere und Dokumentation.

+0

Diese Antwort ist nützlich, weil es eine alternative Möglichkeit zum Erstellen und Visualisieren eines hierarchischen Clustering über scipy zeigt, also ich Upvoted es. Dies beantwortet jedoch nicht die ursprüngliche Frage, wie das Dendrogramm eines durch * scikit-learn * erzeugten Clusterings visualisiert werden soll. Es wäre toll, wenn Sie eine Funktion hinzufügen würden, die die Ausgabe von scikit-learn verwendet und eine Datenstruktur wie Z erstellt. – conradlee

0

Für diejenigen, die aus Python heraustreten und die robuste D3-Bibliothek verwenden möchten, ist es nicht sehr schwierig, die d3.cluster() (oder, ich denke, d3.tree()) APIs zu verwenden, um ein schönes, anpassbares Ergebnis zu erzielen.

Eine Demo finden Sie unter jsfiddle.

Das Array children_ funktioniert glücklicherweise problemlos als JS-Array, und der einzige Zwischenschritt ist die Verwendung d3.stratify(), um es in eine hierarchische Darstellung zu verwandeln. Insbesondere müssen wir jeden Knoten ein id und ein parentId haben:

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_ 

Sie mit mindestens O am Ende (n^2) Verhalten hier aufgrund der findIndex Linie, aber es ist wahrscheinlich keine Rolle, bis die n_samples wird riesig. In diesem Fall könnten Sie einen effizienteren Index vorberechnen.

Darüber hinaus ist es ziemlich plug and tuck Verwendung von d3.cluster(). Siehe canonical block von mobostock oder meinen JSFiddle.

N.B. Für meinen Anwendungsfall genügte es, nur Nicht-Blatt-Knoten zu zeigen; es ist ein bisschen schwieriger, die Samples/Blätter zu visualisieren, da diese nicht alle explizit im Array sind.

1

Hier ist ein simple function für die Übernahme eines hierarchischen Clustering-Modells von sklearn und Plotten mit der scipy dendrogram-Funktion. Scheint, dass Grafikfunktionen in sklearn oft nicht direkt unterstützt werden. Sie können eine interessante Diskussion darüber in Bezug auf die Pull-Anfrage für dieses plot_dendrogram Code-Snippet here finden.

Ich würde klarstellen, dass die Verwendung Fall, dass Sie (die Definition Anzahl der Cluster) beschreiben in scipy verfügbar: nachdem Sie das hierarchische Clustering scipy die durchgeführt haben linkage Sie die Hierarchie, was auch immer Anzahl der Cluster schneiden können Sie fcluster wollen mit mit der Anzahl der Cluster, die im Argument t und criterion='maxclust' angegeben sind.