目录

  • 0 简介
  • 1 效果展示——树状网络
  • 2 效果展示——小世界网络
  • 3 代码
    • 3.1 创建网络:树状网络
    • 3.2 创建网络:小世界网络
    • 3.3 创建网络:随机网络
    • 3.4 创建网络:补充说明
    • 3.5 在随机位置生成初始感染节点
    • 3.6 传染
    • 3.7 可视化
  • 4 执行
    • 4.1 创建小世界网络
    • 4.2 创建树状网络
    • 4.3 传播模拟可视化
  • 5 后记

0 简介

本博文代码的思路是:
1、假设某个(某些)在网络中的节点有某种传染病
2、传染病会根据一定的传播概率传播给与被感染的节点直接相邻(连接)的其他节点

然后,要把这个传播的过程用图片的形式呈现出来,并且记录下每次迭代的感染数据。
顺便一提,本文基于SI模型,如果需要SIR模型还是SIS还是SIER模型的可以自己改进。

1 效果展示——树状网络

照例,先来一波效果展示。
在可视化中红色是已被感染的节点,绿色是健康节点。


这里我先展示一下3个分支5层的树状网络的效果,为了快点模拟完成,设置传染率0.5,初始感染节点为3个。

初始状态(第0次迭代):

第1次迭代:

第2次迭代:

……
第12次迭代:

……
第16次迭代:

……
第22次迭代(最后一个节点还蛮顽强的):

在出图片的同时,终端还输出了每次的感染情况:

2 效果展示——小世界网络

可能一个类型的网络看得还不太清楚,所以我再展示一下小世界网络的传播情况。
这是一个有80个节点的小世界网络,初始邻居数是4,断线重连概率是0.2。设置传染率0.5,初始感染节点为3个。

初始状态(第0次迭代):

第1次迭代:

第2次迭代:

……
第5次迭代:

第6次迭代:

第7次迭代:

第8次迭代:

第9次迭代:

终端输出的每次迭代的感染情况:

3 代码

好的,现在步入正题。

首先调用需要的包。

import numpy as np
import random
import copy
import matplotlib.pyplot as plt
import networkx as nx

然后再写几个生成不同形态的网络的函数。本博文提供了树状网络,小世界网络和随机网络。

3.1 创建网络:树状网络

def tree(Branch,Grade):                              #树状网络             #Branch为节点向下分支,Grade为层数       #树状网络无需预先创建网络模型(网络关系矩阵)
    ####创建树状网络初始网络模型(网络关系矩阵)(创捷节点)####
    Nodes = 0
    for power in range(Grade):
        Add_Nodes = Branch**power
        Nodes = Nodes + Add_Nodes
    Zero = []
    a = np.zeros(Nodes,int)
    for i in range(Nodes):
        Zero.append(a)
    Zero = np.array(Zero)
    ###########构建树状网络关系(连结各节点)##############
    C = copy.deepcopy(Zero)
    Nodes = len(C)
    Search = 0
    Start = 0
    End = 1
    for g in range(1,Grade):          #因为一层树无意义
        LastCount = Branch**(g-1)     #上一层树有LastCount个节点一这层连结
        Count = Branch**g             #这层树有Count个节点与上一层连结
        LastNode = range(Start,End)   #上一层树在网络关系矩阵的位置
        Start = End                   #这层树的起始与终止位置
        End = Start + Count
        ThisNode = range(Start,End)   #这层数在网络关系矩阵的位置
        Cursor = Start
        for n in LastNode:
            for t in range(Cursor,(Cursor+Branch)):    #该层树与上一层树创建双向连结关系,游标Cursor指向该层树
                C[t][n] = 1
                C[n][t] = 1
            Cursor = Cursor + Branch  #游标Cursor切换
    return C

3.2 创建网络:小世界网络

