使用默认为节点名称的节点标签绘制 networkx 图

Plotting networkx graph with node labels defaulting to node name

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)?

tl/dr:只需将 with_labels=True 添加到 nx.draw 调用即可。

您正在查看的 page 有点复杂,因为它显示了如何将许多不同的东西设置为标签,如何为不同的节点赋予不同的颜色,以及如何提供仔细控制节点位置。所以发生了很多事情。

但是,您似乎只希望每个节点都使用自己的名称,并且您对默认颜色和默认位置很满意。所以

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)

我觉得更好的答案是不使用networkx来绘制。他们明确警告您图形可视化很难,networkx 主要用于图形分析(来自 https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.layout):

Drawing NetworkX provides basic functionality for visualizing graphs, but its main goal is to enable graph analysis rather than perform graph visualization. In the future, graph visualization functionality may be removed from NetworkX or only available as an add-on package.

Proper graph visualization is hard, and we highly recommend that people visualize their graphs with tools dedicated to that task. Notable examples of dedicated and fully-featured graph visualization tools are Cytoscape, Gephi, Graphviz and, for LaTeX typesetting, PGF/TikZ. To use these and other such tools, you should export your NetworkX graph into a format that can be read by those tools. For example, Cytoscape can read the GraphML format, and so, networkx.write_graphml(G, path) might be an appropriate choice.

因此我的建议是将图形转换为某种格式,该格式具有用于图形可视化的专用软件,然后绘制(例如 pydot、pygraphviz、graphviz 等)。我怀疑 pydot 和 pygraphviz 出于某种原因是最好的,因为 networkx 只支持这两个。从 pygraphviz 的文档看来它有一个类似的 api 所以如果你已经想使用 networkx (https://pygraphviz.github.io/documentation/stable/tutorial.html):

它可能是最容易使用的

The API is very similar to that of NetworkX. Much of the NetworkX tutorial at https://networkx.org/documentation/latest/tutorial.html is applicable to PyGraphviz. See http://pygraphviz.github.io/documentation/latest/reference/api_notes.html for major differences.

此外,pydot 目前还没有真正的文档(这让我很困扰。如果它在我的浏览器上看起来不太好,或者让我觉得项目没有被认真对待它的开发人员 idk 感觉有些不对,即使它有更多的用户 pydot 15k vs pygraphviz 4k)参考:https://github.com/pydot/pydot/pull/241.

此外,pygraphviz 似乎比常规 graphviz 参考具有更精细的控制:。此外,我不知道如何将 networkx 直接转换为 graphviz obj(因为 graphviz 拥有最好的文档和最高的用户群 ~19k,所以我确实更喜欢它),因此出于这些原因我会选择 pygraphviz。 Pygravix 也有文档,虽然很小但让我很开心(虽然不如 graphviz 但我知道如何从 networkx 制作 graphviz 图)。做出这些决定很难,但我不能永远坚持下去,这似乎足够谨慎。另外,networkx 很好,因为我也可以将 dgl 图转换为 networkx(并且重新标记很简单)。

考虑到这些原因,让我给你我写的示例代码,它可以使用 pygraphviz 做你想做的事情(但如果你知道如何使用 pydot 可以做到,使用 networkx 转换为 pydot obj 是微不足道的,请参阅我之前的 link):

# 

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")

# 
img = mpimg.imread('file.png')
plt.imshow(img)
plt.show()

# remove file 
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 "" {\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 可视化的 link 因为它通常看起来非常有用:Display graph without saving using pydot 并且可能会在其他人需要时为他们提供 pydot 答案。不过,我很乐意看到支持 pydot 的论据。


Edit1:如果您想按属性而不是按标签绘制,请参阅此答案:NetworkX node attribute drawing请注意,按照我建议的方式重新标记并不总是具有预期的语义(例如,它可能连接两个节点不打算加入)。


Edit2:如果您想绘制属性而不是意外发生自循环,请参阅此答案:Draw more information on graph\nodes using PyGraphviz