モデル
次のような確率モデルを考える.
ここで は差分を意味する記号であり, を表す.
また は試行回数パラメータ , 成功確率パラメータ の二項分布を表し, 記号 (実は読み方を知らない. 「チルダ」でいいのか?)は左辺の が右辺の二項分布に従う確率変数であることを表す.
イメージしやすくするため, は時間を表す添字であり, は 時点で, ある感染症に感染するリスクがある集団の人数, は 時点で病気にかかっていて人に病気を移す可能性がある集団の人数だと思ってほしい.
(ただし今日のこの日記の目的は感染症の数理モデルを解説することではないので, 過度な期待はしないほうがよい.)
このモデルは「感染せずに残っている 人が, 確率 で感染者になる」というもので, 感染者が1人から2人に増えたら, 感染する確率も2倍になるだろうという考えで作られた.
一方, 次の微分方程式を考える.
総人口 を一定としたので, であり, 組の方程式を一つにまとめることができる.
これを解くと, 次の関数が得られる.
微分方程式によるモデルと二項分布のモデルは違うものであるが, パラメータ , 0 時点めの感染者数 , 総人口 と数字を適当に決め, 両方プロットしてみよう.
黒い実線が乱数による , 青い点線が微分方程式の解である.
累積でなく日毎の新規感染者数 に関心があることも多いだろう. 微分方程式の解を微分して,
もプロットしてみる.
黒い点が乱数による , 青い実践が微分方程式の解によるものである.
この場合は両者でそこそこ近い値が得られている(運がわるいと大きくずれることもある).
最尤推定
時刻 0 の感染者数と総人口()はわかっているものとする. パラメータ をデータから推定したい.
そこで最尤法を用いることにする. 対数尤度関数はモデルから直ちに,
と書ける. 記号が煩雑になるのをさけるため, とした. この () が観測されるデータである.
またここでの はパラメータ が登場しない項という意味である.
対数尤度関数を で微分した式を0とおいて解けば最尤推定量が求まるが, 計算を楽にしたいので, 一旦 とおいて で微分することにする.
をどこか一時点に固定して考えると, で微分することにより, 時点め以外の項は消える.
であるから,
である.
であり, なので,
である. これを0とおいて解きたいが と動くので,
という方程式が複数できる. 全部足し合わせて, 最尤推定量は
である. ここで, は から または になる場合を除いた集合を指す.
(0=0という方程式には情報がないので)
アニメーション
パラメータ , 0 時点めの感染者数 , 総人口 と数字を適当に決め, 時点 までの情報で を最尤推定し, 微分方程式の解に代入してプロットしてみよう.
成長曲線の推定あるあるとして, このように単純なモデルでも, 成長曲線がサチってくる点(変曲点)を超えるまで未来の予測はなかなかうまくいかないというのがある.
(本当は流行初期に未来のことを知りたいのに…)
アニメが苦手な人のために, 時間を色の濃淡に変えた2次元の図も載せておく.
パラメータの推定値の時点ごとの履歴は次のようであった.
おしまい.
コード
Rです.
library(ggplot2) library(gganimate) simSigmoid <- function(Tim,N,I0,b,seed=1){ set.seed(seed) xt <- integer(Tim) #Delta I_t St <- integer(Tim) It <- integer(Tim) St[1] <- N-I0 It[1] <- I0 for(t in 1:(Tim-1)){ xt[t] <- rbinom(1,St[t],b*It[t]) It[t+1] <- It[t] + xt[t] St[t+1] <- St[t] - xt[t] } data.frame(time=0:(Tim-1),St=St,It=It,xt=xt) } estparam <- function(St,It,xt){ flag <- St!=0 & It!=0 b_est = sum(xt[flag])/sum(St[flag]*It[flag]) return(b_est) } Sigmoid <- function(t,N,I_0,b){ N/(1+((N-I_0)/(I_0))*exp(-N*b*t)) } dSigmoid <- function(t,N,I_0,b){ A <- N*b B <- ((N-I_0)/(I_0)) (N*B*A*exp(-A*t))/(1+B*exp(-A*t))^2 } dat <- simSigmoid(100, 1000, 1, 0.15/1000, seed=123) ggplot(dat, aes(x=time, y=It))+ geom_line()+ stat_function(fun = Sigmoid, args=list(N=1000,I_0=1,b=0.15/1000), colour="royalblue", linetype=2)+ labs(y="Infection(cumulative)")+ theme_bw(16) ggsave("fixcum.png") ggplot(dat, aes(x=time, y=xt))+ geom_point()+ stat_function(fun = dSigmoid, args=list(N=1000,I_0=1,b=0.15/1000), colour="royalblue")+ labs(y="Infection(by date)")+ theme_bw(16) ggsave("fix.png") bhat <- sapply(1:100, function(t)estparam(dat$St[1:t],dat$It[1:t],dat$xt[1:t])) predm <- sapply(1:100, function(t)dSigmoid(1:100, 1000, 1, bhat[t])) df <- data.frame(time=1:100, xt = dat$xt, pred=as.vector(predm), time_dummy=rep(1:100,each=100)) ggplot(df, aes(x=time, group=time_dummy))+ geom_line(aes(y=pred, colour=time_dummy), alpha=0.3)+ geom_point(aes(y=xt))+ labs(y="Infection(by date)", colour="time")+ theme_bw(16) ggsave("inf.png") ggplot(df, aes(x=time, group=time_dummy))+ geom_vline(aes(xintercept = time_dummy), linetype=2)+ geom_point(aes(y=xt))+ geom_line(aes(y=pred), colour="royalblue")+ labs(y="Infection(by date)")+ theme_bw(16)+ transition_manual(time_dummy) anim_save("inf.gif") dfb <- data.frame(time=1:100, estimates=bhat) ggplot(dfb,aes(x=time, y=estimates))+ geom_hline(yintercept = 0.15/1000, linetype=2)+ geom_point()+ theme_bw(16) ggsave("est.png")