绘制 networkx 图,节点标签默认为节点名称
- 2025-04-01 09:56:00
- admin 原创
- 11
问题描述:
NetworkX 功能强大,但我尝试绘制一个默认显示节点标签的图表,令我惊讶的是,对于刚接触 Networkx 的人来说,这个看似简单的任务竟然如此繁琐。这里有一个示例,展示了如何向图表中添加标签。
https://networkx.github.io/documentation/latest/examples/drawing/labels_and_colors.html
这个例子的问题在于,它使用了太多的步骤和方法,而我想要做的只是在绘制图形时显示与节点名称相同的标签。
# Add nodes and edges
G.add_node("Node1")
G.add_node("Node2")
G.add_edge("Node1", "Node2")
nx.draw(G) # Doesn't draw labels. How to make it show labels Node1, Node2 along?
有没有办法nx.draw(G)
在图表中内联显示默认标签(在本例中为 Node1、Node2)?
解决方案 1:
tl/dr:只需添加with_labels=True
到nx.draw
呼叫即可。
您正在查看的页面有些复杂,因为它展示了如何设置许多不同的东西作为标签,如何为不同的节点赋予不同的颜色,以及如何提供精确控制节点位置的功能。所以有很多事情要做。
但是,您似乎只是希望每个节点使用自己的名称,并且对默认颜色和默认位置感到满意。因此
import networkx as nx
import pylab as plt
G=nx.Graph()
# Add nodes and edges
G.add_edge("Node1", "Node2")
nx.draw(G, with_labels = True)
plt.savefig('labels.png')
如果你想让节点标签不同,你可以发送一个字典作为参数。例如,
labeldict = {}
labeldict["Node1"] = "shopkeeper"
labeldict["Node2"] = "angry man with parrot"
nx.draw(G, labels=labeldict, with_labels = True)
解决方案 2:
我觉得更好的答案是不要使用 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 非常相似。https ://networkx.org/documentation/latest/tutorial.html上的大部分 NetworkX 教程适用于 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 obj(因为 graphviz 拥有最好的文档和最高的用户群 ~19k 所以我更喜欢它),所以出于这些原因我会选择 pygraphviz。Pygravix 也有文档,虽然很小但却让我很满意(虽然不如 graphviz 好,但我不知道如何从 networkx 制作 graphviz 图表)。做出这些决定很难,但我不能永远停留在这个上面,这似乎足够用心。此外,networkx 很棒,因为我也可以将 dgl 图表转换为 networkx(并且重新标记很简单)。
考虑到这些原因,让我给你我编写的示例代码,该代码使用 pygraphviz 完成你想要的操作(但如果你知道如何操作,你可以用 pydot 来完成,使用 networkx 转换为 pydot obj 很简单,请参阅我之前的链接):
# https://stackoverflow.com/questions/28533111/plotting-networkx-graph-with-node-labels-defaulting-to-node-name
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()=}')
# Since the actual graph is undirected, we convert it for visualization purpose.
g = g.to_networkx().to_undirected()
print(f'{g=}')
# relabel
int2label = {0: "app", 1: "cons", 2: "with", 3: "app3", 4: "app4", 5: "app5"}
g = nx.relabel_nodes(g, int2label)
# https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.layout
g = nx.nx_agraph.to_agraph(g)
print(f'{g=}')
print(f'{g.string()=}')
# draw
g.layout()
g.draw("file.png")
# https://stackoverflow.com/questions/20597088/display-a-png-image-from-python-on-mint-15-linux
img = mpimg.imread('file.png')
plt.imshow(img)
plt.show()
# remove file https://stackoverflow.com/questions/6996603/how-to-delete-a-file-or-folder
Path('./file.png').expanduser().unlink()
# import os
# os.remove('./file.png')
输出:
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 "" {
app -- cons [key=0,
id=0];
app -- with [key=0,
id=1];
app -- app3 [key=0,
id=2];
app -- app4 [key=0,
id=3];
app -- app5 [key=0,
id=4];
}
'
不过我想保留这个关于 pydot 可视化的链接,因为它总体上看起来非常有用:使用 pydot 显示图形而不保存,并且可能为其他人提供 pydot 答案(如果他们需要的话)。不过,我很乐意看到支持 pydot 的论据。
编辑1:如果您想按属性而不是按标签进行绘图,请参阅此答案:NetworkX 节点属性绘图注意,按照我建议的方式重新标记并不总是具有预期的语义(例如,它可能会连接两个不打算连接的节点)。
编辑2:如果你想绘制属性,而不是意外发生自循环,请参见以下答案:使用 PyGraphviz 在 graph\nodes 上绘制更多信息