12

Sto utilizzando lo seaborn clustermap per creare cluster e visivamente funziona perfettamente (questo example produce risultati molto simili).Estrazione di cluster da clustermap Seaborn

Tuttavia, ho difficoltà a capire come estrarre i cluster a livello di programmazione. Ad esempio, nel link di esempio, come ho potuto scoprire che 1-1 rh, 1-1 lh, 5-1 rh, 5-1 lh sono un buon cluster? Visivamente è facile. Sto cercando di utilizzare metodi di guardare attraverso i dati, e dendrogrammi ma sto avendo poco successo

EDIT Codice da esempio:

import pandas as pd 
import seaborn as sns 
sns.set(font="monospace") 

df = sns.load_dataset("brain_networks", header=[0, 1, 2], index_col=0) 
used_networks = [1, 5, 6, 7, 8, 11, 12, 13, 16, 17] 
used_columns = (df.columns.get_level_values("network") 
          .astype(int) 
          .isin(used_networks)) 
df = df.loc[:, used_columns] 

network_pal = sns.cubehelix_palette(len(used_networks), 
            light=.9, dark=.1, reverse=True, 
            start=1, rot=-2) 
network_lut = dict(zip(map(str, used_networks), network_pal)) 

networks = df.columns.get_level_values("network") 
network_colors = pd.Series(networks).map(network_lut) 

cmap = sns.diverging_palette(h_neg=210, h_pos=350, s=90, l=30, as_cmap=True) 

result = sns.clustermap(df.corr(), row_colors=network_colors, method="average", 
       col_colors=network_colors, figsize=(13, 13), cmap=cmap) 

Come posso tirare quali modelli sono in quali cluster out di result?

EDIT2 Il result non porta con sé un linkage con la dendrogram_col che credo avrebbe lavorato con fcluster. Ma il valore di soglia per selezionare questo mi confonde. Immagino che i valori nella heatmap superiore alla soglia vengano raggruppati insieme?

risposta

10

Durante l'utilizzo di result.linkage.dendrogram_col o result.linkage.dendrogram_row funzionerà attualmente, sembra essere un dettaglio di implementazione. Il percorso più sicuro consiste nel calcolare esplicitamente i collegamenti e trasferirli alla funzione clustermap, che ha i parametri row_linkage e col_linkage solo per quello.

Sostituzione l'ultima riga nel tuo esempio (result = ...) con il seguente codice fornisce lo stesso risultato di prima, ma si avrà anche row_linkage e col_linkage variabili che è possibile utilizzare con fcluster ecc

from scipy.spatial import distance 
from scipy.cluster import hierarchy 

correlations = df.corr() 
correlations_array = np.asarray(df.corr()) 

row_linkage = hierarchy.linkage(
    distance.pdist(correlations_array), method='average') 

col_linkage = hierarchy.linkage(
    distance.pdist(correlations_array.T), method='average') 

sns.clustermap(correlations, row_linkage=row_linkage, col_linkage=col_linkage, row_colors=network_colors, method="average", 
       col_colors=network_colors, figsize=(13, 13), cmap=cmap) 

In questo particolare esempio, il codice potrebbe essere semplificato di più poiché l'array di correlazioni è simmetrico e pertanto row_linkage e col_linkage saranno identici.

Nota: Una risposta precedente comprendeva un invito a distance.squareshape secondo quanto il codice Seaborn fa, ma che is a bug.

+0

Ehi @Marcel M, non vorresti usare una "matrice di dissimilarità" invece di una matrice di correlazione? Come '1 - np.abs (correlazioni)' o qualcosa del genere? –

+1

@ O.rka Il passaggio delle correlazioni a 'sns.clustermap()' deriva dall'esempio riportato nella domanda, che ho appena copiato. Entrambe le versioni calcolano le distanze tra le correlazioni, quindi alla fine vengono effettivamente utilizzate le distanze, ma ammetto di non sapere quanto senso faccia a farlo (non so perché l'esempio di Seaborn lo faccia). Nel mio progetto, utilizzo direttamente le distanze. –

3

Probabilmente vuoi una nuova colonna nel tuo dataframe con l'appartenenza al cluster. Sono riuscito a fare questo da frammenti di codice assemblati rubati da tutto il web:

import seaborn 
import scipy 

g = seaborn.clustermap(df,method='average') 
den = scipy.cluster.hierarchy.dendrogram(g.dendrogram_col.linkage, 
             labels = df.index, 
             color_threshold=0.60) 
from collections import defaultdict 

def get_cluster_classes(den, label='ivl'): 
    cluster_idxs = defaultdict(list) 
    for c, pi in zip(den['color_list'], den['icoord']): 
     for leg in pi[1:3]: 
      i = (leg - 5.0)/10.0 
      if abs(i - int(i)) < 1e-5: 
       cluster_idxs[c].append(int(i)) 

    cluster_classes = {} 
    for c, l in cluster_idxs.items(): 
     i_l = [den[label][i] for i in l] 
     cluster_classes[c] = i_l 

    return cluster_classes 

clusters = get_cluster_classes(den) 

cluster = [] 
for i in df.index: 
    included=False 
    for j in clusters.keys(): 
     if i in clusters[j]: 
      cluster.append(j) 
      included=True 
    if not included: 
     cluster.append(None) 

df["cluster"] = cluster 

Quindi questo ti dà una colonna con 'g' o 'r' per i cluster serra o rosso-etichettati. Determina il mio color_threshold tracciando il dendrogramma e osservando i valori dell'asse y.

+0

Non funzionerà su dati più grandi dove ci sono più gruppi che colori poiché (ad esempio) il verde si ripeterà, questo raggrupperà i colori. – PvdL