Python學習筆記#9:病毒傳播模擬程序

概述

本章藉助matplotlib包,模擬疫情傳播,有兩種思路:

  1. 使用面向對象的方式,每個人爲一個獨立對象
  2. 使用矩陣的方式

方法1. 面向對象方式

面向對象的方式,每個人爲一個獨立對象

  1. 邏輯非常簡單
  2. python效率太慢

1. 導入包

import numpy as np
import matplotlib.pyplot as plt

2. 全局參數

# 地圖寬度
width = 100

# 總人口
pop = 2000

# 初始病人數量
n = 10

# 感染半徑
sd = 10

# 感染機率 50%
sr = 0.5

3. 定義人

  1. 座標:隨機分佈
  2. 顏色:綠色代表健康,紅色代表感染
  3. 運動函數:人隨機運動
# 人
class People(object):
    def __init__(self):
    	# 隨機座標
        self.x = np.random.rand() * width
        self.y = np.random.rand() * width
        self.loc = np.array([self.x, self.y])
        self.color = 'g'

    # 隨機運動
    def move(self):
        self.x += np.random.randn()
        self.y += np.random.randn()

4. 環境

  1. 按照人口生成不同的人口對象
  2. 隨機選擇病源
# 人羣
all_people = np.array([People() for i in range(pop)])

# 初始化患病人羣
sick_people = np.random.choice(all_people, size=n, replace=False)
for p in sick_people:
    p.color = "r"

5. 病毒感染

  1. 遍歷所有人羣,區分患者與普通人
  2. 遍歷患者:
    1. 遍歷所有人,計算與當前患者的距離
      1. 當目標與患者距離小於傳播半徑,則有一定機率感染。
  3. 返回患病熟料
# 病毒感染函數
def affect(all_people):
    sick_people = []
    healthy_people = []
    n = 0
    for p in all_people:
        if p.color == "r":
            sick_people.append(p)
            n += 1
        if p.color == "g":
            healthy_people.append(p)

    for sp in sick_people:
        for hp in healthy_people:
            dist = np.linalg.norm(sp.loc - hp.loc)
            rad = np.random.rand()
            if hp.color == "g" and dist <= sd and rad < sr:
                hp.color = "r"
                n += 1
    return n

6. 動態顯示

使用matplotlib互交模式,動態顯示

plt.ion()

# 模擬
while n < pop:
    plt.clf()
    update(all_people)
    n = affect(all_people)
    plt.scatter([p.x for p in all_people], [p.y for p in all_people], c=[p.color for p in all_people], s=3)
    plt.axis([0, width, 0, width])
    plt.pause(0.5)
    print("總人數:{},傳染人數:{}".format(pop, n))

plt.ioff()
plt.show()

7. 結果

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

總人數:1000,傳染人數:150
總人數:1000,傳染人數:585
總人數:1000,傳染人數:875
總人數:1000,傳染人數:984
總人數:1000,傳染人數:1000

方法2. 矩陣運算方式

藉助numpy,可以非常快速的處理矩陣運算,特點:

  1. 速度快
  2. 處理數據量大
  3. 比較抽象

1. 代碼

import numpy as np
import matplotlib.pyplot as plt


class VirusSimulator(object):
    def __init__(self):

        # 地圖寬度
        self.width = 100

        # 總人口
        self.pop = 5000

        # 初始病人數量
        self.first_patients = 10

        # 感染半徑
        self.infection_radius = 5

        # 感染機率 50%
        self.infection_potential = 0.5

        # 人羣:橫座標、縱座標
        self.locations = np.random.randn(self.pop, 2) * self.width

        # 狀態:(g-正常、r-感染)
        self.status = np.array(["g"] * self.pop)

        # 初始化感染羣
        self.initPatients()

    # 更新人羣位置
    def move(self):
        self.locations += np.random.randn(self.pop, 2)

    # 初始化患病人羣
    def initPatients(self):
        for i in np.random.randint(self.pop, size=self.first_patients):
            if self.status[i] == "g":
                self.status[i] = "r"

    # 統計感染人羣
    @property
    def patients(self):
        return self.locations[self.status == "r"]

    # 統計感染人數
    @property
    def patients_num(self):
        return self.status[self.status == "r"].size

    # 傳染函數
    def affect(self):
        for ip in self.patients:
            distances = np.sqrt(np.sum(np.asarray(ip - self.locations)**2, axis=1))
            self.status[distances < self.infection_radius] = "r"

    # 顯示函數
    def display(self):
        current_patient_num = self.patients_num
        print("總人數:{},傳染人數:{}".format(self.pop, current_patient_num))
        plt.ion()

        while current_patient_num < self.pop:
            plt.clf()
            plt.scatter(self.locations[:, 0], self.locations[:, 1], c=self.status, s=1)
            plt.axis([-self.width, self.width, -self.width, self.width])
            self.move()
            self.affect()
            current_patient_num = self.patients_num
            print("總人數:{},傳染人數:{}".format(self.pop, current_patient_num))
            plt.pause(0.5)

        plt.ioff()
        plt.show()


if __name__ == "__main__":
    vs = VirusSimulator()
    vs.display()

2. 重難點

運動
簡單的生成一個隨機向量矩陣,大小和原來的座標矩陣一致,然後相加,即可達到隨機運動的效果。

    # 更新人羣位置
    def move(self):
        self.locations += np.random.randn(self.pop, 2)

裝飾器
Python內置的@property裝飾器就是負責把一個方法變成屬性調用

    # 統計感染人羣
    @property
    def patients(self):
        return self.locations[self.status == "r"]

    # 統計感染人數
    @property
    def patients_num(self):
        return self.status[self.status == "r"].size

感染函數

  1. 得到所有的感染者座標
  2. 遍歷感染者:
    1. 計算每一個感染者與所有人的距離
    2. 將所有小於傳染範圍的鄰居標記爲感染
    # 傳染函數
    def affect(self):
        for ip in self.patients:
            distances = np.sqrt(np.sum(np.asarray(ip - self.locations)**2, axis=1))
            self.status[distances < self.infection_radius] = "r"

3. numpy的優勢

距離算法:

distances = np.sqrt(np.sum(np.asarray(ip - self.locations)**2, axis=1))

實際上可以分解爲

l = np.asarray(ip - self.locations)

其中ip爲單一的點,self.locations爲數組,結果也爲數組,大小和self.locations一樣。

更進一步,得到每個點的差的乘積

l = np.asarray(ip - self.locations)**2

乘積相加,但是這樣只有一個數字,

s = np.sum(np.array(p - locations)**2)

想要得到矩陣,需要控制參數axis

# axis=1,表示沿着x軸相加,橫向
l = np.sum(np.array(p - locations)**2,axis=1)

# axis=1,表示沿着y軸相加,豎向
l = np.sum(np.array(p - locations)**2,axis=0)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章