我认为更好的解决方案是不使用networkx进行绘图。他们明确警告说,图形可视化很难,并且networkx主要用于图形分析(来源:
https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.layout):
“NetworkX提供了基本的图形可视化功能,但其主要目标是实现图形分析而不是执行图形可视化。在未来,图形可视化功能可能会从NetworkX中删除或仅作为附加包提供。”
“适当的图形可视化很困难,我们强烈建议人们使用专门用于该任务的工具来可视化其图形。专门和功能完备的图形可视化工具的著名例子包括Cytoscape、Gephi、Graphviz以及用于LaTeX排版的PGF/TikZ。为了使用这些和其他类似的工具,您应该将您的NetworkX图导出为可以被这些工具读取的格式。例如,Cytoscape可以读取GraphML格式,因此,networkx.write_graphml(G,path)可能是一个合适的选择。”
因此,我的建议是将图形转换为一些具有专用软件进行图形可视化的格式,然后再进行绘制(例如pydot、pygraphviz、graphviz等)。我怀疑pydot和pygraphviz之所以是最好的,可能是因为networkx只支持这两个。从pygraphviz的文档中可以看出,它具有类似的API,因此如果您已经想使用networkx,则可能是最容易使用的(
https://pygraphviz.github.io/documentation/stable/tutorial.html):
“API与NetworkX非常相似。 NetworkX教程的大部分内容在
https://networkx.org/documentation/latest/tutorial.html上也适用于PyGraphviz。请参见
http://pygraphviz.github.io/documentation/latest/reference/api_notes.html了解主要差异。”
此外,目前pydot实际上没有文档(这让我个人感到困扰。我不知道是因为它在我的浏览器上看起来不好还是因为它让我感到该项目的开发者并未认真对待,无论它的用户数量高还是低,pydot 15k vs pygraphviz 4k)参考:
https://github.com/pydot/pydot/pull/241。
此外,似乎pygraphviz比常规graphviz具有更细粒度的控制能力,参考:
Graphviz vs PyGraphViz。此外,我不知道如何将networkx直接转换为graphviz对象(因为graphviz拥有最好的文档和最高的用户基数约19k,所以我更喜欢它),因此我会选择pygraphviz。尽管Pygravix的文档虽小但让我感到满意(虽然不如graphviz好,但我不知道如何从networkx制作graphviz图形)。做出这些决定很难,但我不能永远停留在这里,而且这看起来足够周到。此外,networkx很好,因为我也可以将dgl图形转换为networkx(重新标记也很简单)。
考虑到这些原因,让我给您展示使用pygraphviz编写的示例代码,该代码可以实现您想要的功能(但如果您找到了方法,使用pydot也可以实现,使用networkx将其转换为pydot对象非常容易,请参见我之前的链接)。
import dgl
import numpy as np
import torch
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from pathlib import Path
g = dgl.graph(([0, 0, 0, 0, 0], [1, 2, 3, 4, 5]), num_nodes=6)
print(f'{g=}')
print(f'{g.edges()=}')
g = g.to_networkx().to_undirected()
print(f'{g=}')
int2label = {0: "app", 1: "cons", 2: "with", 3: "app3", 4: "app4", 5: "app5"}
g = nx.relabel_nodes(g, int2label)
g = nx.nx_agraph.to_agraph(g)
print(f'{g=}')
print(f'{g.string()=}')
g.layout()
g.draw("file.png")
img = mpimg.imread('file.png')
plt.imshow(img)
plt.show()
Path('./file.png').expanduser().unlink()
输出:
g=Graph(num_nodes=6, num_edges=5,
ndata_schemes={}
edata_schemes={})
g.edges()=(tensor([0, 0, 0, 0, 0]), tensor([1, 2, 3, 4, 5]))
g=<networkx.classes.multigraph.MultiGraph object at 0x7f8443e94250>
g=<AGraph <Swig Object of type 'Agraph_t *' at 0x7f846117a930>>
g.string()='graph "" {\n\tapp -- cons [key=0,\n\tid=0];\napp -- with [key=0,\nid=1];\napp -- app3 [key=0,\nid=2];\napp -- app4 [key=0,\nid=3];\napp -- app5 [key=0,\nid=4];\n}\n'
虽然我想留下这个关于pydot可视化的链接,因为它通常非常有用:
使用pydot显示图形而不保存,并且可能为其他人提供pydot答案,如果他们需要。虽然我很想看到支持pydot的论点。
编辑1:如果您想按属性而不是按标签绘制图形,请参见此答案:
NetworkX节点属性绘制请注意,我建议的重新标记方式并不总是具有预期的语义(例如,它可能会连接两个本来不应该连接的节点)。
编辑2:如果您想绘制属性而不是意外发生自环,请参见此答案:
使用PyGraphviz在图形节点上绘制更多信息。