概述
本章藉助matplotlib
包,模擬疫情傳播,有兩種思路:
- 使用面向對象的方式,每個人爲一個獨立對象
- 使用矩陣的方式
方法1. 面向對象方式
面向對象的方式,每個人爲一個獨立對象
- 邏輯非常簡單
- 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. 定義人
- 座標:隨機分佈
- 顏色:綠色代表健康,紅色代表感染
- 運動函數:人隨機運動
# 人
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. 環境
- 按照人口生成不同的人口對象
- 隨機選擇病源
# 人羣
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. 病毒感染
- 遍歷所有人羣,區分患者與普通人
- 遍歷患者:
- 遍歷所有人,計算與當前患者的距離
- 當目標與患者距離小於傳播半徑,則有一定機率感染。
- 遍歷所有人,計算與當前患者的距離
- 返回患病熟料
# 病毒感染函數
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. 代碼
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
感染函數
- 得到所有的感染者座標
- 遍歷感染者:
- 計算每一個感染者與所有人的距離
- 將所有小於傳染範圍的鄰居標記爲感染
# 傳染函數
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)