根据相似性度量(例如余弦相似性等)对 Holoviews 热图的列和行进行重新排序

Reorder columns and rows of Holoviews Heatmap based on similarity measure (e.g. cosine similarity etc.)

我很惊讶之前似乎没有人问过这个问题。

假设我有一个 pandas 数据框(随机示例),我可以获得带有 Holoviews 和 Bokeh 渲染器的热图:

rownames = 'ABCDEFGHIJKLMNO'
df = pd.DataFrame(np.random.randint(0,20,size=(20, len(rownames))), columns=list(rownames))
hv.HeatMap({'x': df.columns, 'y': df.index, 'z': df}, 
           kdims=[('x', 'Col Categories'), ('y', 'Row Categories')], 
           vdims='z').opts(cmap="viridis", width=520, height=520)

数据(x 和 y)是分类数据,因此行或列的初始顺序并不重要。我想根据一些相似性度量对 rows/columns 进行排序。

一种方法是使用seaborn clustermap:

heatmap_sns = sns.clustermap(df, metric="cosine", standard_scale=1, method="ward", cmap="viridis")

输出如下所示:

列和行已根据相似性排序(在本例中,余弦基于点积;其他可用,例如 'correlation' 等)。

但是,我想在 Holoviews 中显示 clustermap。如何从 seaborn 矩阵更新原始数据帧的顺序?

可以使用以下方法从 seaborn clustermap 访问重新排序的 columns/rows 的索引:

> print(f'rows: {heatmap_sns.dendrogram_row.reordered_ind}')
> print(f'columns: {heatmap_sns.dendrogram_col.reordered_ind}')
rows: [5, 0, 13, 2, 18, 7, 4, 16, 12, 19, 14, 15, 10, 3, 8, 6, 17, 11, 1, 9]
columns: [7, 1, 10, 5, 9, 0, 8, 13, 2, 6, 14, 3, 4, 11, 12]

要更新 row/column 原始数据帧的顺序:

# get col and row names by ID
colname_list = [df.columns[col_id] for col_id in heatmap_sns.dendrogram_col.reordered_ind]
rowname_list = [df.index[row_id] for row_id in heatmap_sns.dendrogram_row.reordered_ind]
# update dataframe
df_ro = df.reindex(rowname_list)
df_ro = df_ro[colname_list]

我在这里是通过首先获取名称来完成的,也许甚至还有一种直接的方法可以通过索引更新 columns/rows。

hv.HeatMap({'x': df_ro.columns, 'y': df_ro.index, 'z': df_ro}, 
           kdims=[('x', 'Col Categories'), ('y', 'Row Categories')], 
           vdims='z').opts(cmap="viridis", width=520, height=520)

由于我使用的是随机数据,因此类别中的顺序很少,但图片看起来还是少了一些噪点。请注意,holoviews/df y 轴与 seaborn clustermap-matrix 相比只是反向的,这就是图形看起来翻转的原因。

A​​lex 的答案(即之前接受的答案)的一种更简洁的方法是使用 sns.clustermap() 函数返回对象的 data2d 属性。这个 属性 包含重新排序的数据(即聚类后的数据)。所以:

df_ro = heatmap_sns.data2d  

替换以下所有行:

# get col and row names by ID
colname_list = [df.columns[col_id] for col_id in 
heatmap_sns.dendrogram_col.reordered_ind]
rowname_list = [df.index[row_id] for row_id in 
heatmap_sns.dendrogram_row.reordered_ind]
# update dataframe
df_ro = df.reindex(rowname_list)
df_ro = df_ro[colname_list]