如何可视化(树状图)分层项目的字典?

How to visualize (dendrogram) a dictionary of hierarchical items?

这是我第一次使用 Python 从字典格式的分层数据中进行可视化。数据的最后一部分如下所示:

d = {^2820: [^391, ^1024], ^2821: [^759, 'w', ^118, ^51], ^2822: [^291, 'o'], ^2823: [^25, ^64], ^2824: [^177, ^2459], ^2825: [^338, ^1946], ^2826: [^186, ^1511], ^2827: [^162, 'i']}

所以我在列表上有索引,指向字典的键(索引)。我想这可以用作可视化的基础结构,如果我错了请纠正我。数据上的字符是 "end nodes/leaves",不引用任何索引。

我找到了可能用于可视化的 NetworkX,但我不知道从哪里开始使用它和我的数据。我希望它会像这样简单:

import networkx as nx
import matplotlib.pyplot as plt

d = {^2820: [^391, ^1024], ^2821: [^759, 'w', ^118, ^51], ^2822: [^291, 'o'], ^2823: [^25, ^64], ^2824: [^177, ^2459], ^2825: [^338, ^1946], ^2826: [^186, ^1511], ^2827: [^162, 'i']}

G = nx.Graph(d)
nx.draw(G)
plt.show()

我正在寻找层次树状图或某种聚类作为输出。抱歉,目前我还不确定什么是最好的可视化效果,可能与此类似:

更新

NetworkX 的使用其实非常简单。我正在提供其他简单的示例数据并寻找答案是否也可以通过树状图而不是有线网络图来可视化?

# original sequence: a,b,c,d,b,c,a,b,c,d,b,c
d = {^1: ['b', 'c'], ^2: ['a', ^1, 'd', ^1], 'S': [^2, ^2]}
G = nx.Graph(d)
nx.draw_spring(G, node_size=300, with_labels=True)

正如我们所见,图表显示了简单的关系,但没有我愿意做的数据的层次结构和顺序。有向图给出了更多细节,但仍然无法从中构建原始序列:

对于树状图,显然需要按照第一个答案中指出的那样计算权重和端节点。对于这种方法,数据结构可能是这样的:

d = {'a': [], 'b': [], 'c': [], 'd': [], ^1: ['b', 'c'], ^2: ['a', ^1, 'd', ^1], 'S': [^2, ^2]}

一个想法是使用SciPy's dendrogram function to draw your dendrogram. To do so, you just need to create the linkage matrix Z, which is described in the documentation of the SciPy linkage function。链接矩阵 Z 的每一行 [x, y, w, z] 描述 w 的权重 xy 合并形成具有 z 个叶子的有根子树.

为了演示,我创建了一个简单的示例,它使用了一个具有三片叶子的小型树状图,如下所示:

我使用以下代码创建了这个可视化:

# Load required modules
import networkx as nx
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram

# Construct the graph/hierarchy
d           = { 0: [1, 'd'], 1: ['a', 'b', 'c'], 'a': [], 'b': [], 'c': [], 'd': []}
G           = nx.DiGraph(d)
nodes       = G.nodes()
leaves      = set( n for n in nodes if G.out_degree(n) == 0 )
inner_nodes = [ n for n in nodes if G.out_degree(n) > 0 ]

# Compute the size of each subtree
subtree = dict( (n, [n]) for n in leaves )
for u in inner_nodes:
    children = set()
    node_list = list(d[u])
    while len(node_list) > 0:
        v = node_list.pop(0)
        children.add( v )
        node_list += d[v]

    subtree[u] = sorted(children & leaves)

inner_nodes.sort(key=lambda n: len(subtree[n])) # <-- order inner nodes ascending by subtree size, root is last

# Construct the linkage matrix
leaves = sorted(leaves)
index  = dict( (tuple([n]), i) for i, n in enumerate(leaves) )
Z = []
k = len(leaves)
for i, n in enumerate(inner_nodes):
    children = d[n]
    x = children[0]
    for y in children[1:]:
        z = tuple(subtree[x] + subtree[y])
        i, j = index[tuple(subtree[x])], index[tuple(subtree[y])]
        Z.append([i, j, float(len(subtree[n])), len(z)]) # <-- float is required by the dendrogram function
        index[z] = k
        subtree[z] = list(z)
        x = z
        k += 1

# Visualize
dendrogram(Z, labels=leaves)
plt.show()

有几个关键事项需要注意:

  1. 给出 d 数据结构,我使用 NetworkX 有向图 (DiGraph) 。方向性很重要,因此我们可以确定哪些节点是 leaves(没有 children -> 度数为零)和 inner_nodes(两个或更多 children -> non-zero出度).
  2. 通常树状图中的每条边都有一些权重,但您的示例中没有任何权重。相反,我使用以每个内部节点 n 为根的子树中的叶子数作为 n.
  3. 的权重
  4. 如果一个内部节点有两个以上 children,您必须添加 "dummy" 个内部节点,因为链接矩阵的每一行都将两个节点合并在一起。这就是为什么我写for y in children[1:]:,等等

我猜您可能能够在创建示例中的 d 之前根据数据的外观来简化此代码,因此这可能更像是一个概念验证。