python的NetworkX库可以帮助我们构建网络拓扑并实现拓扑的可视化,这对于网络研究,日常展示等都是十分的方便。不过,我发现并没有文章比较系统的介绍如何在可视化的图中展示节点和边的属性,从而让图更加的具体,直观。因此就想写一篇文章来教会大家如何在图中显示节点和边的属性。
先说总体思路,想要展示节点和边的属性,我们需要借助如下两个函数(重点关注labels参数):
draw_networkx_labels(G, pos, labels=a dictionary used to describe what you want to show in node)
draw_networkx_edge_labels(G, pos, edge_labels=a dictionary used to describe what you want to show in edge)
draw_networkx_labels会在图中的节点上显示你通过labels参数传入的数据。
同理,draw_networkx_edge_labels则会在图中的边上显示你通过edge_labels参数传入的数据。
需要注意的是,这两个函数只显示了labels,并不会将点啊,边啊显示出来。所以,为了显示完整的拓扑,在调用以上两个函数之前,需要先调用draw(G, pos)将基础的点边拓扑先画出来,然后再调用draw_networkx_labels和draw_networkx_edge_labels来画出点的labels和边的labels。
模板代码如下:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
# write your topology construction logic here
pos = nx.spring_layout(G) # choose a layout from https://networkx.github.io/documentation/latest/reference/drawing.html#module-networkx.drawing.layout
nx.draw(G, pos)
node_labels = {a dictionary contains what you want you show. Key:node_name. Value:text shown in node G.nodes[node_name]}
nx.draw_networkx_labels(G, pos, labels=node_labels)
edge_labels = {a dictionary contains what you want you show. Key:edge. Value:text shown in edge}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.show()
看到这,你可能还不是很明白到底该怎么用。不要着急,下面我们通过几个例子来实战一下,看完这几个例子,你再回来看上面的内容,就能完全理解了!
import networkx as nx
import matplotlib.pyplot as plt
def main():
G = nx.Graph()
# topology construction logic
G.add_node('s1', desc='I am switch 1')
G.add_node('s2', desc='I am switch 2')
G.add_node('s3', desc='I am switch 3')
G.add_edge('s1','s2', name='edge 0')
G.add_edge('s1','s3', name='edge 1')
G.add_edge('s2','s3', name='edge 2')
# draw graph with labels
pos = nx.spring_layout(G)
nx.draw(G, pos)
node_labels = nx.get_node_attributes(G, 'desc')
nx.draw_networkx_labels(G, pos, labels=node_labels)
edge_labels = nx.get_edge_attributes(G, 'name')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.show()
if __name__ == '__main__':
main()
在这个例子中,我们构建了一个三角形拓扑,每个点有属性desc,用于存储这个点的描述信息,同时每条边有个属性name。我们想在图中将desc属性和name属性展示出来,由于每个节点都有desc属性,所以我们直接调用nx.get_node_attributes(G, ‘desc’)来获得的node_labels。edge_labels的获取同理。最后的运行结果如下:
在例子1中,我们通过get_node_attributes和get_edge_attributes很方便的得到了node_labels和edge_labels,但是这种方法有一个缺陷,那就是只能获取并展示一个属性。而节点或边有多个属性是很正常的情况,所以在这个例子中,我们将会展示如何将多个属性同时在图中显示。
在例子1的基础上,我们为每个节点增加一个新的属性attr1,同时为每个边增加一个新的属性weight,并在图中进行展示,代码如下:
import networkx as nx
import matplotlib.pyplot as plt
def main():
G = nx.Graph()
# topology construction logic
G.add_node('s1', desc='I am switch 1', attr1='new attr1')
G.add_node('s2', desc='I am switch 2', attr1='new attr1')
G.add_node('s3', desc='I am switch 3', attr1='new attr1')
G.add_edge('s1','s2', name='edge 0', weight=50)
G.add_edge('s1','s3', name='edge 1', weight=100)
G.add_edge('s2','s3', name='edge 2', weight=20)
# draw graph with labels
pos = nx.spring_layout(G)
nx.draw(G, pos)
# generate node_labels manually
node_labels = {}
for node in G.nodes:
node_labels[node] = G.nodes[node] # G.nodes[node] will return all attributes of node
nx.draw_networkx_labels(G, pos, labels=node_labels)
# generate edge_labels manually
edge_labels = {}
for edge in G.edges:
edge_labels[edge] = G[edge[0]][edge[1]] # G[edge[0]][edge[1]] will return all attributes of edge
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.show()
if __name__ == '__main__':
main()
这断代码的关键在于我们不再通过nx.get_node_attributes来获取node_labels,而是手工构建了node_labels。edge_labels同理。
注1:要在边上显示边的所有属性,也可以在调用draw_networkx_edge_labels时不传入edge_labels参数,即直接nx.draw_networkx_edge_labels(G, pos)就可以。但节点属性不行,必须要像代码中一样手动构造。
注2:由于布局的原因,节点的内容没有显示完整,在实际使用过程中可以根据具体的拓扑尝试不同的layout方式来达到理想的展示效果,这里我故意没有调整就是为了说明这一点。
当然,我们不仅可以显示属性,我们也可以显示任何我们想要显示的内容,只要正确的生成了node_labels与edge_labels即可。在这个例子中,我们依然使用三角形拓扑,只不过我们现在在节点上显示些不一样的内容:
import networkx as nx
import matplotlib.pyplot as plt
def main():
G = nx.Graph()
# topology construction logic
G.add_node('s1', desc='I am switch 1', attr1='new attr1')
G.add_node('s2', desc='I am switch 2', attr1='new attr1')
G.add_node('s3', desc='I am switch 3', attr1='new attr1')
G.add_edge('s1','s2', name='edge 0', weight=50)
G.add_edge('s1','s3', name='edge 1', weight=100)
G.add_edge('s2','s3', name='edge 2', weight=20)
# draw graph with labels
pos = nx.spring_layout(G)
nx.draw(G, pos)
# generate node_labels manually
node_labels = {}
for node in G.nodes:
node_labels[node] = 'Hello\n I am node: ' + str(node) # whatever you like
nx.draw_networkx_labels(G, pos, labels=node_labels)
nx.draw_networkx_edge_labels(G, pos) # no edge_labels parameter, default is showing all attributes of edges
plt.show()
if __name__ == '__main__':
main()
与例子2类似,我们手动构建了node_labels的内容,只不过加入了我们想要在node上显示的自定义内容。现在的运行结果如下所示:
这样,我们就在节点上显示了Hello信息!edge上的显示内容也可以自定义,方法与node_labels的构建类似,可以参照例子2的代码自行编写。
通过以上的讲解以及例子,我相信你应该已经基本掌握了在networkx图中展示节点和边的属性的方法,其实核心就是利用两个函数:
draw_networkx_labels(G, pos, labels=a dictionary used to describe what you want to show in node)
draw_networkx_edge_labels(G, pos, edge_labels=a dictionary used to describe what you want to show in edge)
并根据需求传入相应的node_labels与edge_labels即可。
当需要展示的属性只有一个时,node_labels与edge_labels的构建可以借助以下两个函数:
get_node_attributes(G, node_attributes_name)
get_edge_attributes(G, edge_attributes_name)
如果需要展示两个及以上的属性,或者是想展示一些自定义的内容,那就需要手动构建node_labels与edge_labels啦!构建方法在例子2与例子3中均有展示。