带 plotly 的树状图 - 如何为层次聚类设置自定义链接方法

Posted

技术标签:

【中文标题】带 plotly 的树状图 - 如何为层次聚类设置自定义链接方法【英文标题】:Dendrogram with plotly - how to set a custom linkage method for hierarchical clustering 【发布时间】:2020-07-17 06:45:52 【问题描述】:

我是 plotly 新手,需要绘制具有组平均链接的树状图。

我知道create_dendrogram() 中有一个distfun 参数,但我不知道要传递给该参数以获取组平均链接distfun 参数显然必须是可调用的。我应该将什么函数传递给它?

作为旁注,我有一个样本成对距离矩阵 0 13 0 2 14 0 17 1 18 0 当我传递给create_dendrogram() 方法时,它似乎产生了不正确的结果。我在这里做错了什么?

代码:

import plotly.figure_factory as ff

import numpy as np

X = np.matrix([[0,0,0,0],[13,0,0,0],[2,14,0,0],[17,1,18,0]])

names = list("0123")
fig = ff.create_dendrogram(X, orientation='left', labels=names)
fig.update_layout(width=800, height=800)
fig.show()

我应该这样做的代码是从 plotly 网站 bc idk 复制而来的。 本站:https://plotly.com/python/v3/dendrogram/

【问题讨论】:

分享您获得此代码的链接? 【参考方案1】:

您可以使用scipy.cluster.hierarchy.linkage() 选择链接方式 通过create_dendrogram() 函数中的linkagefun 参数。

例如,使用UPGMA (Unweighted Pair Group Method with Arithmetic mean) algorithm:

import plotly.figure_factory as ff
import scipy.cluster.hierarchy as sch
import numpy as np

X = np.matrix([[0,0,0,0],[13,0,0,0],[2,14,0,0],[17,1,18,0]])

names = "0123"
fig = ff.create_dendrogram(X,
                           orientation='left',
                           labels=names,
                           linkagefun=lambda x: sch.linkage(x, "average"),)
fig.update_layout(width=800, height=800)
fig.show()

请注意,X 必须是数据样本矩阵。

【讨论】:

确实没有错误。这取决于您指定的方法。举个例子,我用average。其他的联动方式可以看here和代码here。 但它应该首先对较低的距离值进行分组。 “平均”是我需要的方法,但分组是错误的。由于 (1,3) 的距离为 1,它应该将 1,3 组合在一起,但在执行时将 (0,1) 组合在一起。 好的,我明白了。 X 必须是数据样本矩阵。不是距离矩阵。 如何将 NxN 成对距离矩阵转换为链接所需的“一维压缩距离矩阵”? scipy.spatial.distance.pdist 似乎没有奏效。 (它需要 2x2 矩阵) 我使用scipy.spatial.distance.squareform (据说)将成对矩阵转换为压缩矩阵,但是当我运行代码时我得到in get_dendrogram_traces d=distfun(X) in pdist raise ValueError: A 2-dimentional array must be passed. 如何解决这个问题?【参考方案2】:

这有点老了,但对于其他有类似问题的人,我认为distfun 参数只是指定了您希望如何将数据矩阵转换为压缩距离矩阵 - 您自己定义函数。

例如,经过一番头撞后,我拼凑在一起data_to_dist 将数据矩阵转换为 Jaccard 距离矩阵,然后将其压缩。您应该知道 plotly 的 dendrogram 实现不会检查您的矩阵是否压缩,因此您的 distfun 需要确保发生这种情况。也许这是错误的,但看起来distfun 应该只接受一个位置参数(数据矩阵)并返回一个对象(压缩距离矩阵):

import plotly.figure_factory as ff
import numpy as np
from scipy.spatial.distance import jaccard, squareform

def jaccard_dissimilarity(feature_list1, feature_list2, filler_val): #binary
    all_features = set([i for i in feature_list1 if i != filler_val])#filler val can be used to even up ragged lists and ignore certain dtypes ie prots not in a module
    all_features.update(set([i for i in feature_list2 if i != filler_val]))#works for both numpy arrays and lists
    counts_1 = [1 if feature in feature_list1 else 0 for feature in all_features]
    counts_2 = [1 if feature in feature_list2 else 0 for feature in all_features]
    return jaccard(counts_1, counts_2)

def data_to_dist_matrix(mn_data, filler_val = 0):
    #notes:
        #the original plotly example uses pdist to find manhatten distance for clustering.  
        #pdist 'Returns a condensed distance matrix Y' - https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.pdist.html#scipy.spatial.distance.pdist.
        #a condensed distance matrix is required for input into scipy linkage for clustering.  
        #plotly dendrogram function does not do this conversion to the output of a given distfun call - https://github.com/plotly/plotly.py/blob/cfad7862594b35965c0e000813bd7805e8494a5b/packages/python/plotly/plotly/figure_factory/_dendrogram.py#L340
        #therefore you should convert distance matrix to condensed form yourself as below with squareform
    distance_matrix = np.array([[jaccard_dissimilarity(a,b, filler_val) for b in mn_data] for a in mn_data])
    return squareform(distance_matrix)



# toy data to visually check clustering looks sensible
data_array = np.array([[1, 2, 3,0], 
                       [2, 3, 10, 0], 
                       [4, 5, 6, 0],
                       [5, 6, 7, 0],
                       [7, 8, 1, 0],
                       [1,2,8,7],
                       [1,2,3,8],
                       [1,2,3,4]])

y_labels = [f'MODULE_i' for i in range(8)]

#this is the distance matrix and condensed distance matrix made by data_to_dist_matrix and is only included so I can check what it's doing
dist_matrix = np.array([[jaccard_dissimilarity(a,b, 0) for b in data_array] for a in data_array])
condensed_dist_matrix = data_to_dist_matrix(data_array, 0)

# Create Side Dendrogram
fig = ff.create_dendrogram(data_array, 
                           orientation='right', 
                           labels = y_labels,
                           distfun = data_to_dist_matrix)

【讨论】:

以上是关于带 plotly 的树状图 - 如何为层次聚类设置自定义链接方法的主要内容,如果未能解决你的问题,请参考以下文章

在SPSS中生成层次聚类分析的树状图

R语言层次聚类(hierarchical clustering):使用scale函数进行特征缩放hclust包层次聚类(创建距离矩阵聚类绘制树状图dendrogram,在树状图上绘制红色矩形框)

R语言ggplot2可视化:为层次聚类树状图dendrogram中的簇进行着色在树状图dendrogram中为不同的层次聚类簇配置不同的色彩

R语言绘图——层次聚类图及树状图添加side bar

Plotly:如何为所有子图设置 xticks?

Python使用matplotlib可视化树状图层次聚类系统树图树状图根据给定的距离度量将相似点分组在一起并根据点的相似性将它们组织成树状图链接起来(Dendrogram)