使用 anytree 和 graphviz 在 python 中渲染一棵树,不合并公共节点

Rendering a tree in python using anytree and graphviz, without merging common nodes

我正在使用 python3 的任何树包从列表 ["abc", "abd", "aec", "add", "adcf"] 创建一棵树。在这棵树中,每个列表元素的第一个字符 - a 是一个根,随后,其他字符被添加为它们的子元素。当我渲染树时,它看起来像:

a
├── b
│   ├── c
│   └── d
├── e
│   └── c
└── d
    ├── d
    └── c
        └── f

但是当我使用to_picture方法将树渲染成图片时,图像是-

我不希望合并公共节点,因为它会向我的树中添加不需要的路径。

作为anytree to_picture()函数使用graphviz生成文件。

来自,您可以阅读:

graphviz uses the node id as label. If distinct nodes need to have the same label, the label has to be defined explicitly.

节点使用它们的 id 排列在 graphviz 中。在您的情况下,图表仅使用节点名称生成,然后 graphviz 在您获得它时创建循环边缘。

您真正想要的是每个节点的不同 id 以及与之关联的 labelDotExporter class 有一个名为 nodeattrfunc 的属性,我们可以向其传递函数或 lambda 并为节点生成属性

下面是您将如何根据您的数组进行操作

import anytree
from anytree import Node, RenderTree

data = ["abc", "abd", "aec", "add", "adcf"]
from anytree.exporter import DotExporter

nodes = {}
first_node = None

for elem in data:
    parent_node = None
    parent_node_name = ""
    for i, val in enumerate(elem):
        if i not in nodes:
            nodes[i] = {}
        key = parent_node_name + val
        if key not in nodes[i]:
            print("Creating new node for ", key)
            nodes[i][key] = Node(key, parent=parent_node, display_name=val)

        if first_node is None:
            first_node = nodes[i][key]
        parent_node = nodes[i][key]
        parent_node_name = val

print(nodes)
DotExporter(nodes[0]["a"],
            nodeattrfunc=lambda node: 'label="{}"'.format(node.display_name)).to_dotfile("graph.txt")
DotExporter(nodes[0]["a"],
            nodeattrfunc=lambda node: 'label="{}"'.format(node.display_name)).to_picture("graph.png")

这将生成下面的点文件

digraph tree {
    "a" [label="a"];
    "ab" [label="b"];
    "bc" [label="c"];
    "bd" [label="d"];
    "ae" [label="e"];
    "ec" [label="c"];
    "ad" [label="d"];
    "dd" [label="d"];
    "dc" [label="c"];
    "cf" [label="f"];
    "a" -> "ab";
    "a" -> "ae";
    "a" -> "ad";
    "ab" -> "bc";
    "ab" -> "bd";
    "ae" -> "ec";
    "ad" -> "dd";
    "ad" -> "dc";
    "dc" -> "cf";
}

以及下图

我正要实施建议的修复,但发现使用 UniqueDotExporter 可以完美地处理重复的 ID!