在 Scipy/Matplotlib 中注释树状图节点

Posted

技术标签:

【中文标题】在 Scipy/Matplotlib 中注释树状图节点【英文标题】:Annotating Dendrogram nodes in Scipy/Matplotlib 【发布时间】:2015-07-30 19:30:17 【问题描述】:

我正在尝试在scipy.cluster.hierarchy.dendrogram 生成的树状图中标记节点。

我正在使用augmented dendrogram suggested here,尝试将示例中的集群间距离标签 (1.01,1.57) 替换为 ('a+c','a+b+c') 等字符串。

下面是一个示例链接矩阵

Z = array([[ 2,  7,  0,  2],
           [ 0,  9,  0,  2],
           [ 1,  6,  0,  2],
           [ 5, 10,  0,  3],
           [11, 12,  0,  4],
           [ 4,  8,  0,  2],
           [14, 15,  0,  6],
           [13, 16,  0,  9],
           [ 3, 17,  1, 10]])

对于这个例子,我创建了如下临时标签:

labels = [str(Z[ind,0].astype(int))+'+'+str(Z[ind,1].astype(int)) for ind in range(len(Z))]

并将augmented_dendrogram修改为:

def augmented_dendrogram(labels,*args, **kwargs):
    ddata = cl.dendrogram(*args, **kwargs)
    if not kwargs.get('no_plot', False):
        for ind,(i, d) in enumerate(zip(ddata['icoord'], ddata['dcoord'])):
            x = 0.5 * sum(i[1:3])
            y = d[1]
            plt.plot(x, y, 'ro')
            plt.annotate(labels[ind], (x, y), xytext=(10,15),
                         textcoords='offset points',
                         va='top', ha='center')
return ddata

但是,生成的标签未与树状图中的节点对齐:

如何将标签与正确的节点对齐?

【问题讨论】:

【参考方案1】:

如果我正确理解您的问题,那么您正在寻找由 scipy 的 dendrogram 函数返回的字典中的字段 'leaves'。根据 scipy 的documentation:

对于每个 i,H[i] == j,簇节点 j 出现在叶子从左到右遍历的位置 i 中,其中 和 。如果 j 小于 n,则第 i 个叶节点对应于原始观察。否则,它对应于非单例集群。

简而言之,这意味着您可以使用此字段将标签排序为正确的顺序,例如通过将相应的行更改为:

plt.annotate(labels[ddata['leaves'][ind]], (x, y), xytext=(10,15), textcoords='offset points', va='top', ha='center')

【讨论】:

据我判断,这个解决方案行不通,因为ddata['leaves'] 只包括叶子(即只有终端,但不包括内部节点)。【参考方案2】:

这是我在处理类似问题时找到的解决方案。请注意,OP 中给出的链接矩阵缺少距离(第三列)。我人为地插入这些距离,然后使用它们来识别树状图中节点的正确索引。 scipy distance 函数生成距离已经排序的链接矩阵(至少在使用 ward 作为链接方法时)。

这是我的代码:

    Z = np.array([[ 2,  7,  0,  2],
           [ 0,  9,  0,  2],
           [ 1,  6,  0,  2],
           [ 5, 10,  0,  3],
           [11, 12,  0,  4],
           [ 4,  8,  0,  2],
           [14, 15,  0,  6],
           [13, 16,  0,  9],
           [ 3, 17,  1, 10]], dtype=float)

    Z[:, 2] = np.arange(1., len(Z)+1)
    labels = [str(len(Z)+1+ind)+'='+str(Z[ind,0].astype(int))+'+'+str(Z[ind,1].astype(int)) for ind in range(len(Z))]

    fig, ax = plt.subplots(1, 1, figsize=(10, 10))
    dn = dendrogram(Z, ax=ax)
    ii = np.argsort(np.array(dn['dcoord'])[:, 1])
    for j, (icoord, dcoord) in enumerate(zip(dn['icoord'], dn['dcoord'])):
        x = 0.5 * sum(icoord[1:3])
        y = dcoord[1]
        ind = np.nonzero(ii == j)[0][0]
        ax.annotate(labels[ind], (x,y), va='top', ha='center')
    plt.tight_layout()
    plt.savefig('./tmp.png')
    plt.close(fig)

结果是:

【讨论】:

以上是关于在 Scipy/Matplotlib 中注释树状图节点的主要内容,如果未能解决你的问题,请参考以下文章

Windows10+Python3下安装NumPy+SciPy+Matplotlib

python虚拟环境 + win7-64位安装scipy matplotlib

windows下python2.7版本numpy,Scipy,matplotlib安装

python库安装(numpy+scipy+matplotlib+scikit_learn)

linux下安装numpy,pandas,scipy,matplotlib,scikit-learn

python数据分析之numpy+pandas+scipy+matplotlib+scikit-learn安装