def small_world(Nodes,Neighour,Alpha):               #小世界网络      #Nodes为节点数,neighour为初始邻居数,Alhpa为按照watts-strogatz法断线重连概率
    ####创建树状网络初始网络模型(网络关系矩阵)(创捷节点)####
    Zero = []
    a = np.zeros(Nodes,int)
    for i in range(Nodes):
        Zero.append(a)
    Zero = np.array(Zero)
    ###########构建网络关系(连结各节点)##############
    C = copy.deepcopy(Zero)
    Nodes = len(C)                                  #节点数
    for a in range(Nodes):                          #先按正常连接
        Add_time = 0
        while Add_time < (Neighour/2):
            C[a][a-(Add_time+1)] = 1
            C[a-(Add_time+1)][a] = 1
            Add_time += 1
    ##################断线重连部分###################
    for r in range(Nodes):                          #读取各节点
        Add_time = 0
        while Add_time < (Neighour/2):
            if C[r][r-(Add_time+1)] == 1:           #若存在连线,根据所填概率询问是否重连
                if random.random() < Alpha:         #若生成的随机数小于Alpha则重连
                    C[r][r-(Add_time+1)] = 0                    #清除现有连线
                    C[r-(Add_time+1)][r] = 0
                    Ob = random.randint(0,(Nodes-1))#新建连结对象
                    while C[r][Ob] == 1 or Ob == r: #两点之间不能存在多个连结,并且新建的连结对象不能是自身
                        Ob = random.randint(0,(Nodes-1))
                    C[r][Ob] = 1                    #连结
                    C[Ob][r] = 1
            Add_time += 1
    return C                                        #返回一个连结属性矩阵,1为连结,0为无连结

3.3 创建网络:随机网络

def random_network(Nodes,Linker):                           #随机网络
    ####创建树状网络初始网络模型(网络关系矩阵)(创捷节点)####
    Zero = []
    a = np.zeros(Nodes,int)
    for i in range(Nodes):
        Zero.append(a)
    Zero = np.array(Zero)
    ###########构建网络关系(连结各节点)##############
    C = copy.deepcopy(Zero)
    Nodes = len(C)                                  #节点数
    for a in range(Linker):
        target1 = random.randint(0,Nodes-1)         #随机抓取两个节点
        target2 = random.randint(0,Nodes-1)
        while C[target1][target2] == 1 or target1 == target2:
            target1 = random.randint(0,Nodes-1)
            target2 = random.randint(0,Nodes-1)
        C[target1][target2] = 1
        C[target2][target1] = 1
    return C                                        #返回一个连结属性矩阵,1为连结,0为无连结 

3.4 创建网络:补充说明

补充一下,本博文的网络构建采用矩阵的方式构建,我随手print一个10节点邻居为2重连率为0.2的小世界网络:

接下来还有3个关于传播和可视化的函数。

3.5 在随机位置生成初始感染节点

def catastrophe(Nodes,Amount):                      #设置初始感染者数量,在随机位置生成
    a = range(Nodes)
    Infecters = random.sample(a,Amount)
    InfectStatus = np.zeros(Nodes,int)              #感染状态表
    for i in Infecters:
        InfectStatus[i] = 1                         #感染者登记为1
    return InfectStatus                             #返回一个感染者编号列表

3.6 传染

def infect(Connections,InfectStatus,Beta):         #传染模型 Connections为节点连结关系属性矩阵,Infecters为初始感染者,Beta为感染率
    Status1 = copy.deepcopy(InfectStatus)           #前一状态感染者列表
    Status2 = copy.deepcopy(InfectStatus)           #当前状态待更新感染者列表
    C = copy.deepcopy(Connections)
    Nodes = len(C)
    if sum(InfectStatus) < Nodes/2:                  #当感染节点数小于总数的一半时
        for i in range(len(Status1)):               #遍历所有节点,发现感染者
            if Status1[i] == 1:                     #若状态为1,即为感染
                for j in range(len(C[i])):
                    if Status1[j] == 0 and C[i][j] == 1:                #若连结关系为1,即为连结
                        if random.random() <= Beta: #若生成的随机数小于Beta则登记为感染
                            Status2[j] = 1          #i感染j
    else:
        for a in range(len(Status1)):
            if Status1[a] == 0:
                for b in range(len(C[a])):
                    if Status1[b] == 1 and C[a][b] == 1:
                        if random.random() <= Beta:
                            Status2[a] = 1
    return Status2                                  #返回新的感染者列表

