我想使用 Graphvize 绘制下图:
xx = nx.DiGraph()
xx.add_node("P")
xx.add_node("C0")
xx.add_node("C1")
xx.add_node("I2")
xx.add_node("C3")
xx.add_node("C4")
xx.add_node("I5")
xx.add_node("C6")
xx.add_node("C7")
xx.node["C1"]['pos'] = (2,3)
xx.node["I2"]['pos'] = (4,5)
xx.node["C3"]['pos'] = (6,7)
xx.node["C4"]['pos'] = (6,5)
xx.node["I5"]['pos'] = (4,1)
xx.node["C6"]['pos'] = (6,2)
xx.node["C7"]['pos'] = (6,0)
xx.node["P"]['pos'] = (-2,3)
xx.node["C0"]['pos'] = (0,3)
xx.add_edge("P", "C0")
xx.add_edge("C0", "C1")
xx.add_edge("C1", "I2")
xx.add_edge("I2", "C3")
xx.add_edge("I2", "C4")
xx.add_edge("C1", "I5")
xx.add_edge("I5", "C6")
xx.add_edge("I5", "C7")
layout = dict((n, xx.node[n]["pos"]) for n in xx.nodes_iter())
nx.draw(xx,pos=layout,node_color='white')
nx.write_dot(xx,'66666.dot')
使用 matplotlitb 我得到所有节点的正确位置:
使用 Graphviz 没有位置的图形。
我的问题:是否有可能在 Graphviz 中添加正确的位置?是否可以直接在 python 中打开文件“66666.dot”?
非常感谢您的帮助!
最佳答案
由于NetworkX库来不及更新和打破向后兼容性,问题中描述的问题仍然存在于新版本的库中,我将尝试更新NetworkX 2.8.7变体的示例代码并提供一个解决方案。
更新 NetworkX 2.8.7 的示例代码
- 错误:
NameError:未定义名称“nx”
如何修复:这不是版本问题,而是一个 Minimal, Reproducible Example问题,所以只需安装 NetworkX 库(pip install networkx
)并在代码开头包含该库:import networkx as nx
- 错误:
AttributeError:“有向图”对象没有属性“节点”
如何修复:使用xx.nodes["C1"]['pos']
而不是xx.node["C1"]['pos']
。引用:NetworkX Migration guide from 1.X to 2.0 - 错误:
AttributeError:“DiGraph”对象没有属性“nodes_iter”
如何修复:使用xx.nodes()
而不是xx.nodes_iter()
。 Reference . - 错误:
AttributeError:模块 networkx 没有属性 write_dot
如何修复:使用nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
而不是nx.write_dot(xx,'66666.dot')
。引用:AttributeError: 'module' object has no attribute 'write_dot' for networkx library
Note: If you do not already have graphviz and
pygraphviz
installed, you may need to install it.pygraphviz
library for Windows can be installed as a precompiled binary downloaded from Unofficial Windows Binaries for Python Extension Packages with commandpip install nameOfDownloadedPackage.whl
.
示例代码现在如下所示:
# Listing 1
import networkx as nx
xx = nx.DiGraph()
xx.add_node("P")
xx.add_node("C0")
xx.add_node("C1")
xx.add_node("I2")
xx.add_node("C3")
xx.add_node("C4")
xx.add_node("I5")
xx.add_node("C6")
xx.add_node("C7")
xx.nodes["C1"]['pos'] = (2,3)
xx.nodes["I2"]['pos'] = (4,5)
xx.nodes["C3"]['pos'] = (6,7)
xx.nodes["C4"]['pos'] = (6,5)
xx.nodes["I5"]['pos'] = (4,1)
xx.nodes["C6"]['pos'] = (6,2)
xx.nodes["C7"]['pos'] = (6,0)
xx.nodes["P"]['pos'] = (-2,3)
xx.nodes["C0"]['pos'] = (0,3)
xx.add_edge("P", "C0")
xx.add_edge("C0", "C1")
xx.add_edge("C1", "I2")
xx.add_edge("I2", "C3")
xx.add_edge("I2", "C4")
xx.add_edge("C1", "I5")
xx.add_edge("I5", "C6")
xx.add_edge("I5", "C7")
layout = dict((n, xx.nodes[n]["pos"]) for n in xx.nodes())
nx.draw(xx,pos=layout,node_color='white')
nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
正确输出图像的解决方案
TLDR:使用格式 pos="x,y!"
记录文件中节点的位置,而不是 pos=("x,y")
。
现在我们有了一个可行的示例,我们可以继续讨论如何绘制节点的正确位置。
创建的文件 66666.dot
包含 dot
language 中的文本:
strict digraph "" {
P [pos="(-2, 3)"];
C0 [pos="(0, 3)"];
P -> C0;
C1 [pos="(2, 3)"];
C0 -> C1;
I2 [pos="(4, 5)"];
C1 -> I2;
I5 [pos="(4, 1)"];
C1 -> I5;
C3 [pos="(6, 7)"];
I2 -> C3;
C4 [pos="(6, 5)"];
I2 -> C4;
C6 [pos="(6, 2)"];
I5 -> C6;
C7 [pos="(6, 0)"];
I5 -> C7;
}
如果你有graphviz安装后,您可以在终端中运行命令:
dot -Tpng 66666.dot -o output.png
并检查将在文件 output.png
中绘制的内容:
问题是在 graphviz 库中有不同的 layout engines , 负责绘制节点的位置和 dot
layout engine不理解属性 pos="(4, 1)"
, 所以我们将使用 neato
layout engine了解此属性(如其文档中所述)。我们在终端中得到输出:
Error: node P, position (-2, 3), expected two doubles
Error: node C0, position (0, 3), expected two doubles
Error: node C1, position (2, 3), expected two doubles
Error: node I2, position (4, 5), expected two doubles
Error: node I5, position (4, 1), expected two doubles
Error: node C3, position (6, 7), expected two doubles
Error: node C4, position (6, 5), expected two doubles
Error: node C6, position (6, 2), expected two doubles
Error: node C7, position (6, 0), expected two doubles
和文件output.png
:
我们看到坐标不影响绘图位置,并且写入有关输入数据格式不正确的错误。如此处所述(converting network graph to graphviz),此错误是由于坐标值输入不正确造成的。 graphviz 理解的格式是 pos="x,y"
,而不是 pos="(x,y)"
。
Note: Full value format looks like this
%f,%f('!')?
(e.g.pos="42,24!"
). The optional!
indicates the node position should not change.
让我们在将图形对象写入文件之前更改其位置格式:
# Listing 2
# See the previous part of the code in Listing 1
for node in xx:
xx.nodes[node]['pos'] = "{},{}!".format(
xx.nodes[node]['pos'][0], xx.nodes[node]['pos'][1])
nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
现在文件 66666.dot
中的结果:
strict digraph "" {
P [pos="-2,3!"];
C0 [pos="0,3!"];
P -> C0;
C1 [pos="2,3!"];
C0 -> C1;
I2 [pos="4,5!"];
C1 -> I2;
I5 [pos="4,1!"];
C1 -> I5;
C3 [pos="6,7!"];
I2 -> C3;
C4 [pos="6,5!"];
I2 -> C4;
C6 [pos="6,2!"];
I5 -> C6;
C7 [pos="6,0!"];
I5 -> C7;
}
output.png
中的结果:
看起来挺好的。为了使其看起来像 matplotlib 的结果,您需要添加更多属性 label
, shape
到节点和arrowhead
到边缘:
# Listing 3
# See the previous part of the code in Listing 1
for node in xx:
xx.nodes[node]['pos'] = "{},{}!".format(
xx.nodes[node]['pos'][0], xx.nodes[node]['pos'][1])
xx.nodes[node]['label'] = ""
xx.nodes[node]['shape'] = "circle"
for edge in xx.edges:
xx.edges[edge]['arrowhead'] = 'box'
nx.drawing.nx_agraph.write_dot(xx,'66666.dot')
output.png
中的结果:
Note (optional): To show the result of the matplotlib, add at the beginning of the file
import matplotlib.pyplot as plt
and at the endplt.show()
.
直接用Python打开.dot
文件
- 您可以使用 standard Python library 的函数读取文件.
- 要在 Python 中直接打开文件
filename.dot
并获取图形对象,可以使用函数networkx.drawing.nx_agraph.read_dot
. - 如果你需要在 GUI 中嵌入图形,这取决于框架,SO 和互联网上有很多关于如何在 wxPython、PyQt5、Tkinter 中使用 Matplotlib 嵌入图形的答案。
关于Python Networkx graphviz : Plot right position of nodes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35730535/