Python graphviz:如何对根树的相同深度的节点进行排序
Python graphviz: how to order nodes at the same depth of a rooted tree
我不知道如何对有根树中相同深度的节点强制执行顺序。它已在 this thread 中被问到但没有解决。问题是,就像那个问题的作者所说的那样,graphviz(至少是 graphviz 的 python 库)无法维持创建顺序。
例如:
g = Graph(format="png")
g.node("x", shape="doublecircle")
g.node("y", shape="circle") # removed in graph 2
g.node("z", shape="circle") # removed in graph 2
g.node("nil1", shape="none", label="NIL")
g.node("nil2", shape="none", label="NIL")
g.edge("x", "nil1")
g.edge("x", "y")
g.edge("y", "z")
g.edge("y", "nil2")
上面的代码在我的电脑上生成了这张图
如您所见,y 节点和 nil1 节点交换了位置,但它们不应该。
但是在我从上面的代码中删除第 3 行和第 4 行之后,输出发生了变化!
这很有趣。我不知道为什么删除定义两个节点及其形状的两条线会改变输出。即使这两条线确实影响了输出,但预期输出无论如何都不同于两个产生的输出中的任何一个。知道发生了什么以及我的代码有什么问题吗?
在同一等级上,Graphviz 按照定义的顺序绘制节点。在TB
(从上到下)中,同一级别的布局节点按从左到右的顺序绘制。
在您的情况下,如果您希望 nil1
节点出现在 之前 (左侧) y
节点 - 您必须定义它之前。与 z
和 nil2
.
相同
这应该有效:
g = Graph(format="png")
g.node("x", shape="doublecircle")
g.node("nil1", shape="none", label="NIL")
g.node("y", shape="circle") # removed in graph 2
g.node("nil2", shape="none", label="NIL")
g.node("z", shape="circle") # removed in graph 2
g.edge("x", "nil1")
g.edge("x", "y")
g.edge("y", "z")
g.edge("y", "nil2")
为了清楚起见,这里有一个基本示例。
digraph name {
rankdir=TB
a b c d
}
这里我们按字母顺序定义了节点,因此我们将在图表上得到相同的顺序:
如果我们颠倒顺序:
digraph name {
rankdir=TB
d c b a
}
图表上的顺序也会颠倒:
回答为什么删除节点定义会影响顺序的问题。如果不需要特殊属性,您实际上可以省略定义节点。在这种情况下,graphviz 将在看到带有未定义节点的边缘语句时隐式添加节点定义,例如这个:
digraph {
a -> b
c -> d
}
翻译为:
digraph {
a
b
a -> b
c
d
c -> d
}
所以在你的例子中,从一开始就删除了 y
和 z
的定义,这使得它们出现得更晚,就在它们的边缘定义之前。这隐含地将 nil1
和 nil2
定义置于 y
和 z
之上。这与我一开始建议的显式解决方案具有相同的效果。
我不知道如何对有根树中相同深度的节点强制执行顺序。它已在 this thread 中被问到但没有解决。问题是,就像那个问题的作者所说的那样,graphviz(至少是 graphviz 的 python 库)无法维持创建顺序。
例如:
g = Graph(format="png")
g.node("x", shape="doublecircle")
g.node("y", shape="circle") # removed in graph 2
g.node("z", shape="circle") # removed in graph 2
g.node("nil1", shape="none", label="NIL")
g.node("nil2", shape="none", label="NIL")
g.edge("x", "nil1")
g.edge("x", "y")
g.edge("y", "z")
g.edge("y", "nil2")
上面的代码在我的电脑上生成了这张图
如您所见,y 节点和 nil1 节点交换了位置,但它们不应该。
但是在我从上面的代码中删除第 3 行和第 4 行之后,输出发生了变化!
这很有趣。我不知道为什么删除定义两个节点及其形状的两条线会改变输出。即使这两条线确实影响了输出,但预期输出无论如何都不同于两个产生的输出中的任何一个。知道发生了什么以及我的代码有什么问题吗?
在同一等级上,Graphviz 按照定义的顺序绘制节点。在TB
(从上到下)中,同一级别的布局节点按从左到右的顺序绘制。
在您的情况下,如果您希望 nil1
节点出现在 之前 (左侧) y
节点 - 您必须定义它之前。与 z
和 nil2
.
这应该有效:
g = Graph(format="png")
g.node("x", shape="doublecircle")
g.node("nil1", shape="none", label="NIL")
g.node("y", shape="circle") # removed in graph 2
g.node("nil2", shape="none", label="NIL")
g.node("z", shape="circle") # removed in graph 2
g.edge("x", "nil1")
g.edge("x", "y")
g.edge("y", "z")
g.edge("y", "nil2")
为了清楚起见,这里有一个基本示例。
digraph name {
rankdir=TB
a b c d
}
这里我们按字母顺序定义了节点,因此我们将在图表上得到相同的顺序:
如果我们颠倒顺序:
digraph name {
rankdir=TB
d c b a
}
图表上的顺序也会颠倒:
回答为什么删除节点定义会影响顺序的问题。如果不需要特殊属性,您实际上可以省略定义节点。在这种情况下,graphviz 将在看到带有未定义节点的边缘语句时隐式添加节点定义,例如这个:
digraph {
a -> b
c -> d
}
翻译为:
digraph {
a
b
a -> b
c
d
c -> d
}
所以在你的例子中,从一开始就删除了 y
和 z
的定义,这使得它们出现得更晚,就在它们的边缘定义之前。这隐含地将 nil1
和 nil2
定义置于 y
和 z
之上。这与我一开始建议的显式解决方案具有相同的效果。