Matplotlib的实际应用(networkx与matplotlib)
纲要:本节内容虽然涉及到networkx,Matplotlib;但我们重点是如何用Matplotlib绘复杂图,networkx仅仅是作为辅助,甚至你可以完全不懂它
一、最短距离用坐标分布图表示出来
先来补充些基础知识:
1.if-else的简写
1.通常写法
b=4
if b>4:
print('对了')
else:
print('错了')
2.简写
'对了' if b>4 else '错了'
2.for in循环的简写
q=[(a,b) for a in range(10) for b in range(10) if a>0 and a%2==0 and b%2==0]
这条语句的阅读顺序是 for a in range(10) --> for b in range(10) -->if a>0 and a%2==0 and b%2==0 -->(a,b)
也就是说for in 和if else组合在一起时就是从前往后读
q=[(a,b) for a in range(10) for b in range(10) if a>0 and a%2==0 and b%2==0]
print(q)
# [(2, 0), (2, 2), (2, 4), (2, 6), (2, 8), (4, 0), (4, 2), (4, 4), (4, 6), (4, 8), (6, 0), (6, 2), (6, 4), (6, 6), (6, 8), (8, 0), (8, 2), (8, 4), (8, 6), (8, 8)]
q=[(a+b) for a in range(10) for b in range(10) if a>0 and a%2==0 and b%2==0]
print(q)
# [2, 4, 6, 8, 10, 4, 6, 8, 10, 12, 6, 8, 10, 12, 14, 8, 10, 12, 14, 16]
3.reshape(-1)
import numpy as np
c=np.array([[1,2,3],[4,5,6]])
print('两行三列')
print(c.reshape(2,3))
[[1 2 3]
[4 5 6]]
print('三行两列')
print(c.reshape(3,2))
[[1 2]
[3 4]
[5 6]]
print('我也不知道是几行,反正就是1列')
print(c.reshape(-1,1))
[[1]
[2]
[3]
[4]
[5]
[6]]
print('我也不知道是几列,反正就是1行')
print(c.reshape(1,-1))
[[1 2 3 4 5 6]]
print('不分行列,就是一串数字')
print(c.reshape(-1))
[1 2 3 4 5 6]
z = np.array([[1, 2, 3, 4], [5, 6, 7,8], [9, 10, 11, 12], [13, 14, 15, 16]])
print(z.reshape(-1))
输出:[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
4.关于np.array()
import networkx as nx
import numpy as np
G=nx.graph_atlas(100)
aa=[[nx.shortest_path_length(G,i,j) for j in G if i != j]for i in G]
bb=np.array([[nx.shortest_path_length(G,i,j) for j in G if i != j]for i in G])
cc=np.array([[nx.shortest_path_length(G,i,j) for j in G if i != j]for i in G]).reshape(-1)
dd=[nx.shortest_path_length(G,i,j) for i in G for j in G if i != j]
print(aa)
print(bb)
print(cc)
print(dd)
cc和dd虽然有逗号上的区别,但事实上在用法上这两个没有丝毫的差别(日后求纠正)
5.绘制网络任意两点之间最短距离的分布图
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
G=nx.graph_atlas(100) #得到一幅网络图
dd=np.array([[nx.shortest_path_length(G,i,j) for j in G if i != j]for i in G]).reshape(-1) #求得任意两点之间的最短距离
bins=np.arange(-0.5,max(dd)+1.5,1.0) #根据最短距离里的最大值确定横坐标的取值范围
plt.hist(dd,bins=bins)
'''
bin=np.arange(0,60,2)得到[0,2,4,6,8...52,54,56,58]不包括60
所以bins=np.arange(-0.5,max(dd)+1.5,1.0)中是max(dd)+1.5而不是max(dd)+0.5
bins=np.arange(-0.5,max(dd)+1.5,1.0)得到[-0.5,0.5,1.5,2.5,3.5]
'''
6.关于plt.hist里的bins
bins:整数值或序列。如果bins为整数值,则bins为柱子个数,根据数据的取值范围和柱子个数bins计算每个柱子的范围值,柱宽=(x.max()-x.min())/bins。例:数据取值为[1,2,3,4,5,6],bins=6,柱宽=0.8333…,则每个柱子的范围分别为:[1,1.83),
[1.83,2.66)…,[5.17,6]
如果bins取值为序列,则该序列给出每个柱子的范围值(即边缘)。除最后一个柱子外,其他柱子的取值范围均为半开(左闭右开)。如:在一个数据共n=6个取值的情况下,若bins=range(1,n+2)即bins的取值为[1,2,3,4,5,6,7]。则每个柱子的范围为:[1,2),[2,3),…,[5,6),[6,7]。
8.回忆一下散点图的绘制
9.以下才是真正的核心内容
二、集聚系数坐标分布图
1.运用到了字典和列表的转换的知识,请看“字典与列表”专栏
2.hist也好scatter也罢,肚子里的东西都是列表: hist([列表],bins=5) , scatter([列表1],[列表2])
反正hist和scatter肚子里的列表更多的是喜欢[degree[i] for i in list(degree.keys())]
这种复杂的形式出现,而不是简简单单的[1,2,3,4,5,6]
import matplotlib.pyplot as plt
cluster={0:0.15,1:0.33,2:0.24,3:0.66,4:0.66,5:0.5,6:0.5,7:1.0,8:0.5} #8个节点的集聚系数
degree={0:2,1:1,2:4,3:5,4:2,5:1,6:4,7:2,8:3} #8个节点的度
plt.subplot(121)
plt.hist(list(cluster.values()),bins=5)
plt.subplot(122)
plt.scatter([degree[i] for i in list(degree.keys())],[cluster[i] for i in list(cluster.keys())]) #度与集聚系数的关系
'''你千万要熟练这种[degree[i] for i in list(degree.keys())]用法'''
三、ER随即网络坐标分布图
1.简单的绘制ER度分布图
import networkx as nx
G = nx.graph_atlas(100)
print(G.degree()) #[(0, 3), (1, 1), (2, 1), (3, 3), (4, 2), (5, 2)]
print(dict(G.degree())) #{0: 3, 1: 1, 2: 1, 3: 3, 4: 2, 5: 2}
我们要尝试输出G.degree()看看他是什么类型,然后再根据字典或者列表的特点互相转换
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
##产生ER随即网络图
g=nx.erdos_renyi_graph(1000, 0.006,directed=False)
degree=dict(nx.degree(g)) #度分布{0: 10, 1: 5, 2: 2, 3: 8,,,,997: 3, 998: 7, 999: 3}
degree=list(degree.values()) #[10,5,2,8,,,,3,7,3]
average_degree=np.mean(degree) #6.144
max_degree=max(degree) #16
print(max_degree)
bins=np.arange(-0.5,max(degree)+1.5,1.0)
plt.hist(degree,bins=bins,normed=1,facecolor='green',alpha=0.5)
plt.xlabel('degree')
plt.title('degree distribution')
2.绘制泊松分布
hist([列表1],[列表2]),scatter([列表1],[列表2])里面的列表可能是range(15)
也可能是[degree[i] for i in list(degree.keys())]
,你必须熟悉这些用法
import scipy.stats
import matplotlib.pyplot as plt
k=0.5
print([scipy.stats.poisson.pmf(xi,k) for xi in range(15)])#泊松分布
plt.plot(range(15),[scipy.stats.poisson.pmf(xi,k) for xi in range(15)],'ro-')
3.以下才是真正的核心内容
import scipy.stats
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
N=1000
krange=[0.5,1.0,2.0,np.log(N)]
#设置4个平均度<k>来绘制ER图;在ER网络图中给出了平均度即可求p,p=<k>/(N-1)
#np.log(N)=6.90
for i in range(4):
k=krange[i]
p=1.0*k/(N-1) #求得ER图的p
deg=[]
for j in range(100):
g=nx.erdos_renyi_graph(N,p,directed=False)
deg+=dict(nx.degree(g)).values()
#对于每个p我们都绘制100幅ER图,并把他们的度存储在deg里面
plt.subplot(2,2,i+1)
bins=np.arange(-0.5,max(deg)+0.5,1.0)
plt.hist(deg,bins=bins,normed=1,facecolor='green',alpha=0.5)
plt.plot(range(15),[scipy.stats.poisson.pmf(xi,k) for xi in range(15)],'ro-')
plt.xlim(-1,15)
plt.title('<k>=%s'%k)
四、ER网络的连通集团分布图
1.先补充些基础知识
import numpy as np
krange=np.arange(0.2,2*np.log(100),0.2)
print(krange)
print(np.log(100))#默认以e为底数
print(np.log10(100))
2.以下才是真正的核心内容
五、小世界网络及其特征统计量
(下面的计算用到了这些代码)
我们通过取不同的p来得到不同的p对应的网络的集聚系数和最短距离
我们发现在圈主的地方具有“高集聚系数,低平均距离”的小世界特性
根据上面得到的prange,drange,crange就可以绘图了
plt.plot(prange,drange) #可以看最短距离随着p的变化情况
plt.plot(prange,crange) #可以看平均集聚系数随着p的变化
ratio=[crange[i]/drange[i] for i in range(len(crange))] #ratio是集聚系数和距离的比值
plt.plot(prange,ratio) #就可以看c/d随着p的变化情况
还需要对横纵坐标处理一下就可以得到下图:
完整代码如下:(这个太麻烦,仅供了解了)
六、.如何统计100幅图片的平均值
①如何统计100幅随即网络的平均度、平均集聚系数、平均最短距离、平均最大联通集团
N=100
pro=[0.2,0.4,0.6,0.8,1.0,1.2,1.4,1.6]
Avg_deg=[]
Avg_cls=[]
Avg_spl=[]
Avg_gcc=[]
for p in pro:
avg_deg=[]
avg_cls=[]
avg_spl=[]
avg_gcc=[]
for _ in range(100):
g=nx.erdos_renyi_graph(N,p,directed=False) #根据每一个p与N绘制一个随即网络图
avg_deg.append(np.mean(nx.degree(g).values()))
avg_cls.append(nx.average_clustering(g))
avg_gcc.append( max([len(x) for x in nx.connected_components(g)] )
avg_spl.append(nx.average_shorteest_path_length(g))
Avg_deg.append(np.mean(avg_deg))
Avg_cls.append(np.mean(avg_cls))
Avg_spl.append(np.mean(avg_spl))
Avg_gcc.append(np.mean(avg_gcc))
'''avg_deg里面存放着100个随即网络图的平均度
avg_cls里面存放着100个随即网络图的平均集聚系数
avg_gcc里面存放着100个随即网络图的最大连通集团
avg_spl里面存放着100个随即网络图的平均最短距离
'''
②
③如何统计100次实验的平均数据
已知我们有一个函数,suspect_infect_recovery(g, sources, beta, gamma),g是一个网络,sources是一个列表涵盖了这个网络最先得了传染病的那个节点,beta=0.3是感染概率,gamma=1是恢复概率;这个函数可以返回一个state,state里面记录了本次传染病盛行后各个节点得状态是“I”还是“R”还是“S”(I是感染,R是感染后恢复,S是未感染)
g=nx.karate_club_graph() #空手道俱乐部
res=[[] for i in g] #res事实上就是[[], [], [], [], []...]
for _ in range(100): #让g中的每一个节点都循环100次得到较为客观的值
for i in g:
state=suspect_infect_recovery(g, [i], 0.3, 1)#让g中的每一个节点都做一次sources(第一个被感染的人)
s=sum([1 for i in state if state[i] !="S"])#统计共有多少人受到了感染
res[i].append(s) #res[i]就是列表中的列表,它当然可以调用列表的方法
#此时的res就是[[24, 16, 5, 24, 24..],[21, 8, 25, 15, 7..]...]
impact=[sum(r)/len(r) for r in res] #让g中的每一个节点都循环100次再做平均得到一个客观的值
#impact就是[18.02, 15.69, 17.07, 14.68...]的样子
Impact_sort=sorted([(i,impact[i]) for i in range(len(impact))],key=lambda x:x[1],reverse=True)
for i in Impact_sort:
print(i)
七、无标度网络的相关分析
1.set()函数
2.bipartite.projected_graph(B, nodes=)
g=bipartite.projected_graph(B, nodes=)。以1-N 3-N为例,1和3有共同的邻居,经过二分图的投影1-3相连。g=bipartite.projected_graph(B, nodes=)也就是提取B和nodes里面有共同邻居的那些节点组成连边
from networkx import nx
from networkx.algorithms import bipartite
M=nx.Graph()
M.add_edges_from([(0,'A'),(1,'N'),(3,'N'),(4,'P'),(5,'B')])
#1-N 3-N所以1和3有共同的邻居,经过二分图的投影1-3相连
G = bipartite.projected_graph(M, [0,1,3,4,5])
print(list(G))
print(list(G.edges()))
3.plt.loglog是双对数坐标,不做强制了解
import matplotlib.pyplot as plt
x = [2**i for i in range(4,14)]
y = [i**2 for i in x]
plt.loglog(x,y,'ro',basex=2,basey=2)
plt.show()
4.抛砖引玉
from networkx import nx
from networkx.algorithms import bipartite
import matplotlib.pyplot as plt
M=nx.Graph()
M.add_edges_from([(0,'A'),(1,'N'),(3,'N'),(4,'P'),(5,'B'),(6,'C'),(7,"N"),(8,'B')])
#以1-N 3-N为例,1和3有共同的邻居,经过二分图的投影1-3相连
G = bipartite.projected_graph(M, [0,1,3,4,5,6,7,8])
deg_dist=nx.degree_histogram(G)
plt.loglog(range(0,len(deg_dist)),deg_dist)
#事实上,对于plt.loglog是不需要任何参数的,plt.loglog(x,y,'o')就可以了,默认以10为底
print(list(G))
print(list(G.edges()))
print(nx.degree(G))#统计出每个节点的度
print(deg_dist)#[3,2,3]知度为0的有三个,度为1的有2个,度为2的有三个
5.以下才是真正的核心内容
对下面代码的注解:
mec设置标记边缘颜色
八、社团划分
①本节内容
from networkx import nx
import random
N=128#网络规模
C=4#社团数量
zin=14#社团内连边数
zout=2#社团外连边数
n=int(N/C)#每个社团的节点数为32
nodes=[]
nall=[]
for a in ['a','b','c','d']:
xx=[]
for i in range(n):
xx.append(a+str(i))
nodes+=xx
nall.append(xx)
#nodes为['a0','a1'...'b0','b1'...'c0','c1'...'d0','d1'...]
#nall为[['a0','a1'...],['b0','b1'...],['c0','c1'...],['d0','d1'...]]
pin=1.0*zin/(n-1)/2 #以pin的概率产生社团内连边
pout=1.0*zout/(3*n-1)/2 #以pout的概率产生社团外连边
g=nx.Graph()
for nc in nall:
for i in nc:
for j in nc:#社团内连边
if i==j:
continue
p=random.random()
if p<pin:
g.add_edge(i,j)
for j in set(nodes)-set(nc):#社团外连边
p=random.random()
if p<pout:
g.add_edge(i,j)
pos=nx.layout.spring_layout(g)
nx.draw(g,pos,node_size=200)
你只需要修改最后两条代码为:
state={i:'0' for i in g} #我们先给网络g里面的节点设置一个state属性,并且这些节点的state属性都是一个初始值'0'
for nc in nall:
for i in nc:
state[i]=i[0] #a社团的的state就是'a';b社团的state就是'b'
colors={'a':'blue','b':'yellow','c':'red','d':'green'}
color=[colors[state[u]] for u in g] #不同的属性赋予不同的颜色
pos=nx.layout.spring_layout(g)
nx.draw(g,pos=pos,node_color=color,node_size=200,alpha=0.9,edgecolors='k')
就可以绘出四种社团颜色的代码:
②本节内容摘抄的思想
上幅图给了a、b、c、d四个社团,每个社团有32个节点,请你给我统计出两个列表,
一个是[‘a0’,‘a1’…‘b0’,‘b1’…‘c0’,‘c1’…‘d0’,‘d1’…],另一个是[[‘a0’,‘a1’…],[‘b0’,‘b1’…],[‘c0’,‘c1’…],[‘d0’,‘d1’…]]
nodes=[]
nall=[]
for a in ['a','b','c','d']:
xx=[]
for i in range(32):
xx.append(a+str(i))
nodes+=xx
nall.append(xx)
#nodes为['a0','a1'...'b0','b1'...'c0','c1'...'d0','d1'...]
#nall为[['a0','a1'...],['b0','b1'...],['c0','c1'...],['d0','d1'...]]
如何根据不同的社团绘制不同的颜色?
state={i:'0' for i in g} #我们先给网络g里面的节点设置一个state属性,并且一开始所有节点的state属性都是一个初始值'0'
for nc in nall:
for i in nc:
state[i]=i[0] #a社团的的state就是'a';b社团的state就是'b'
colors={'a':'blue','b':'yellow','c':'red','d':'green'}
color=[colors[state[u]] for u in g] #不同的属性赋予不同的颜色
pos=nx.layout.spring_layout(g)
nx.draw(g,pos=pos,node_color=color,node_size=200,alpha=0.9,edgecolors='k')
就可以绘出四种社团颜色的代码:
九、疾病传播动力学
import random
from networkx import nx
def suspect_infect_recovery(G,sources,beta,gamma):
state={i:'S' for i in G}#G中每个节点都设置为未感染
for s in sources:
state[s]='I' #sources里面的设置为感染节点
flag=False #flag何时为True呢?当没有感染结点的时候
while True: #(while True:就是永远循环下去)
flag=True
state_temp=state.copy() #复制字典state给state_temp
for i in G:
if state[i]=="S":
nbs=sum([1 for j in G.neighbors(i) if state[j] =="I"]) #当i是未感染时,那就统计他有多少个感染的邻居
if nbs==0:
continue
if random.random()<(1-(1-beta)**nbs):#(1-beta)的nbs次方就是i节点在有nbs个邻居感染的情况下仍然未感染概率
state_temp[i]="I"
elif state[i]=="I":
flag=False #flag如果为False就是还存在感染节点的意思,此时仍不能跳出循环
if random.random()<gamma:
state_temp[i]="R"
#也就是说每循环一遍就有未感染的因为邻居而有可能受到感染,也有感染的变成未感染的
state=state_temp.copy()
if flag:
break #也就是当flag为True时就是跳出循环了
return state
beta=0.3#感染概率
gamma=1#恢复概率
g=nx.karate_club_graph() #空手道俱乐部
state=suspect_infect_recovery(g,[1],beta,gamma)
print("各个节点的状态:",state)
print("总感染数:",sum([1 for i in state if state[i] !='S']))
pos=nx.layout.spring_layout(g)
colors={"R":'b',"I":'r',"S":'g'}
color=[colors[state[u]] for u in g]#根据节点的status给它相应的颜色
nx.draw(g,pos=pos,node_color=color,with_labels=True,node_size=300)
(代码解说在这幅图下面)
import random
from networkx import nx
def suspect_infect_recovery(G,sources,beta,gamma):
state={i:'S' for i in G}#G中每个节点都设置为未感染
for s in sources:
state[s]='I' #sources里面的设置为感染节点
flag=False #flag何时为True呢?当没有感染结点的时候
while True: #(while True:就是永远循环下去)
flag=True
state_temp=state.copy() #复制字典state给state_temp
for i in G:
if state[i]=="S":
nbs=sum([1 for j in G.neighbors(i) if state[j] =="I"]) #当i是未感染时,那就统计他有多少个感染的邻居
if nbs==0:
continue
if random.random()<(1-(1-beta)**nbs):#(1-beta)的nbs次方就是i节点在有nbs个邻居感染的情况下仍然未感染概率
state_temp[i]="I"
elif state[i]=="I":
flag=False #flag如果为False就是还存在感染节点的意思,此时仍不能跳出循环
if random.random()<gamma:
state_temp[i]="R"
#也就是说每循环一遍就有未感染的因为邻居而有可能受到感染,也有感染的变成未感染的
state=state_temp.copy()
if flag:
break #也就是当flag为True时就是跳出循环了
return state
beta=0.3#感染概率
gamma=1#恢复概率
g=nx.karate_club_graph() #空手道俱乐部
pos=nx.layout.spring_layout(g)
res=[[] for i in g] #res事实上就是[[], [], [], [], []...]
for _ in range(100): #让g中的每一个节点都循环100次得到较为客观的值
for i in g:
state=suspect_infect_recovery(g, [i], beta, gamma)#让g中的每一个节点都做一次sources(第一个被感染的人)
s=sum([1 for i in state if state[i] !="S"])#统计共有多少人受到了感染
res[i].append(s)
#此时的res就是[[24, 16, 5, 24, 24..],[21, 8, 25, 15, 7..]...]
impact=[sum(r)/len(r) for r in res] #让g中的每一个节点都循环100次再做平均得到一个客观的值
#impact就是[18.02, 15.69, 17.07, 14.68...]的样子
nx.draw(g,pos=pos,node_color=impact,with_labels=True,node_size=300)
Impact_sort=sorted([(i,impact[i]) for i in range(len(impact))],key=lambda x:x[1],reverse=True)
for i in Impact_sort:
print(i)
十、PageRank
简单来讲:
import networkx as nx
G = nx.graph_atlas(100)
pageRank_list = nx.pagerank(G)
print("pageRank值是:", pageRank_list)
计算得到了每个点的PR值
具体来讲
# -*- coding: utf-8 -*-
import networkx as nx
G = nx.DiGraph()# 创建有向图
edges = [("A", "B"), ("A", "C"), ("A", "D"), ("B", "A"), ("B", "D"), ("C", "A"), ("D", "B"), ("D", "C")]
# 有向图之间的边的关系
for edge in edges:
G.add_edge(edge[0], edge[1])
#或者G.add_edges_from(edges)
pageRank_list = nx.pagerank(G, alpha=1)
print("pageRank值是:", pageRank_list)
得到的结果如下:
pageRank值是: {'A': 0.33333396911621094, 'B': 0.22222201029459634, 'C': 0.22222201029459634, 'D': 0.22222201029459634}
(0.3333,0.2222,0.2222,0.2222)也就是对应着 A、B、C、D 四个页面最终平衡状态下的影响力。你能看出 A 页面相比于其他页面来说权重更大,也就是 PR 值更高。而 B、C、D 页面的 PR 值相等。
Ex:
已知一个网络矩阵如下,利用PageRank算法,请计算这个网络每个节点的pr值,并根据pr值的大小控制节点大小绘制网络图
import networkx as nx
import matplotlib.pyplot as plt
from networkx.algorithms.link_analysis import pagerank
G = nx.Graph()
with open( 'Karate_club.txt', 'r' )as file: ##5,10
lines = file. readlines( )
for i in range( len(lines)):
lines[i] = lines[i].strip('\n').split(' ')
for j in range(len( lines[i])):
if lines[i][j] == '1':
G.add_edge(i + 1, j + 1)
pr=nx.pagerank(G)
print(pr)
layout = nx.spring_layout(G)
nx.draw(G, pos=layout, node_size=[i * 6000 for i in pr.values()],node_color='g',with_labels=True)
plt.show()