路与圈
前几个星期写论文去了,耽搁了更新。给自己一个小目标,每周坚持一更!
为了保证实用性和逻辑性,接下来的更新内容会参考这本书的内容来学习networkx的使用。
大卫·伊斯利, 乔恩·克莱因伯格. 网络、群体与市场[M]. 清华大学出版社, 2011.
这本书在华文慕课上也有北大老师讲的非常nice的配套学习视频:人群与网络
实例构图
众所周知,互联网是从美国的ARPA计算机网络发展来的,1970年12月时的arpanet长这个样子:
利用networkx画出我们需要的graph:
import networkx as nx #载入networkx包
import matplotlib.pyplot as plt #用于画图
G = nx.Graph() #无向图
edges = [('UCSB','SRI'),('UCSB','UCLA'),
('SRI','UCLA'),('SRI','STAN'),('SRI','UTAH'),
('UCLA','STAN'),('UCLA','RAND'),
('UTAH','SDC'),('UTAH','MIT'),
('RAND','SDC'),('RAND','BBN'),
('MIT','BBN'),('MIT','LINC'),
('BBN','HARV'),
('LINC','CASE'),
('HARV','CARN'),
('CASE','CARN')]
G.add_edges_from(edges)
接下来是画图:
labels={}
for node in G.nodes():
labels[node]=node
pos=nx.spring_layout(G) # 生成节点位置信息
plt.rcParams['figure.figsize']= (6, 4) # 设置画布大小
nx.draw_networkx_nodes(G,pos) # 画节点
nx.draw_networkx_edges(G,pos) # 画边
nx.draw_networkx_labels(G,pos,labels) # 画标签
plt.axis('off') # 去掉坐标刻度
# 保存并显示图片
# plt.savefig("ARPA.png")
plt.show()
于是,我们成功的将一个简单的网络抽象成图,为接下来的分析做好了准备。
路
在用户手册的目录中,我们找到算法(Algorithms)下的最短路(shortest_path)和简单路(simple_path)。
最短路径
这里奉上最短路径相关的函数,从用户手册中直接截图出来的:
寻找两点间的最短路径:
print(nx.shortest_path(G, 'UCSB', 'SDC'))
out:
['UCSB', 'SRI', 'UTAH', 'SDC']
当然两点之间可能不止一条最短路径,所以我们可以输出所有最短路径:
print(list(nx.all_shortest_paths(G, 'UCSB', 'SDC')))
out:
[['UCSB', 'SRI', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'RAND', 'SDC']]
最短路径的长度有四种用法,只输入图,输入图和源节点,输入图和目标节点和全都输入。
仅输入图会返回各个节点到其他节点的最短距离的字典:
print(list(nx.shortest_path_length(G)))
out:
[('UCSB', {'UCSB': 0, 'SRI': 1, 'UCLA': 1, 'STAN': 2, 'UTAH': 2, 'RAND': 2, 'SDC': 3, 'MIT': 3, 'BBN': 3, 'LINC': 4, 'HARV': 4, 'CASE': 5, 'CARN': 5}),
('SRI', {'SRI': 0, 'UCSB': 1, 'UCLA': 1, 'STAN': 1, 'UTAH': 1, 'RAND': 2, 'SDC': 2, 'MIT': 2, 'BBN': 3, 'LINC': 3, 'HARV': 4, 'CASE': 4, 'CARN': 5}),
('UCLA', {'UCLA': 0, 'UCSB': 1, 'SRI': 1, 'STAN': 1, 'RAND': 1, 'UTAH': 2, 'SDC': 2, 'BBN': 2, 'MIT': 3, 'HARV': 3, 'LINC': 4, 'CARN': 4, 'CASE': 5}),
('STAN', {'STAN': 0, 'SRI': 1, 'UCLA': 1, 'UCSB': 2, 'UTAH': 2, 'RAND': 2, 'SDC': 3, 'MIT': 3, 'BBN': 3, 'LINC': 4, 'HARV': 4, 'CASE': 5, 'CARN': 5}),
('UTAH', {'UTAH': 0, 'SRI': 1, 'SDC': 1, 'MIT': 1, 'UCSB': 2, 'UCLA': 2, 'STAN': 2, 'RAND': 2, 'BBN': 2, 'LINC': 2, 'HARV': 3, 'CASE': 3, 'CARN': 4}),
('RAND', {'RAND': 0, 'UCLA': 1, 'SDC': 1, 'BBN': 1, 'UCSB': 2, 'SRI': 2, 'STAN': 2, 'UTAH': 2, 'MIT': 2, 'HARV': 2, 'LINC': 3, 'CARN': 3, 'CASE': 4}),
('SDC', {'SDC': 0, 'UTAH': 1, 'RAND': 1, 'SRI': 2, 'MIT': 2, 'UCLA': 2, 'BBN': 2, 'UCSB': 3, 'STAN': 3, 'LINC': 3, 'HARV': 3, 'CASE': 4, 'CARN': 4}),
('MIT', {'MIT': 0, 'UTAH': 1, 'BBN': 1, 'LINC': 1, 'SRI': 2, 'SDC': 2, 'RAND': 2, 'HARV': 2, 'CASE': 2, 'UCSB': 3, 'UCLA': 3, 'STAN': 3, 'CARN': 3}),
('BBN', {'BBN': 0, 'RAND': 1, 'MIT': 1, 'HARV': 1, 'UCLA': 2, 'SDC': 2, 'UTAH': 2, 'LINC': 2, 'CARN': 2, 'UCSB': 3, 'SRI': 3, 'STAN': 3, 'CASE': 3}),
('LINC', {'LINC': 0, 'MIT': 1, 'CASE': 1, 'UTAH': 2, 'BBN': 2, 'CARN': 2, 'SRI': 3, 'SDC': 3, 'RAND': 3, 'HARV': 3, 'UCSB': 4, 'UCLA': 4, 'STAN': 4}),
('HARV', {'HARV': 0, 'BBN': 1, 'CARN': 1, 'RAND': 2, 'MIT': 2, 'CASE': 2, 'UCLA': 3, 'SDC': 3, 'UTAH': 3, 'LINC': 3, 'UCSB': 4, 'SRI': 4, 'STAN': 4}),
('CASE', {'CASE': 0, 'LINC': 1, 'CARN': 1, 'MIT': 2, 'HARV': 2, 'UTAH': 3, 'BBN': 3, 'SRI': 4, 'SDC': 4, 'RAND': 4, 'UCSB': 5, 'UCLA': 5, 'STAN': 5}),
('CARN', {'CARN': 0, 'HARV': 1, 'CASE': 1, 'BBN': 2, 'LINC': 2, 'RAND': 3, 'MIT': 3, 'UCLA': 4, 'SDC': 4, 'UTAH': 4, 'UCSB': 5, 'SRI': 5, 'STAN': 5})]
仅输入图和源节点会返回源节点到其他节点的最短距离字典:
print(nx.shortest_path_length(G, source = 'UCSB'))
out:
{'UCSB': 0, 'SRI': 1, 'UCLA': 1, 'STAN': 2, 'UTAH': 2, 'RAND': 2, 'SDC': 3, 'MIT': 3, 'BBN': 3, 'LINC': 4, 'HARV': 4, 'CASE': 5, 'CARN': 5}
仅输入图和目标节点会返回其他节点到目标节点的最短距离字典:
print(nx.shortest_path_length(G, target= 'UCSB'))
out:
{'UCSB': 0, 'SRI': 1, 'UCLA': 1, 'STAN': 2, 'UTAH': 2, 'RAND': 2, 'SDC': 3, 'MIT': 3, 'BBN': 3, 'LINC': 4, 'HARV': 4, 'CASE': 5, 'CARN': 5}
这里结果和上面一样是因为这里计算的是无向图。
全都输入,等价于len(shortest_path()):
print(nx.shortest_path_length(G, source='UCSB', target = 'SDC'))
out:
3
平均最短路径,事实上就是将上面的所有节点对之间的最短距离求平均数:
print(nx.average_shortest_path_length(G))
out:
2.5641025641025643
求两点间是否有路可达:
print(nx.has_path(G, 'UCSB', 'SDC'))
out:
True
简单路
所谓简单路,就是不走重复点的路。
networkx中简单路仅有两个函数,求图中两点间的所有简单路径和最短简单路径:
print(list(nx.all_simple_paths(G, 'UCSB', 'SDC')))
out:
[['UCSB', 'SRI', 'UCLA', 'RAND', 'SDC'],
['UCSB', 'SRI', 'UCLA', 'RAND', 'BBN', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'UCLA', 'RAND', 'BBN', 'HARV', 'CARN', 'CASE', 'LINC', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'STAN', 'UCLA', 'RAND', 'SDC'],
['UCSB', 'SRI', 'STAN', 'UCLA', 'RAND', 'BBN', 'MIT', 'UTAH', 'SDC'], ['UCSB', 'SRI', 'STAN', 'UCLA', 'RAND', 'BBN', 'HARV', 'CARN', 'CASE', 'LINC', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'UTAH', 'MIT', 'BBN', 'RAND', 'SDC'],
['UCSB', 'SRI', 'UTAH', 'MIT', 'LINC', 'CASE', 'CARN', 'HARV', 'BBN', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'SRI', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'SRI', 'UTAH', 'MIT', 'BBN', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'SRI', 'UTAH', 'MIT', 'LINC', 'CASE', 'CARN', 'HARV', 'BBN', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'STAN', 'SRI', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'STAN', 'SRI', 'UTAH', 'MIT', 'BBN', 'RAND', 'SDC'], ['UCSB', 'UCLA', 'STAN', 'SRI', 'UTAH', 'MIT', 'LINC', 'CASE', 'CARN', 'HARV', 'BBN', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'RAND', 'BBN', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'RAND', 'BBN', 'HARV', 'CARN', 'CASE', 'LINC', 'MIT', 'UTAH', 'SDC']]
最短简单路径的返回其实和所有简单路径的内容一样,但是得到的答案根据路径的长度,由短到长进行排序。
print(list(nx.shortest_simple_paths(G, 'UCSB', 'SDC')))
out:
[['UCSB', 'SRI', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'RAND', 'SDC'],
['UCSB', 'SRI', 'UCLA', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'SRI', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'STAN', 'UCLA', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'STAN', 'SRI', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'UTAH', 'MIT', 'BBN', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'RAND', 'BBN', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'UCLA', 'RAND', 'BBN', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'SRI', 'UTAH', 'MIT', 'BBN', 'RAND', 'SDC'],
['UCSB', 'SRI', 'STAN', 'UCLA', 'RAND', 'BBN', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'STAN', 'SRI', 'UTAH', 'MIT', 'BBN', 'RAND', 'SDC'],
['UCSB', 'SRI', 'UTAH', 'MIT', 'LINC', 'CASE', 'CARN', 'HARV', 'BBN', 'RAND', 'SDC'],
['UCSB', 'UCLA', 'RAND', 'BBN', 'HARV', 'CARN', 'CASE', 'LINC', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'SRI', 'UCLA', 'RAND', 'BBN', 'HARV', 'CARN', 'CASE', 'LINC', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'SRI', 'UTAH', 'MIT', 'LINC', 'CASE', 'CARN', 'HARV', 'BBN', 'RAND', 'SDC'],
['UCSB', 'SRI', 'STAN', 'UCLA', 'RAND', 'BBN', 'HARV', 'CARN', 'CASE', 'LINC', 'MIT', 'UTAH', 'SDC'],
['UCSB', 'UCLA', 'STAN', 'SRI', 'UTAH', 'MIT', 'LINC', 'CASE', 'CARN', 'HARV', 'BBN', 'RAND', 'SDC']]
圈(环)
圈的简单定义为,长度大于三且起点与终点相同的路。
networkx中与圈相关的函数:
首先是求图中所有圈的基:
先看networkx中关于圈的基给出的解释:A basis for cycles of a network is a minimal collection of cycles such that any cycle in the network can be written as a sum of cycles in the basis. Here summation of cycles is defined as “exclusive or” of the edges. Cycle bases are useful, e.g. when deriving equations for electric circuits using Kirchhoff’s Laws.
网络圈的基是圈的最小集合,这样网络中的任何圈都可以写成基中圈的和。这里圈的总和被定义为边的“异或”。圈基是有用的,例如,当使用基尔霍夫定律推导电路方程时。
nx.cycle_basis(G)
out:
[['BBN', 'HARV', 'CARN', 'CASE', 'LINC', 'MIT'],
['UTAH', 'SDC', 'RAND', 'BBN', 'MIT'],
['SRI', 'STAN', 'UCLA'],
['UCSB', 'SRI', 'UCLA'],
['UTAH', 'SRI', 'UCLA', 'RAND', 'BBN', 'MIT']]
第二个函数是针对有向图的,这里就不做展示了。
find_cycle()函数根据给定的源节点返回按照深度优先遍历找到的第一个圈,如果是有向图还能通过选择orientation参数来控制深度遍历的方向。
nx.find_cycle(G,'UCSB')
out:
[('UCSB', 'SRI'), ('SRI', 'UCLA'), ('UCLA', 'UCSB')]
完整代码资源
参考
networkx官网地址:https://networkx.org/