感染シミュレーションを作ってみる on Processing
ゆめみでデータ分析をしているtmhr11です。今日はプログラミングによるビジュアライズが簡単にできるPrcessingを使って、サクっと感染のビジュアライズを作ってみたいと思います。
なお、シミュレーション内容について疫学的・科学的根拠はありません。見た目雰囲気が感染を想起させるものであれば、良しとしますw。
動機
1. コロナによって、連日感染者数の時間的増減はグラフ表示されていますが、感染のイメージは、隣人から隣人へと、空間的に広がって行くものだと思います。このイメージを見たい。
2.Processingは前から知っていたが、ちゃんと使っていなかった。言語はJAVAと思っていたが、最近Pythonでも記述できることを知った。
もちろん、Pythonでもmatplotlibというメジャーな視覚化ライブラリーはありますが、(https://qiita.com/kira4845/items/9d340d68a0336f954506)
動きがないし、見た目真面目臭くてつまらないですね。
一方、Processing(https://processing.org/) は、美しく動きのある表現が簡単にできる、プロトタイピングツールとして定評があります。Pythonの素晴らしい計算ライブラリとProcessingの視覚化環境がそろえば最強では、と思ったのでした。
Processingとは
グラフィック表示ライブラリと、プログラミング実行環境がセットになっています。ベジェ曲線や細かなタイポグラフィーの出力も可能です。 https://www.d-improvement.jp/learning/processing/
昔FLASHのactionscriptでタイムラインを使わずに動画を作っていた人は、雰囲気が近いことを感じると思います。
エンジニアよりアート系の学生のほうに知られているのかもしれませんね。
通常、javaで記述するのですが、ここはあえてPythonを選択してみようと思います。
シミュレーションルール
・シンプルにすること
x,x方向2次元のグリッドを空間とし、各グリッドの1コマ(セル)が、周囲のセルに対して影響すること(セルオートマトン)をベースとした。セルの状態変化ルールはシンプルであるほどかっこいいと思います。
・lifegame
セルオートマトンの代表で、life game という70年代の有名なゲーム(?)があるのですが、簡単に言えば、周囲との粗密関係で、生命個体を表すセルが生き残るか死ぬかが決まる、生態系シミュレーションの元祖のようなものです。http://math.shinshu-u.ac.jp/~hanaki/lifegame/
セル自体は単純なルールに従っているだけにも関わらず、一見予測不能な動きや形を発生させるという点で、生命の神秘に迫っているような気にさせますが、作った本人はそこまで考えていなかったでしょう。複雑系という学術領域の文脈で出てくることも多いです。余談ですが、複雑系という名称は、英語のcomplex systemを直訳したものと思いますが、渋谷系・ビジュアル系のようにチャラい印象を受けますw。よっぽど、'複雑システム'とかにしておけば良かったんじゃないでしょうか。
本題に戻ると、ルールは以下にしました。
・セルは、感染状態と非感染状態を持つ。
・感染状態は30ステップ(=時間)続き、31ステップ以降非感染状態になる。
・感染状態最初の2ステップは周囲に感染力を持つ
・非感染状態のセルの縦横斜め1ブロック(合計8ブロック)に、感染力を持ったセルが存在すると、80%の確率で感染し、新たな感染セルとなる。
これは、新規感染後2ステップは周囲に感染させる力を持つ、3〜30ステップは免疫を持った状態で、31ステップ目で免疫力は無くなることを意味します。
以上ですが、各種定数は調整可能として、一番面白く見える塩梅を探すのが肝です。
制作
Processingには多くのサンプルコードが付属しています。その中を漁っていると、なんとlifegameがあるではないですか。このコードを流用します。これで制作時間8割減です。
1.アルゴリズムを記述していきます。
lifegameの肝である、周囲セルの状態探索と、状態変化の部分をカスタマイズしていきます。
def iteration(): # When the clock ticks
# Save cells to buffer
# (so we opeate with one array keeping the other intact)
global disp_text
new_infection = 0
for x in range(grid_w):
for y in range(grid_h):
cellsBuffer[x][y] = cells[x][y]
# Visit each cell:
for x in range(grid_w):
for y in range(grid_h):
# And visit all the neighbours of each cell
neighbours = 0 # We'll count the neighbours
for xx in range(x - 1, x + 2):
for yy in range(y - 1, y + 2):
# Make sure you are not out of bounds
if 0 <= xx < grid_w and 0 <= yy < grid_w:
# Make sure to check against self
if not (xx == x and yy == y):
if (cellsBuffer[xx][yy] > (lifetime - 2)) and (cellsBuffer[xx][yy] <= (lifetime - 0)):
# Check alive neighbours and count them
neighbours = neighbours + 1
if cellsBuffer[x][y] > 0:
cells[x][y] = cellsBuffer[x][y] - 1
else:
if neighbours > 0:
if random(100) > 80:
cells[x][y] = lifetime # new infection, reflesh lifetime
new_infection = new_infection + 1
2. 見た目を変えていきます。
Processingでのアニメーションは、draw()メソッドが1フレーム毎に呼び出され、描写されるというのが基本となります。
色合い、セルの形状などを、変えていきます。今回はセル円形とし、活性状態に合わせてサイズも微妙に変化させます。
せっかくなんで色もサイケデリックにしていきましょう。
def draw():
global lastRecordedTime
global disp_text
global lifetime
#reflash screen
fill(0,0,0)
noStroke()
rect(0, 0, width, height)
# Draw grid
for x in range(grid_w):
for y in range(grid_h):
life = cells[x][y]
if cells[x][y] > 0:
fill(color( life * 8, 0,100 )) # 感染中
else:
fill(dead) # 感染なし
circle(x * cellSize + cellSize/2, y * cellSize + cellSize/2, life*0.5)
3. こうなります
まとめ
・いい感じに気持ち悪い。
動きが生き物っぽい。
・新規感染の波が起こる
シミュレーションは常に同じルールに従っているにも関わらず、新規感染の発生数には波が起きます。つまり、小康状態と感染爆発を繰り返します。個人的な意見ですが、コロナ感染の増減とは、人間の活動量に依存している以外に、自然界のゆらぎが影響していると思います。
・Python on Processsing の問題
スピードが思ったほど早くない
遅い、描画部分に引きづられてるのかもしれません。
Pythonのライブラリが自由にインポートできない
これが最も問題で、Pythonでscikit-learnなどのライブラリをインポートする方法が、あるのかどうか?単純にimport文を書くだけではダメなようです。
また、コメントに日本語を使うと文字化けするという、細かな制約も見られました。
それでは、ご視聴頂き有難うございました。
この記事が気に入ったらサポートをしてみませんか?