层次聚类树形图中的节点索引

时间:2016-10-25 12:26:27

标签: python machine-learning tree scipy cluster-analysis

我正在寻找注释层次聚类dendrogram,但是在绘制时将scipy.cluster.hierarchy.dendrogram生成的节点索引与原始链接矩阵中的节点索引相关联(例如,生成时) scipy.cluster.hierarchy.linkage)。

例如,假设我们有以下示例(改编自此SO question),

import numpy as np
from scipy.cluster.hierarchy import dendrogram, linkage
from matplotlib import pyplot as plt
%matplotlib inline

# generate two clusters: a with 10 points, b with 5:
np.random.seed(1)
a = np.random.multivariate_normal([10, 0], [[3, 1], [1, 4]], size=[10,])
b = np.random.multivariate_normal([0, 20], [[3, 1], [1, 4]], size=[5,])
X = np.concatenate((a, b),)
Z = linkage(X, 'ward')
# make distances between pairs of children uniform 
# (re-scales the horizontal (distance) axis when plotting)
Z[:,2] = np.arange(Z.shape[0])+1 

def plot_dendrogram(linkage_matrix, **kwargs):

    ddata = dendrogram(linkage_matrix, **kwargs)
    idx = 0
    for i, d, c in zip(ddata['icoord'], ddata['dcoord'], 
                       ddata['color_list']):       
        x = 0.5 * sum(i[1:3])
        y = d[1]
        plt.plot(y, x, 'o', c=c)
        plt.annotate("%.3g" % idx, (y, x), xytext=(15, 5),
                             textcoords='offset points',
                             va='top', ha='center')
        idx += 1
plot_dendrogram(Z, labels=np.arange(X.shape[0]),
                truncate_mode='level', show_leaf_counts=False, 
               orientation='left')

产生以下树形图: enter image description here

原始X矩阵具有(X.shape[0] == 15)个样本,垂直轴上的刻度标签对应于每个树叶的样本ID。每个节点的编号是dendrogram函数返回的该节点的id。现在,如果我们查看原始的链接矩阵,前两列给出了每个树节点的子节点,

print(Z[:,:2].astype('int'))
  [[ 0  3]
   [ 1  8]
   [ 6 16]
   [ 2  5]
   ...
   [22 24]
   [23 25]
   [26 27]]

例如,链接矩阵中的节点0用于子项[0, 3],但在上面的树形图上标记为数字9。同样,节点1标记为数字4等。

我想知道找出这两个指数之间的对应关系的最简单方法是什么?我查看了dendrogram函数,但没有看到任何简单的方法(特别是如果我们将树形图截断到某个级别(例如truncate_mode='level', p=2)...

注意:我实际上正在使用sklearn.cluster.AgglomerativeClustering给出的链接矩阵,但这对于这个问题并不重要(如github issue所示)。< / p>

Note2 :或者,如果有办法计算每个树形图节点的叶子列表,这也可以解决我的问题。

2 个答案:

答案 0 :(得分:1)

我可以从发布的材料中推断出我希望你已经注意到的事情:

  1. 链接矩阵节点按聚类的顺序编号。该 原始节点为0-14。 [0 3]成为节点15,[1 8]是节点16, [6 [1 8]]是节点17等。
  2. 树状图节点从左(下)前的根,深度优先,上(右)分支开始编号,标签N-1下降到0。
  3. 左右由链接矩阵中的列确定。
  4. 链接矩阵具有2N + 1行:N + 1个原始节点和N个聚类。要重建树状图标签,请从矩阵的最后一行开始, [26 27] 。这会得到标签 N-1 ,或13.在右侧节点(第1列)上重新出现,然后在左侧(第0列)重新出现。每次分配时减少标签值。

    这是否足够清楚?

    递归深度对你来说是一个问题?对于N个节点,树深度不应该大于log2(N),对于一百万个节点,树深度不应该大于22-25。您的运行时堆栈无法处理?我的默认值是一千。

    在任何情况下, 都难以转换为迭代。从根节点开始创建一堆未解析的节点。

    stack = [root]
    while stack is not empty:
        node = stack.pop()
        stack.push (node.right)
        stack.push (node.left)
        process node  # assign node number, etc.
    

    这使得维护节点计数器变得更加容易,因为它现在是一个局部变量。还要注意这是一个基本的图搜索算法:&#34;当我们有节点时,抓住列表中的下一个节点,将其邻居推送到列表上(但检查每个节点是否已经处理),并处理当前节点。&#34;

    对于深度优先,使用堆栈;对于广度优先,请使用队列。

答案 1 :(得分:0)

我发现的另一个解决方案是使用图中的几何坐标从树形图(_DendrogramChildren class here¹中可用的代码)计算给定节点的所有子节点。然后因为每个节点都有一个唯一的子列表,我们可以将图上的节点索引与链接矩阵的节点索引相关联。

然而,这个解决方案并不令人满意(使用图中节点的坐标),不能很好地扩展,也不适用于截断的树状图。上面@Prune提出的解决方案更合适。

¹不会在这里重新张贴,因为它太长了