web-dev-qa-db-fra.com

Extraction de clusters à partir de clustermap seaborn

J'utilise le seaborn clustermap pour créer des clusters et visuellement cela fonctionne très bien (cela exemple produit des résultats très similaires).

Cependant, j'ai du mal à trouver comment extraire par programme les clusters. Par exemple, dans l'exemple de lien, comment pourrais-je savoir que 1-1 rh, 1-1 lh, 5-1 rh, 5-1 lh font un bon cluster? Visuellement, c'est facile. J'essaie d'utiliser des méthodes de recherche dans les données et des dendrogrammes mais j'ai peu de succès

[~ # ~] éditez [~ # ~] Code de l'exemple:

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)

Comment puis-je extraire les modèles dans quels clusters de result?

EDIT2 Le result porte avec lui un linkage avec le dendrogram_col avec lequel je PENSE que je travaillerais avec fcluster . Mais la valeur seuil à sélectionner qui me déroute. Je suppose que les valeurs de la carte thermique qui sont supérieures au seuil seraient regroupées?

23
sedavidw

En utilisant result.linkage.dendrogram_col ou result.linkage.dendrogram_row fonctionnera actuellement, il semble que ce soit un détail d'implémentation. La route la plus sûre consiste à calculer d'abord les liens de manière explicite et à les transmettre à la fonction clustermap, qui a row_linkage et col_linkage paramètres juste pour ça.

Remplacement de la dernière ligne de votre exemple (result = ...) avec le code suivant donne le même résultat qu'auparavant, mais vous aurez également row_linkage et col_linkage variables que vous pouvez utiliser avec fcluster etc.

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)

Dans cet exemple particulier, le code pourrait être simplifié davantage car le tableau de corrélations est symétrique et donc row_linkage et col_linkage sera identique.

Remarque: Une réponse précédente comprenait un appel à distance.squareshape selon ce que fait le code dans seaborn, mais cela est un bug .

15
Marcel M

Vous souhaitez probablement une nouvelle colonne dans votre trame de données avec l'appartenance au cluster. J'ai réussi à le faire à partir d'extraits de code assemblés volés sur le 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

Cela vous donne donc une colonne avec "g" ou "r" pour les grappes étiquetées en vert ou en rouge. Je détermine mon seuil de couleur en traçant le dendrogramme et en observant les valeurs de l'axe y.

6
sjc