首页 > 技术 > 新冠病毒的SIR模型

新冠病毒的SIR模型

化学课作业?

UPD:论文:
[pdf-embedder url=”https://www.yhzq-blog.cc/wp-content/uploads/2020/03/Covid-19.pdf”]

化学课的期末作业是对新冠病毒进行数学建模。。也不需要特别精确,就用最简单的SIR模型吧。

S代表易感者,I表示感染者,R表示恢复者+死亡者。易感者会变成感染者,感染者会变成恢复者或者死亡者(这两种没有区别,都不会再传染或者被传染。那么这可以用一种化学反应表示:

$$S + I \mathop\rightarrow^r 2I \\ I \mathop\rightarrow^a R$$

第一个是感染的过程,反应速率为r,I相当于一种自催化剂。第二个是恢复和死亡的过程,速率为a。根据rate law,我们能列出如下ODE

$${dS\over dt} = -rSI \\ {dI\over dt} = rSI – aI \\ {dR\over dt} = aI$$

这就是SIR模型了。这个模型的一些假设:

1.人口在一段时间内保持不变。
2.得了传染病的人会有一定的传染性。
3.。。。其实还有很多,想想就不合理。比如潜伏期啥的

在这里面还有一个重要的参数叫基本再生数$R_0$。它表示在发病初期,一个病人在平均患病期能传染的人数。所以$R_0$和1的大小关系取决了这个病是否能传播。通过这个ODE:
$${dR\over dt} = aI$$
可以解得
$$R(t) ={e^{aIt} + C}\\ Since\ R(0) = 0, R(t) = e^{aIt}$$
我们让$I = 1$,且不考虑I的变化,那么这个函数就代表了这个一个患者在t转换成死亡或者痊愈的概率。同理可得,一个患者在t仍然存活的概率为$e^{-at}$。那么,在t时刻新增的患者(由这一个患者传染的)为$rS_0e^{-at}$,所以
$$R_0=\int_0^\infty rS_0e^{-at}dt={rS_0\over a}$$

首先在中国健康部爬下数据。我决定使用湖北的数据,因为假设2,最好使用一个小区域的数据。而且湖北是病毒爆发的地方。湖北的数据:

现在我们有三个变量不知道:$S_0,r,a$, 分别是易感人群的初始数量和两个速率。首先,我们知道$1/a$应该为恢复的天数。查了些资料发现大概是25天,就先假设$a=0.04$。剩下两个利用双重循环暴力试下。利用R^2做最优。

解ODE的函数:

def get_a(n, S, r, a):
    I = 494
    R = 0
    dt = 0.001
    dS = 0
    dI = 0
    dR = 0
    aS = [S]
    aI = [I]
    aR = [R]
    cont = 0
    for i in range(int(n / dt)):
        S += dS
        I += dI
        R += dR
        if (cont == 1 / dt):
            aS.append(S)
            aI.append(I)
            aR.append(R)
            cont = 0
        cont += 1
        dS = -1 * r * S * I * dt
        dI = (r * S * I - a * I) * dt
        dR = a * I * dt
    return np.asarray(aS), np.asarray(aI), np.asarray(aR)

下面是找最优的

def get_error(aI, aR, aS, S):
    mI = sum((aI - tI) ** 2) / len(tI)
    mR = sum((aR - tR) ** 2) / len(tR)
    mS = sum((aS - (S - tI - tR)) ** 2) / len(aS)
    rI = mI / np.var(tI)
    rR = mR / np.var(tR)
    rS = mS / np.var(S - tI - tR)
    return (rI + rR + rS) / 3
def get_max(n, a):
    bS, bI, bR = get_a(n, 60000, 1e-5, a)
    bE = get_error(bI, bR, bS, 60000)
    ers = []
    for S in range(68000, 100000, 1000):
        cS, cI, cR = get_a(n, S, 1e-5, a)
        cE = get_error(cI, cR, cS, S)
        po = -1
        for r in np.arange(1e-7, 1e-5, 5e-7):
            aS, aI, aR = get_a(n, S, r, a)
            tE = get_error(aI, aR, aS, S)
            if tE < cE:
                cS, cI, cR = aS, aI, aR
                cE = tE
                po = r
        ers.append(cE)
        if cE < bE:
            bS, bI, bR = cS, cI, cR
            bE = cE
            print(po, S, 1 - bE, po * S / a)
    return bS, bI, bR, ers

返回的ers可以看出现在的人数区间是否有最好的。比如画出来是这:

能看出来已经经过极值点了,这个区间就是可行的。找出比较小的r和S的区间后,再加入a的变化。

mins = 1e15
for a in np.arange(0.035, 0.042, 0.001):
    bS, bI, bR, cE = get_max(54, a)
    if np.min(cE) < mins:
        mins = np.min(cE)
        aS, aI, aR = bS, bI, bR
        print(a, 1 - mins)

然后就得出了这样的结果:

$R_0 = 7.94, R^2 = 0.945$, 但是看着就感觉很不靠谱。。加上时间

预测还有三个月。。

用相同的a和r预测美国,遍历下S,找到最好的,结果:

好像海星,$R^2=0.97$,预测:

还有20天到峰值,120天结束。

意大利:这个准确度真的让我震惊了。。$R^2=0.997$

预测:

伊朗:这个数据波动比较奇怪,可能因为比较动荡?

预测

法国:


德国:


西班牙: