NetworkX:Python图与网络模型基础

Posted 金融系程老师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NetworkX:Python图与网络模型基础相关的知识,希望对你有一定的参考价值。

在《运筹学》课堂上,我们学习过图与网络,当时用到R语言下的igraph包来计算和展示结果。Python下也有类似甚至更好的库: NetworkX

安装命令如下

 
   
   
 
  1. conda install networkx

引入约定为

 
   
   
 
  1. import networkx as nx

1 图的绘制

无向图

无向图由点和边构成,其绘制思路为:①新建空图→②添加点→③添加边。

  • 新建空的无向图

 
   
   
 
  1. G = nx.Graph()

以后所有的信息都添加在无向图G上。

  • 添加点:addnode和add_nodes_from

 
   
   
 
  1. # 添加一个点

  2. G.add_node(1) # 点的名字叫1

  3. G.add_node('a') # 点的名字叫a

  4. # 添加一组点

  5. G.add_nodes_from([2,3,4])

虽然还没有讲到怎么展示这张图,但你可能想看看自己已经画了啥;所以我们剧透一下:输入nx.draw(G)看看吧。

  • 添加边:add_edge和add_edges_from

 
   
   
 
  1. # 添加一条边

  2. G.add_edge(1,2) # 在1、2之间添加边

  3. G.add_edge(1,'a') # 在1、a之间添加边

  4. # 添加一组边

  5. G.add_edges_from([(2,3),(3,4,),('a',3)])

  6. # 添加边时自动生成点

  7. G.add_edge(3,'b') # 此前没有添加过b点

  8. # 添加圈

  9. G.add_cycle(['b','c','d'])

  10. # 注意这些命令都有color参数,将来会用到

移除点或边使用remove_*系列方法。

展示图

NetworkX可以结合matpltlib库来展示图,因此需要载入plt:

 
   
   
 
  1. import matplotlib.pyplot as plt

最常用的展示命令是 networkx.draw(),所有参数都是可选的。

 
   
   
 
  1. nx.draw()

简单介绍一些可选参数,如

  • ax:画纸名

  • nodecolor/edgecolor/font_color:点、边、字颜色

  • nodeshape/nodesize:点的形状和大小

  • style:边的形状(solid/dashed/dotted/dashdot)

  • alpha:点和边的透明度

  • with_labels:点是否显示标签

  • arrows/arrowstyle/arrowsize:有向图的箭头设定

我们并列展示默认和自定义结果:

 
   
   
 
  1. fig = plt.figure(figsize=(20,5))

  2. ax1 = plt.subplot(121)

  3. nx.draw(G,ax=ax1)

  4. ax2 = plt.subplot(122)

  5. nx.draw(G,ax=ax2,node_color='pink',with_labels=True,node_size=500,node_shape='D',style='-.')

  6. plt.draw()


另有 networkx.draw_networkx()函数,支持自定义点的位置(类型)。

有向图

有向图和无向图的差别仅仅在边是有方向的:

  • 新建空的有向图

 
   
   
 
  1. G = nx.DiGraph() # 注意差别

  • 添加点:略

  • 添加有向边

 
   
   
 
  1. G.add_edge(1,2)

  2. G.add_edges_from([(2,3),(2,4),(3,4),(4,3)])

  3. nx.draw(G,ax=ax2,node_color='pink',with_labels=True,arrowstyle='fancy',edge_color='pink')

NetworkX:Python图与网络模型基础

2 从图到网络:权的添加

方法一

add_weighted_edges_from方法能够接受(起点,终点,权重)作为元素的序列。推荐这种方法。

 
   
   
 
  1. G = nx.DiGraph()

  2. elist = [('a', 'b', 5.0), ('b', 'c', 3.0), ('a', 'c', 1.0), ('c', 'd', 7.3)]

  3. G.add_weighted_edges_from(elist)

方法二

add_edge方法可以添加weight参数。

 
   
   
 
  1. G.add_edge(1,2,weight=5.5)

方法三

类索引方法,在修改权重时非常有用。

 
   
   
 
  1. G[1][2]['weight']=5.5

  2. G.edges[1,2]['weight']=5.5

添加权重标签

按照上述三个方法添加的边权重,将被记录在边属性下,我们可以通过G.edges(data=True)方法来查看:

 
   
   
 
  1. G.edges(data=True)

特别注意参数data一定要为True,不指定data参数时默认只提取边的起点和终点。结果如下:

 
   
   
 
  1. OutEdgeDataView([('a', 'b', {'weight': 5.0}), ('a', 'c', {'weight': 1.0}), ('b', 'c', {'weight': 3.0}), ('c', 'd', {'weight': 7.3})])

关系已经很明确了,我们用元组u,v,w来解包,并将其放在键为边,值为权的字典中:

 
   
   
 
  1. # 方法一:for循环

  2. label = {}

  3. for (u,v,d) in G.edges(data=True):

  4.    label[(u,v)] = str(d['weight'])

  5. # 方法二:字典推导式

  6. label = {(u,v):str(d['weight']) for u,v,d in G.edges(data=True)}

绘制权重标签

我们分4步来画图:点→边→点标签→边标签(顺序不重要)。因为要分4个图层来画,所以需要明确点的位置,不能用nx.draw()这种随性的方法。

首先定义点的位置pos:

 
   
   
 
  1. pos = nx.spring_layout(G)

这表明我们使用Fruchterman-Reingold的力引导算法来画图,目的是减少边的交叉(推荐)。可选的pos还有circular_layout、kamada_kawai_layout、random_layout、rescale_layout、shell_layout和spectral_layout,点少的时候看不出来,点多就不一样了。

 
   
   
 
  1. nx.draw_networkx_nodes(G,pos)

  2. nx.draw_networkx_labels(G,pos)

  3. nx.draw_networkx_edges(G,pos)

  4. nx.draw_networkx_edge_labels(G, pos, label) # 关注这里

  5. plt.axis('off') # 不显示坐标

  6. plt.draw()

默认的标签是放在边的中间,可以用label_pos调节,这对双箭头的有向边很重要。

NetworkX:Python图与网络模型基础

3 最短路

《运筹学》课程中,我们学习了Dijkstra算法;NetworkX提供了相应的命令:

  • dijkstra_predecessor_and_distance:给出某起点到所有点的最短路径和最短路程,结果也包含两部分,可以用元组解包提取;

  • dijkstra_path:给出从某起点到某终点的最短路径;

  • dijkstra_path_length:给出从某起点到某终点的最短路程。

构造一个有向图

 
   
   
 
  1. G = nx.DiGraph()

  2. elist = [(1,2,3),(1,3,2),(1,4,5),

  3.         (2,6,7),

  4.         (3,4,1),

  5.         (4,6,5),

  6.         (5,3,5),(5,4,3),(5,6,1)]

  7. G.add_weighted_edges_from(elist)

求点1到点6的最短路。

展示这个有向图

 
   
   
 
  1. pos = nx.circular_layout(G)

  2. label = {(u,v):str(d['weight']) for u,v,d in G.edges(data=True)}

  3. nx.draw_networkx_nodes(G,pos,node_color='w',edgecolors='k')

  4. nx.draw_networkx_nodes(G,pos,node_color='pink',edgecolors='k',nodelist=[1,6])

  5. nx.draw_networkx_labels(G,pos,font_color='k')

  6. nx.draw_networkx_edges(G,pos)

  7. nx.draw_networkx_edge_labels(G, pos,label,label_pos=0.3)

  8. plt.axis('off')

  9. plt.draw()

NetworkX:Python图与网络模型基础

Dijkstra算法求最短路

  • 指定起点,不指定终点

 
   
   
 
  1. pred, dist = nx.dijkstra_predecessor_and_distance(G,1)

  2. print('到每个点的最短路的上一个点:',pred)

  3. print('到每个点的最短路的路程:',dist)

结果:

 
   
   
 
  1. 到每个点的最短路的上一个点: {1: [], 2: [1], 3: [1], 4: [3], 6: [4]}

  2. 到每个点的最短路的路程: {1: 0, 3: 2, 2: 3, 4: 3, 6: 8}

  • 指定起点和终点

 
   
   
 
  1. path = nx.dijkstra_path(G,1,6)

  2. length = nx.dijkstra_path_length(G,1,6)

  3. print('从点1到点6的最短路径是',path)

  4. print('从点1到点6的最短路程是',length)

将最短路径画出来:

附代码:

  
    
    
  
  1. pos = nx.circular_layout(G)

  2. label = {(u,v):str(d['weight']) for u,v,d in G.edges(data=True)}

  3. nx.draw_networkx_nodes(G,pos,node_color='w',edgecolors='k')

  4. nx.draw_networkx_nodes(G,pos,node_color='pink',edgecolors='k',nodelist=[1,6])

  5. nx.draw_networkx_labels(G,pos,font_color='k')

  6. nx.draw_networkx_edges(G,pos)

  7. el = [(path[i],path[i+1]) for i in range(len(path)-1)]

  8. nx.draw_networkx_edges(G,pos,edge_color='r',edgelist=el)

  9. nx.draw_networkx_edge_labels(G, pos,label,label_pos=0.3)

  10. plt.axis('off')

  11. plt.draw()

4 最大流

掌握了最短路再来看最大流,就是很简单的事情了。

  • 通过capacity参数为边添加最大容量;

  • 使用 maximum_flow函数求解。

构造包含最大容量的有向图

 
   
   
 
  1. elist = [(1,2,6),(1,4,6),(2,3,2),(2,5,3),(3,5,2),(3,6,2),(4,3,3),

         (4,6,1),(4,7,2),(5,7,5),(6,7,4)]

  1. G = nx.DiGraph()

  2. for u,v,c in elist:

  3.    G.add_edge(u,v,capacity=c)

绘制这个图

 
   
   
 
  1. pos = nx.circular_layout(G)

  2. label = {(u,v):str(d['capacity']) for u,v,d in G.edges(data=True)}

  3. # 或者label = {(u,v):str(c) for u,v,c in elist}

  4. nx.draw_networkx_nodes(G,pos,node_color='w',edgecolors='k')

  5. nx.draw_networkx_nodes(G,pos,node_color='pink',edgecolors='k',nodelist=[1,7])

  6. nx.draw_networkx_labels(G,pos,font_color='k')

  7. nx.draw_networkx_edges(G,pos)

  8. nx.draw_networkx_edge_labels(G, pos,label,label_pos=0.5)

  9. plt.axis('off')

  10. plt.draw()


图中边的标签表示最大容量(而非单位运价)。我们想求得从点1到点7的最大流。

计算最大流

 
   
   
 
  1. flow_value, flow_dict = nx.maximum_flow(G, 1, 7)

  • 通过提取flow_value,我们可以知道从点1到点7的最大流为10;

  • 通过提取flow_dict,我们可以知道最大流情形下,每条边的实际流量:

 
   
   
 
  1. {1: {2: 5, 4: 5},

  2. 2: {3: 2, 5: 3},

  3. 3: {5: 2, 6: 2},

  4. 4: {3: 2, 6: 1, 7: 2},

  5. 5: {7: 5},

  6. 6: {7: 3},

  7. 7: {}}

我们也可以通过 flow_dict[<起点>][<终点>]来确定特定边上的实际容量,如 flow_dict[1][4]表示边(1,4)上的实际流量为5。

5 结语

NetworkX是复杂网络计算库,能做的事情远不止最短路和最大流。手册在这里,进步靠自己:

 
   
   
 
  1. https://networkx.github.io/documentation/stable/_downloads/networkx_reference.pdf


以上是关于NetworkX:Python图与网络模型基础的主要内容,如果未能解决你的问题,请参考以下文章

Python3画图系列——NetworkX初探

NetworkX是一个图论与复杂网络建模工具

python教程入门学习利用Python绘制关系网络图

数学建模比赛准备---图与网络模型

python学习(32)---networkx

数学建模培训|图与网络模型简介