ランダムウォークによる感染シミュレーション
ランダムウォークは確率的な運動モデルの一種で、次にどちらにどれくらい動くかを確率的に決めるため不規則な挙動を示します。これを利用し人の行動のシミュレーションに用いることが可能です。
ここでは2次元のランダムウォークモデルで感染の広がりのシミュレーションを行います。ある領域に50人の人間がいて一人だけ感染症に掛かっています。1ステップ毎に50人はx,y方向それぞれにランダムな距離移動します。移動後に感染者と一定の距離以内にいる人は感染してしまいます。これをある時間ステップ繰り返したときにどれほど感染症が広まるかをシミュレートします。1ステップの移動距離が小さい場合と1ステップの移動距離が大きい場合で感染の広がり方が変わるかを見ます。
計算結果は500ステップの図をアニメーションgifにして感染が広がる様子を視覚化しています。
なお、実際の新型コロナウィルスの感染状況を反映してはいません。
関連記事
SIRモデルによる感染シミュレーション
SIRモデルによる感染シミュレーション(一度低下した感染率が再上昇する場合)
セル・オートマトンによる感染シミュレーション
ランダムウォーク
環境
- windows10 home
- Anaconda 3/ jupyter notebook 5.6.0
- Python 3.7.0
- Pillow 5.2.0
コード
1ステップのx,y方向の移動距離が±1以内の場合。図の中心に一人だけ感染者を置き人々の移動により感染がどのように増加するかをシミュレートします。感染者との距離が5以内になると感染することにしています。
import random import copy import math from PIL import Image, ImageDraw x = [] # エージェントのx座標リスト y = [] # エージェントのy座標リスト infection = [] # 感染状況リスト(1が感染、0が未感染) img_list = [] # 各時間ステップの画像保存用リスト movex = 1 # 1ステップのx方向最大移動距離 movey = 1 # 1ステップのy方向最大移動距離 touch = 5 # 感染距離(これより感染者に近づくと感染) persons = 50 # エージェント(人)の数 time = 500 # 全時間ステップ # 各エージェントの初期位置の設定 for i in range(persons-1): x.append(random.uniform(100,300)) y.append(random.uniform(100,300)) infection.append(0) # 感染者の位置設定 x.append(200) y.append(200) infection.append(1) # 次のステップの感染状況保管用リスト infection2 = copy.deepcopy(infection) # ランダムウォーク for j in range(time): # 描画用画像生成 infection = copy.deepcopy(infection2) im = Image.new('RGB', (400, 400), (255, 255, 255)) draw = ImageDraw.Draw(im) draw.rectangle(((0, 0), (399, 399)), fill=(255, 255, 255), outline=(0, 0, 0)) # 各エージェント(人)のx,y座標をランダムに変更 for i in range(persons): x[i] += random.uniform(-movex,movex) y[i] += random.uniform(-movey,movey) # 感染判定(感染済みエージェントとの距離が感染距離より近いかどうか) for k in range(persons): if i != k: disrance = math.sqrt((x[i]-x[k])**2+(y[i]-y[k])**2) if disrance < touch and infection[k] == 1: infection2[i] = 1 if infection2[i] == 1: color = (255,0,0) else: color = (0,0,255) # 感染状況の描画(感染者:赤、未感染者:青) draw.rectangle(((x[i]-1, y[i]-1), (x[i]+1, y[i]+1)), fill=color, outline=color) # 画像をリストに追加 img_list.append(im) # gifアニメーションファイルの保存(一画像あたり50ms、ループあり) img_list[0].save('infection_move1.gif', save_all=True, append_images=img_list[1:], optimize=True, duration=50, loop=0)
実行結果
一回の最大移動距離をx,yどちらも最大1とした場合、500ステップ後の感染者は3人(2人増加)となりました。
一回の最大移動距離をx,yどちらも5に変更した場合、感染者はどのように変わるかを確かめます。活発に人が移動する場合のシミュレーションです。
コード
movex,moveyを1から5に変更した以外は同じです。
import random import copy import math from PIL import Image, ImageDraw x = [] # エージェントのx座標リスト y = [] # エージェントのy座標リスト infection = [] # 感染状況リスト(1が感染、0が未感染) img_list = [] # 各時間ステップの画像保存用リスト movex = 5 # 1ステップのx方向最大移動距離 movey = 5 # 1ステップのy方向最大移動距離 touch = 5 # 感染距離(これより感染者に近づくと感染) persons = 50 # エージェント(人)の数 time = 500 # 全時間ステップ # 各エージェントの初期位置の設定 for i in range(persons-1): x.append(random.uniform(100,300)) y.append(random.uniform(100,300)) infection.append(0) # 感染者の位置設定 x.append(200) y.append(200) infection.append(1) # 次のステップの感染状況保管用リスト infection2 = copy.deepcopy(infection) # ランダムウォーク for j in range(time): # 描画用画像生成 infection = copy.deepcopy(infection2) im = Image.new('RGB', (400, 400), (255, 255, 255)) draw = ImageDraw.Draw(im) draw.rectangle(((0, 0), (399, 399)), fill=(255, 255, 255), outline=(0, 0, 0)) # 各エージェント(人)のx,y座標をランダムに変更 for i in range(persons): x[i] += random.uniform(-movex,movex) y[i] += random.uniform(-movey,movey) # 感染判定(感染済みエージェントとの距離が感染距離より近いかどうか) for k in range(persons): if i != k: disrance = math.sqrt((x[i]-x[k])**2+(y[i]-y[k])**2) if disrance < touch and infection[k] == 1: infection2[i] = 1 if infection2[i] == 1: color = (255,0,0) else: color = (0,0,255) # 感染状況の描画(感染者:赤、未感染者:青) draw.rectangle(((x[i]-1, y[i]-1), (x[i]+1, y[i]+1)), fill=color, outline=color) # 画像をリストに追加 img_list.append(im) # gifアニメーションファイルの保存(一画像あたり50ms、ループあり) img_list[0].save('infection_move5.gif', save_all=True, append_images=img_list[1:], optimize=True, duration=50, loop=0)
実行結果
感染者数が大幅に増えました。人が活発に移動することが感染拡大につながることが分かります。
最大移動距離を変えた場合の500ステップ後の画像を並べてみます。確率的ランダムウォークモデルなので計算するたびに結果は変わりますが移動距離が大きくなる(人が活発に移動する)ほど感染者数が多くなることが分かります。
movex,movey = 1の場合
movex,movey = 2の場合
movex,movey = 4の場合
movex,movey = 5の場合
以下のサイトを参考にさせていただきました
ランダムウォーク『ウィキペディア(Wikipedia)』
Pythonの文法メモ >> 【標準ライブラリ】randomによる乱数生成