3.7 可视化

def show_iteration(Connections,Amount,Beta):        #传染病迭代输出模型        #Connections为网络关系矩阵,Amount为初始(0时期)感染者数量
    C = copy.deepcopy(Connections)
    Nodes = len(C)
    InfecterStatus = catastrophe(Nodes,Amount)        #根据设定的初始感染数,在随机位置生成感染者
    g = nx.Graph()                                  #新建画布
    for n in range(Nodes):                          #在画布上设置节点
        g.add_node(n)
    for ed in range(Nodes):                         #在画布上设置边(连结关系)
        for lin in range(ed+1,Nodes):
            if C[ed][lin] == 1:
                g.add_edge(ed,lin)
    pos  =nx.kamada_kawai_layout(g)                 #kamada-kawai路径长度成本函数计算
    Status = {}
    times = 0
    while sum(InfecterStatus) <= Nodes:             #当感染数大于等于节点数时停止迭代
        # plt.imshow(InfecterStatus)
        # plt.pause(3)#帧数
        for s in range(len(InfecterStatus)):        #把感染状态写入字典
            SI = InfecterStatus[s]
            Status[s] = SI
        colors = []
        for c in g:                                 #分配各节点颜色表示感染状态
            sta = Status[c]
            if sta == 1:
                clr = 'r'
            if sta == 0 :
                clr = 'g'
            colors.append(clr)
        nodesize = []
        for ns in g:
            de = ((sum(C[ns])*10)+50)                 #节点大小(节点度数越大,节点越大)
            nodesize.append(de)
        plt.figure(figsize=(12, 8))
        nx.draw_networkx_nodes(g , pos=pos , with_labels=True , node_color=colors , node_size=nodesize , alpha=0.6)
        nx.draw_networkx_edges(g , pos=pos , with_labels=True , width=0.3 , alpha=0.3)
        print(f'迭代第 {times} 次 ---- 感染者数量:{sum(InfecterStatus)} ---- 占比:{(sum(InfecterStatus)/Nodes)}')
        plt.show()
        if sum(InfecterStatus) == Nodes:
            Nodes = Nodes - 1
        InfecterStatus = infect(C,InfecterStatus,Beta)     #传染模型
        times += 1
    print('---------- 迭代完成 ----------')

4 执行

怕有的小伙伴光看上面了不会执行,所以在这边特地说明一下执行部分。
随缘说明,我想到啥说啥,其实也不是很难,根据我以下的说明举一反三很容易。

4.1 创建小世界网络

生成节点数为20,初始邻居数为4,重连率为0.2的小世界网络并打印出来

代码如下:

C = small_world(20,4,0.2)
print(C)

结果如下:

4.2 创建树状网络

生成4个分支,3层的树状网络并打印出来

代码如下:

C = tree(4,3)
print(C)

结果如下:

4.3 传播模拟可视化

以节点数100邻居数4重连率0.2的小世界网络为例进行传染病传播模拟。设置初始感染节点10,传染率0.5。

代码如下:

C = small_world(100,4,0.2)    #生成网络矩阵
show_iteration(C,10,0.5)      #传播模拟

开始运行会跳出一幅图,这是初始状态(第0次迭代)网络的展示。

把上面这幅图关掉,会出现第二幅图,这是第1次迭代网络的展示。同时在终端还会打印出当前以前的传染情况。


依次类推。当全部节点感染的时候,就停止运行。
后话:如果需要连贯地显示可以用imshow动态显示(无需手动关闭上一张图片),具体的调用方法可以自行百度。

5 后记

动态视频可以参考这篇文献中的附录A(建议用网页打开):
Looking into mobility in the Covid-19 ‘eye of the storm’: Simulating virus spread and urban resilience in the Wuhan city region travel flow network
如果可以,也希望大家多多引用。


-----------------------分割线(以下是乞讨内容)-----------------------

更多推荐

【Python】基于Python的复杂网络传播动力学及其可视化