12

Я использую seaborn clustermap для создания кластеров, и визуально он отлично работает (этот example дает очень похожие результаты).Извлечение кластеров из морской кластерной карты

Однако мне трудно понять, как программно извлекать кластеры. Например, в примере ссылки, как я могу узнать, что 1-1 rh, 1-1 lh, 5-1 rh, 5-1 lh сделать хороший кластер? Визуально это легко. Я пытаюсь использовать методы просматривал данные и дендрограммы, но у меня мало успеха

EDIT код из примера:

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) 

Как я могу вытащить какие модели, в которых кластеры из от result?

EDIT2result действительно несет с собой linkage в с dendrogram_col который я думаю, будет работать с fcluster. Но пороговое значение для выбора, которое меня путает. Я бы предположил, что значения в тепловой карте, которые выше порога, будут сгруппированы вместе?

ответ

10

При использовании result.linkage.dendrogram_col или result.linkage.dendrogram_row в настоящее время работает, это, как представляется, деталь реализации. Самый безопасный маршрут состоит в том, чтобы сначала явно вычислить связи и передать их функции clustermap, которая имеет параметры row_linkage и col_linkage.

Замена последней строки в примере (result = ...) с помощью следующего кода дает тот же результат, как и раньше, но вы также будете иметь row_linkage и col_linkage переменные, которые вы можете использовать с fcluster и т.д.

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) 

В этом конкретном примере код может быть упрощен больше, поскольку массив корреляций является симметричным, и поэтому row_linkage и col_linkage будут идентичными.

Примечание: Предыдущий ответ был призыв к distance.squareshape согласно тому, что код в Сиборн делает, но is a bug.

+0

Эй, @ Марсель М, разве вы не хотите использовать «матрицу несходства» вместо корреляционной матрицы? Как '1 - np.abs (корреляции)' или что-то еще? –

+1

@ O.rka Передача корреляций с 'sns.clustermap()' происходит из приведенного в вопросе морского примера, который я только что скопировал. Обе версии вычисляют расстояния между корреляциями, поэтому на самом деле расстояния фактически используются, но я признаю, что не знаю, насколько это важно для этого (я не знаю, почему это делает пример с морскими судами). В моем собственном проекте я использую дистанции напрямую. –

3

Возможно, вам понадобится новый столбец в вашем кадре данных с членством в кластере. Я сумел сделать это из собранных фрагментов кода, украденных из всей сети:

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 

Так что это дает столбец с «г» или «R» для зелено- или красно-меченого кластеров. Я определяю свой color_threshold, создавая график дендрограммы и просматривая значения оси y.

+0

Это не будет работать над большими данными, где больше групп, чем цветов, поскольку (например) зеленый будет повторяться, это будет группировать цвета. – PvdL