【问题标题】:Relation between dendrogram plot coordinates and ClusterNodes in scipyscipy中树状图坐标与ClusterNodes之间的关系
【发布时间】:2017-09-16 17:51:08
【问题描述】:

我正在寻找一种方法,根据to_tree 返回的ClusterNode 返回dendrogram 图中的聚类点坐标。

使用 scipy 从以下数据构建树状图:

X = data
Y = pdist(X)
Z = linkage(Y)
dend = dendrogram(Z)
rootnode, nodesList = to_tree(Z, rd=True)

我想做的是构建一个函数get_coords(somClusterNode),它将返回元组(x, y),指定节点在图中的位置。

感谢this answer,我设法弄清楚如何从树状图返回值中获取位置,例如:

i, d = list(zip(dend['icoord'], dend['dcoord']))[-1]
x = 0.5 * sum(i[1:3])
y = d[1]
plt.plot(x, y, 'ro')

但我可以弄清楚 nodesList 排序和 icoord/dcoord 排序之间的关系,以便将一个映射到另一个。

你知道我可以在哪里寻找吗?

感谢您的帮助!

【问题讨论】:

  • 你使用的是什么版本的 scipy?当我尝试运行您的代码时出现错误:ValueError: Valid methods when the raw observations are omitted are 'single', 'complete', 'weighted', and 'average'.您确定第 3 行不应该是Z = linkage(X, method="ward")吗?
  • 我使用 SciPy v.0.19.0 和 python v.3.5.2
  • 似乎两者都兼容:“输入 y 可能是一维压缩距离矩阵或二维观察向量数组。”在docs.scipy.org/doc/scipy/reference/generated/…
  • 是的,但是如果您指定 method="ward",我的版本仅在提供原始观察时才有效。升级我的 scipy 安装以查看问题是否仍然存在...
  • 好的。那么任何方法都可以。我不认为我的问题取决于我选择的链接方法。我相应地编辑了帖子。

标签: plot scipy hierarchical-clustering linkage dendrogram


【解决方案1】:

每个树状图只映射到一棵 ClusterNodes 树,但任何 ClusterNodes 树都可以映射到无限数量的树状图。因此,从节点 ID 到 (x,y) 位置的映射可能只是树状图数据结构中的另一个字段,而不是 ClusterNode 的函数。因此,我没有定义函数get_coords,而是将一个字典附加到dend,将节点ID 映射到(x,y) 坐标。您可以使用

访问这些职位
x,y = dend['node_id_to_coord'][node_id] # node_id is an integer as returned by ClusterNode.id

代码:

import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import linkage, dendrogram, to_tree
from scipy.spatial.distance import pdist

# create some random data
X = np.random.rand(10, 3)

# get dendrogram
Z = linkage(pdist(X), method="ward")
dend = dendrogram(Z)

# ----------------------------------------
# get leave coordinates, which are at y == 0

def flatten(l):
    return [item for sublist in l for item in sublist]
X = flatten(dend['icoord'])
Y = flatten(dend['dcoord'])
leave_coords = [(x,y) for x,y in zip(X,Y) if y==0]

# in the dendogram data structure,
# leave ids are listed in ascending order according to their x-coordinate
order = np.argsort([x for x,y in leave_coords])
id_to_coord = dict(zip(dend['leaves'], [leave_coords[idx] for idx in order])) # <- main data structure

# ----------------------------------------
# get coordinates of other nodes

# this should work but doesn't:

# # traverse tree from leaves upwards and populate mapping ID -> (x,y);
# # use linkage matrix to traverse the tree optimally
# # (each row in the linkage matrix corresponds to a row in dend['icoord'] and dend['dcoord'])
# root_node, node_list = to_tree(Z, rd=True)
# for ii, (X, Y) in enumerate(zip(dend['icoord'], dend['dcoord'])):
#     x = (X[1] + X[2]) / 2
#     y = Y[1] # or Y[2]
#     node_id = ii + len(dend['leaves'])
#     id_to_coord[node_id] = (x, y)

# so we need to do it the hard way:

# map endpoint of each link to coordinates of parent node
children_to_parent_coords = dict()
for i, d in zip(dend['icoord'], dend['dcoord']):
    x = (i[1] + i[2]) / 2
    y = d[1] # or d[2]
    parent_coord = (x, y)
    left_coord = (i[0], d[0])
    right_coord = (i[-1], d[-1])
    children_to_parent_coords[(left_coord, right_coord)] = parent_coord

# traverse tree from leaves upwards and populate mapping ID -> (x,y)
root_node, node_list = to_tree(Z, rd=True)
ids_left = range(len(dend['leaves']), len(node_list))

while len(ids_left) > 0:

    for ii, node_id in enumerate(ids_left):
        node = node_list[node_id]
        if (node.left.id in id_to_coord) and (node.right.id in id_to_coord):
            left_coord = id_to_coord[node.left.id]
            right_coord = id_to_coord[node.right.id]
            id_to_coord[node_id] = children_to_parent_coords[(left_coord, right_coord)]

    ids_left = [node_id for node_id in range(len(node_list)) if not node_id in id_to_coord]

# plot result on top of dendrogram
ax = plt.gca()
for node_id, (x, y) in id_to_coord.iteritems():
    if not node_list[node_id].is_leaf():
        ax.plot(x, y, 'ro')
        ax.annotate(str(node_id), (x, y), xytext=(0, -8),
                    textcoords='offset points',
                    va='top', ha='center')

dend['node_id_to_coord'] = id_to_coord

【讨论】:

  • 非常感谢!这是我试图解决这个问题的日子!泰,泰,泰!
  • 我尝试编辑以将.iteritems() 更改为.items() 以使其与python3 兼容但是,堆栈不允许编辑低于6 个字符。
  • 我的荣幸。我将在一秒钟内进行编辑。另外,我刚刚发现我可以使用链接矩阵更有效地遍历树。编辑传入。
  • 好的,这个版本现在简单多了。我不知何故没有意识到链接矩阵中的每个链接都对应于dendrogram['icoord'] 和dendrogram['dcoords']。
  • 是的,你是对的,节点 ID 有时会出现在所有错误的地方。我也会回滚。
【解决方案2】:

还有另一种方法:

树状图的 id 似乎是由树的从右到左的反向遍历生成的。这允许我们构造从node.id 到icoord 和dcoord 的索引的翻译,如下所示:

def rl_traversal(node):
    # skipping leaves
    if not node.is_leaf():
        yield node.id
        yield from rl_traversal(node.right)
        yield from rl_traversal(node.left)

id_map = dict(zip( rl_traversal(root), reversed(range(root.get_count()-1))) ))
# id_map[node_id] = dendogram_id

然后可以通过dendo['icoord'][id_map[node_id]]获取节点坐标

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-06
    • 2013-01-25
    • 2020-06-06
    • 2016-06-22
    • 1970-01-01
    • 2019-11-01
    • 1970-01-01
    • 2012-03-31
    相关资源
    最近更新 更多