什么是传染病动力学?numpy和matplotlib用python实现传染病模型SI模型SIS模型SIR模型SEIR模型 什么是传染病动力学?最近,在报道疫情的众多新闻中,相信大家也看到过一些来预测新型冠状病毒会导致感染肺炎的人数。你一定好奇,这个人数要怎么预测呢?预测人数又有什么用呢? 事实上,从学科方向来说,这类研究属于传染病动力学,就是用数学模型去描述传染病在人群中传播的规律,从而预测患病人数,进而指导政府制定措施和政策去控制传染病的传播。* m! D6 W0 }$ D. [
这类研究最早可追溯到18世纪Daniel Bernoulli对天花的研究,而我们今天所要介绍的SIR模型是1927年Kermack与McKendrick在为了研究伦敦黑死病而提出的,是传染病动力学中最基础的模型。 介绍了传染病模型的背景信息,不知道现在你对传染病模型更有兴趣,还是执着地对python更有兴趣呢?不论哪种,这篇文章会满足你所有的好奇心。 numpy和matplotlib首先,安装一下这节课我们需要使用的两个python包,numpy和matplotlib。
5 y+ k' g% `& Cnumpy-是python进行科学和矩阵运算最常用的包。 用numpy建立一维数组,存储和计算每天传染病人数的数据。 1 l8 O9 [% U, y
import numpy as np import matplotlib.pyplot as plt 用matplotlib绘制传染病人数随天数变化的曲线,给出模型预测人数变化的直观认识。 好啦,下面开始用python实现传染病模型吧。 用python实现传染病模型为了让大家能够更好地理解,我们先不直接说SIR模型,我们从最简单的开始。 SI模型首先想象这样一个场景,一个城市有 个人,假设没有人出生和死亡,忽然有一天有 个人感染了病毒成为了患者,如果每天每个患者能够有效传染 个人,那么第二天患病人数是多少呢?最简单的答案是: ,也就是说每天都会新增 个患者。那这样以来,在无限远的将来会有无穷多的人被感染,显然这是不合理的,那错在哪里?仔细思考,你一定发现了,已经患病的人就不能再被传染了,所以我们有必要把人群分为两类,易感者(S-susceptiable)和感染者(I-infective)(你猜的没错,这就是SIR中S和I的含义,R的含义之后介绍再讲)。为了之后方便计算我们记易感者和感染者在人群中的比例为 ,那么 。我们重新考虑上面的问题,顺便来个示意图: Image Name这样的话,每天新增的患者数为 ,也就是总传染人数乘以易感者所占的人群比例。' T" n/ N6 e8 L
那么每天的感染者比例的增加量就是 。我们假设城市有一千万(N=10的7次方)人,每个患者每天接触感染每天0.8人(lamda=0.8),初始感染人数为45人(i0 = 45/N),我们来模拟70天(T=70)的情况。 # population; u7 {; _) {0 x& ~+ r# B% }5 ]
N = 1e7
: f4 B& M _" e9 q# simuation Time / Day/ @( k. U e/ [( y- n9 f
T = 706 O$ h3 g( A2 P% ^5 [* U
# susceptiable ratio6 G+ m1 P( l u" E" m+ g
s = np.zeros([T]), j/ G1 c+ y5 H! @" { Y4 O/ `5 ]5 d
# infective ratio3 n1 b+ ?2 F/ U/ O9 V7 O7 {
i = np.zeros([T])5 {: G, t. u* j. g
# contact rate# S" j8 c5 K1 S' g5 z
lamda = 0.8. G2 m) B: J& K6 i: j, k
( J2 F3 v8 Z* k; v
# initial infective people
. f- n0 G& v7 A% _% v, Li[0] = 45.0 / N# G$ j4 e: P8 \* c8 F
0 h# A2 y# z5 i+ I! Vfor t in range(T-1):
, b0 v# I* v2 _8 J i[t + 1] = i[t] + i[t] * lamda * (1.0 - i[t])3 r# P/ y( A$ M Q2 e) D
* F0 t- H; U8 Z3 b: q6 F9 e: a
( }. D+ m. e3 R3 I+ h; ]5 C7 h/ \# a6 l- c$ `8 J
相信其他语句大家都明白,新知识是这两行:
! |) { W1 S( R5 y; n, ~
, p( v6 l' s3 m/ o
. d: ?( f5 j; Z3 k+ F% ]s = np.zeros([T])$ w; }, L( M) [( c0 o
i = np.zeros([T])
! I" @, ^+ W7 L; D3 n
5 l: a0 Z/ c3 \5 j这两句话的意思是一样的,就是利用numpy(已被我们重新命名为np)的函数(zeros())来建立一个所有元素都是零的数组,而给的参数决定了这个数组的维度。比如:
' _- C! M g/ c0 f2 {) P3 S. o; a
a = np.zeros([2,3])$ X! M4 d8 c0 o6 a1 r% F6 `0 w
a
4 Q6 a1 H0 h4 i! S3 @& G
' g; X) N5 T2 t: barray([[0., 0., 0.],' X# \! n+ b* Z- k% E" c- E
[0., 0., 0.]])( H( w2 A6 a: K
9 P1 h4 |) E; x: ?
3 w/ z5 g7 [1 ~% E6 farray([0., 0., 0., 0., 0.])
; V/ L( x0 O: e4 W$ K6 A m1 S. n9 |& ]2 |
0 b6 P, t, ~+ y; w" U( }
类似的还有产生元素全部是1的数组的函数np.ones():
1 ]" g: f6 K& i
' E; h- N2 Y* n, M8 Ya = np.ones([5])
) [0 ], i; f( M: M% h; u, W' Ia
8 D; _6 ?* r5 w; X) b% Z
4 p2 m. t% T1 oarray([1., 1., 1., 1., 1.])
- [# d8 n- q, I+ D. w3 d
8 H6 Q @5 c, D( a; ~& S, ^' R# f& w/ I9 B% k
a = np.ones([2,3])
1 J3 y" y0 o) R2 k3 I, B' m: ra) m* p1 O* R4 p2 B; R
) @ e- q0 R8 P
4 K3 S' r+ U4 `
array([[1., 1., 1.], C3 z+ z- Z: |0 ~1 q. g8 Q( X: s
[1., 1., 1.]])
$ f5 M" J$ Y0 j7 b7 H# U# |
/ \6 e4 O: v D: V) R! x1 G* p) _4 r. u
plt.plot(i)
2 N# S6 V$ }4 z x( {& G0 D& R Y
: W2 q, G2 H/ p' |+ F: L
6 G& Z) f+ I: `+ ~8 `[<matplotlib.lines.Line2D at 0x7f0c2768d6d8>]# B. ~. f+ Z, E' x. M
+ l0 ]' m s+ C7 c) U
. S& q- g- s$ N! N![]()
' @+ F5 \" E4 H9 | K& i
$ Y) J/ {9 F8 ^( h$ {
- Q7 k- F# Y; H实现SI模型的核心代码是第三个cell的第11,12行:5 Z1 o7 {3 M* o/ M" n; ~! A
8 v; C- l7 ]5 t( o. I, ^, P% o- {for t in range(T-1):/ F& H% Z$ Y$ A* E5 H# W F
i[t + 1] = i[t] + i[t] * lamda * (1.0 - i[t])1 j3 [. m+ q/ w# K- A
% L4 w% u V! H: t. U& [
就是我们建立的数学模型,利用python的for循环语句累加迭代的方式把每天的增加量叠加到感染者比例上。 运行代码完成计算,我们利用matplotlib的pyplot来画出感染者的随天数的变化曲线: 7 r7 L7 X5 @7 t* t+ L' ~/ U
fig, ax = plt.subplots(figsize=(8,4))
1 ?, j p+ m+ R' z2 Wax.plot(i, c='r', lw=2)' V* R' O) y+ E3 q% H( }
ax.set_xlabel('Day',fontsize=20)6 F+ F! s3 M1 B7 F- `
ax.set_ylabel('Infective Ratio', fontsize=20)3 a/ J; [ e2 w) [7 R
ax.grid(1)
* }; Y% O; E& L3 C& q$ e% Kplt.xticks(fontsize=20)
5 N# q4 F5 P. K3 eplt.yticks(fontsize=20);" y7 T3 _1 Y+ H# C
5 r9 e I' H; C7 c& n/ j5 n. P
: O5 d! @- V# |1 U* ]![]()
6 G8 `- q0 s7 ~* _
5 o. E8 G7 z, S* q7 j' f$ g从这个结果看到,大约在25天左右,全部人群都会变成感染者,感染率 。
+ ^: x2 `% w" e: g- h5 ?* R% ^在程序中我们假设每天每个患者传染0.8个人,你可以改变lamda的值,观察全部人群感染的天数的变化。
$ Y$ D7 l& D: C1 t- R. P认真思考你会知道,lamda的现实意义就是该城市的卫生水平,衡量的是消毒,隔离这些措施执行得怎么样。回到传染病模型,按照SI模型计算的结果,我们全人类都会患病,这好可怕!原因是我们忽略了一个很重要的因素,那就是我们有奋斗在一线的医护人员,我们会被治愈!所以SI模型只适合研究具有高传染风险又不能被治愈的病(比如HIV)。 但是对于其他病,我们是可以靠医疗和自身免疫系统康复的,那么紧接着的一个问题就是,被治愈后还会再被传染上嘛?根据这个问题的回答不同,我们有了两个不同的模型,SIR 和 SIS。现在可以揭晓,SIR的R的含义了,就是移出者(Removed),现实含义就是指被治愈后不会再被感染的人。而SIS表示治愈后仍然还是易感者。下面我们用python来分别实现这两个模型。 SIS模型为了实现这个模型,我们需要引入新的一个参数,治愈率 。好啦,先上我们的新示意图: Image Name和SI模型做比较,区别就是计算感染者的增加数时要减去被治愈的人数。% d# G/ H9 i7 N- M; M& q
所以这时候每天的增加的感染者为: ,& v7 H# s' o0 v( g
增加的感染率为: 。
" \& W# u4 q1 m" ?3 Q模型完成啦,修改python代码:
% H$ k1 _% k$ S. N, y) H# susceptiable ratio
* G% `( W, x" Z5 `5 ts = np.zeros([T])4 }7 o X9 Z0 B" W1 w
# infective ratio
4 R+ `/ _$ D1 ]5 @% ai = np.zeros([T]): y# A6 @) B" A Z! U
0 j% R& ?6 H8 W; v
# contact rate
4 X& u" M. z/ mlamda = 1.0
4 |. u, Y. D$ Q( ^1 e1 c: G o# x" S$ w# recover rate
4 v, v& F! x1 z5 q0 a% X3 rgamma = 0.5 ' f/ p$ P, Q) ]% J
; N7 {! R, ^9 g6 y* R9 x# initial infective people# x+ a# q) G4 _$ P* ]/ ~) P0 o
i[0] = 45.0 / N
. g$ Y4 k1 n J9 a: s: \9 ~' @/ P9 O7 M9 J
for t in range(T-1):
* j7 Z/ D( J7 y+ K g6 {5 [ i[t + 1] = i[t] + i[t] * lamda * (1.0 - i[t]) - gamma*i[t]
2 g( H: K9 M/ W8 K
# ?$ y- d# v& v, `3 n- i( J7 S9 G7 ?# |* K8 S* i
6 ^, U) @6 [3 S" B6 g, d运行代码,我们画出曲线(代码和SI模型的画图完全一样):1 `$ H# n* i3 Y& ~1 c5 S" o+ K- m
; W0 E$ O7 L* n& xfig, ax = plt.subplots(figsize=(8,4))
5 z" E) U! F+ Rax.plot(i, c='r', lw=2)2 f! ]1 }6 y3 K+ ^# _
ax.set_xlabel('Day',fontsize=20)9 }. [8 @! e& S- N1 Y( C
ax.set_ylabel('Infective Ratio', fontsize=20): d. T* z* }( {9 H
ax.grid(1)/ y! B8 P. h/ |" V) T
plt.xticks(fontsize=20)
# @ C% H- B' C! A1 O N- Gplt.yticks(fontsize=20);
# n+ p* Y+ L- U. l; u$ G
' I4 i( p8 {+ c# P( b![]()
8 A7 z) `; S$ P- R
' q3 `% k) r O' H- ?4 v
! O4 j. k: M: D& \# ?行代码,我们画出曲线(代码和SI模型的画图完全一样)
) @9 q7 X2 S* r3 M' l% Y: ~( K' R可以看到,达到最大感染率的时间退后10天左右,最后感染和治愈达到动态平衡,人群中有始终有一半的人感染着。所以,SIS模型适合研究具有传染性和反复性的流行病,比如常见流感。同样的,感兴趣的话,改变lamda和gamma的值,观察曲线的变化。和lamda不同的是,gamma的现实意义就是对这种疾病的治疗水平。 SIR模型加入了移出者,被治愈的病人不会再被传染,先上我们的新示意图: Image NameSIR 模型
N6 ^& Y/ A( _+ @注意到这里,人群被分成了三类,不再只有I和S,所以相比于之前的模型,我们需要找到新的约束关系。现在我们需要分别计算三种人每天的增加量了: * m( K. A# N7 A1 l
- 易感者:每天都在被传染,所以一直在减少,减少量为被传染的人数:
- 感染者:增加了被感染的人,减少了治愈的人:
- 移出者:增加了治愈的人: 3 K. k" M* r: C6 }& [6 C; {, a" n
建模完成,修改python代码,并且假设人群普遍易感,新型疾病,初始没有移出者。 7 b3 }. w M0 r6 O
# population9 P, `1 `( T$ y$ y
N = 1e7 + 10 + 5; Q8 g) o* B1 {- C/ C
# simuation Time / Day
: _5 M5 S9 u9 R: z i( l- f( R( CT = 170, v; J% K. G4 n" w1 U
# susceptiable ratio
8 D' ~' \8 f0 U4 L- {- C$ xs = np.zeros([T])
' |/ ~9 r: m2 a6 p9 l# infective ratio2 N: O6 l, C! H2 q! ~6 L
i = np.zeros([T])
: u; E7 ]. W% C( Y8 ~* A# y( D# remove ratio
; G/ n0 y- W8 m+ q- R x7 Br = np.zeros([T])
3 T1 _) q3 i4 B; o5 n! y+ y; f) ]+ Q B9 c- {7 b: q: |3 b
# contact rate- l2 g8 r. h6 t: H, m
lamda = 0.2586( z# S8 v- c( b- D, x2 Y
# recover rate- B6 O3 I4 U- ?) W1 Y! j4 j3 k1 I, F
gamma = 0.08216 |( N* `* ^8 o& O7 d) v
/ ]& s, G5 y% i( x/ W" q1 G0 o+ w# initial infective people
/ Q) g, t0 H1 Y2 O$ c2 ?i[0] = 10.0 / N+ O: Y2 T4 L- {" n# L5 `- I
s[0] = 1e7 / N
4 @' _) i0 o& {0 pfor t in range(T-1):
% j( H( N! w: O( @, a' x, e5 y i[t + 1] = i[t] + i[t] * lamda * s[t] - gamma*i[t] c! D! o. T8 q' X8 {, W$ _' Y
s[t + 1] = s[t] - lamda * s[t] * i[t]
9 a# A y* z8 V! T3 q, f r[t + 1] = r[t] + gamma*i[t]
. @% I* |8 ^/ p. k# g0 d( J
- Y6 Q4 R( E2 _7 {fig, ax = plt.subplots(figsize=(10,6)) t0 l; @ D4 @: n. }/ N+ ]
ax.plot(s, c='b', lw=2, label='S')1 T C, z6 r/ N' f( l( }9 J& f
ax.plot(i, c='r', lw=2, label='I')2 B) g: p# o" G5 B. m9 r
ax.plot(r, c='g', lw=2, label='R')
1 n) m' G3 Y% i& c* Dax.set_xlabel('Day',fontsize=20)
0 ^; C V: E6 v" q8 \$ U$ l5 fax.set_ylabel('Infective Ratio', fontsize=20)
. i/ M$ O! c: Z) f3 q8 G- zax.grid(1)
- x& @% J5 O% t' jplt.xticks(fontsize=20)
" e0 }4 u0 s/ T0 Wplt.yticks(fontsize=20): o4 ]6 `/ `7 M5 Q
plt.legend();
o, V0 e' M6 c* d9 g3 d- Q8 C' X$ I$ I" G. n+ `4 u5 a' R
8 _! P9 e0 k* m( V& d' i5 `( J
5 X7 E3 w8 C( }2 P
4 @0 o4 R9 ?- b# Q) e感染人数峰值发生在一个月左右,最大感染人数不到人群的20%, 但是最终人群的80%都会得此病(就是最终的移出者的比例)。SIR模型适合研究没有潜伏期的急性传染病,治疗后能够痊愈并具有抗病性。 到这里,虽然不准确,我们也可以先用SIR模型来分析一下此次疫情,武汉新型冠状病毒的传染病动力学! 模型有了,其实就是确定参数的问题。一开始就有人做了这个工作: Image Name于教授给的参数是参考了非典的, ,初始易感人数为一千万, 初始感染10人,初始移出者5人,那么我们的城市总人数 , 带入我们的模型得到结果:重现于教授的模型+ q3 p- S b: g- @% e
高峰和尾声日期的推测基本相符。
U3 V' d9 w3 f. s: S, M, O# susceptiable ratio
0 l2 o( h( {6 Ps = np.zeros([T])
, }$ A9 z' F# [& N" x# infective ratio
2 k9 c8 S. ]! h" S; d% gi = np.zeros([T])
[& E: g$ S: c* t6 ~ p# removed ratio6 J8 h7 g" N& M: X9 S& R
r = np.zeros([T])
* `" v7 D" X+ O1 C8 k! h- y4 G* l7 {# t6 y9 M% J
# birth ratio/ l2 h [, ~5 H4 b `
b = 20.0 / N
% a4 u3 a) `6 ~! S D# death ratio+ p( {8 v( H% o& J) D) q
d = 10.0 / N
8 G! U6 v& r* a( K7 [; F6 v, x. h/ Z
# contact rate/ {: Z b" v; {4 X
y = 1.5: W5 B3 {# K; ^
# recover rate
5 P% Z j' Y: A! k% @u = 0.8 # 1 / infective_period
; ], M, O7 y$ m, Q7 U1 Y
% D. _- _2 f4 _5 N# sigma = y / u- z8 `9 @( D7 m) W
% @: R9 y1 L2 O, P( ]; K# initial infective people
1 R3 h/ U4 q7 P4 ~i[0] = 45.0 / N5 G/ n9 h3 O: R
s[0] = 1 - i[0]
6 o- ]& M# K! y4 s N* L( lfor t in range(T-1):( t, F- C6 A# W$ d# q
i[t+1] = i[t] + i[t] * y * s[t] - u*i[t] - d*i[t]: w5 \& r8 o' c ~' m' S
s[t+1] = s[t] - y * s[t] * i[t] + b - d*s[t]
2 v: A9 v4 s7 u7 |( c ]/ r r[t+1] = r[t] + u*i[t] - d*r[t]; i$ M; e4 \; \. q; ?. [0 j& A
5 B; d% p5 i2 R# p8 \
plt.plot(i). { v( e) D8 u9 |6 b
plt.plot(s)
. C. z0 s& R2 z2 L/ F5 N$ L: g8 yplt.plot(r)& `2 h$ j: ~. t- ^! b* a( ^, ^
plt.plot(np.diff(i),ls='--')+ q* k, P, Z, P" M% J
! \- E: A2 l, X4 z- [: D- s
& F3 o* B0 x7 }+ t2 z[<matplotlib.lines.Line2D at 0x7f77796e8518>]
1 S5 H- {3 U2 x/ b. R% ~
! e( q% Y& K& T7 r* z 7 j7 S: W$ g& S0 R. P+ B
7 \4 E1 C4 Y. {) \( {4 H
SEIR模型但是,SIR模型和实际情况的出入会比较大,因为忽略了太多因素了,比如说潜伏期,比如说政策调控,药物,出生死亡等等。下面我们可以和前面一样,把潜伏期考虑进去,新增一个人群,叫潜伏者E(exposed): Image NameSEIR模型
& _( y7 n2 j7 k$ ?% E; ^' ~. Y! `同样的我们需要计算各人群每天的增加量: S:每天减少: 3 m8 Y7 G! E' z1 ]0 i
E:每天增加传染,减少发病:
5 u( h/ c" z% h% J+ xI:每天增加发病,减少治愈:
) J! `# a7 d. R fR:每天增加治愈:
0 y2 p$ Q7 b2 F8 v3 N% m建模完成,修改我们的python程序,这里的 可以理解为潜伏期的倒数。给的4天。新型冠状病毒给目前临床的潜伏期是3-14天。" C6 {! E$ V2 H8 H
# population
3 F9 x. Z* C$ @ y/ `4 ?* @, {N = 1e7 + 10 + 5 j2 k: z+ _% v4 R
# simuation Time / Day) L: |( H! h- P, z8 e$ p9 J# q
T = 170+ M0 ]( n) ?4 Y. g0 y, z9 H9 K
# susceptiable ratio& E4 n& t0 p) G3 A
s = np.zeros([T])9 [. m) \$ I" Y6 w5 ?) a& m4 D
# exposed ratio
3 K2 \* N* P* K9 p- @, ee = np.zeros([T])2 v( W" Q. }3 R( c1 G
# infective ratio
2 S: I5 U3 s1 b0 n8 _i = np.zeros([T])
/ W, ?% N- M* f% b4 A, h- e# remove ratio E# z1 f& r" I ^+ z
r = np.zeros([T])
, L, A9 U: L X: }4 s1 e) I6 n6 s$ C" l; {) U2 E6 x
# contact rate( Y* y& F0 h4 d/ A, W
lamda = 0.5
. F' I: {2 [- d3 p" E% A3 f# recover rate
* m7 v* n5 C. Z% ugamma = 0.0821
; @' u* G6 W* h5 J3 P& _# exposed period
! K$ `+ J6 I, R; ssigma = 1 / 4
' q( Q/ X3 S4 f6 {; g4 @1 L+ y+ G7 f+ Q' P
# initial infective people
4 T! Z( F# z) b R! ~i[0] = 10.0 / N
/ P+ a+ f9 B, D+ e% I# |s[0] = 1e7 / N
4 e; b0 B& p, Re[0] = 40.0 / N% l' y r6 y# I& ]5 e- _' L
for t in range(T-1):
" X. ~/ X; i4 F% e' M: h: t s[t + 1] = s[t] - lamda * s[t] * i[t]. s- ]) B9 u8 F. @& Q
e[t + 1] = e[t] + lamda * s[t] * i[t] - sigma * e[t]
3 M; T5 R6 t. V0 D, O( Z' Q/ x i[t + 1] = i[t] + sigma * e[t] - gamma * i[t]
, q" Q5 F, j2 x) }3 p r[t + 1] = r[t] + gamma * i[t]
4 C/ _; h0 E' t0 C0 w
0 c7 y5 m1 T# m9 }, H/ {" @" X9 Y. ?, m l9 _
fig, ax = plt.subplots(figsize=(10,6))
5 R3 D7 k& O. iax.plot(s, c='b', lw=2, label='S')2 ~/ R" Z% R1 r4 o" m, D
ax.plot(e, c='orange', lw=2, label='E'). u7 p% o; |4 i7 M. p( R w+ F4 u$ ?
ax.plot(i, c='r', lw=2, label='I')7 U' U# d/ y1 A1 K
ax.plot(r, c='g', lw=2, label='R')) m7 k0 a8 M. @$ i
ax.set_xlabel('Day',fontsize=20)% Z8 f; @ f/ K
ax.set_ylabel('Infective Ratio', fontsize=20)
& q' O7 R; z, ]- a2 ?ax.grid(1)
) J6 Y7 ?3 j( Z* m9 Nplt.xticks(fontsize=20)7 s- i) s, X* f& N% l4 {) w! S
plt.yticks(fontsize=20)
( m5 ~2 D; J U5 C+ {$ Jplt.legend();
: h8 s) [! X( X7 Y8 { F3 X: a0 Q! b! i ]: L
* ?% H, W+ _2 s' e
![]()
1 Z$ _$ i4 K5 j" Y8 J9 A9 m1 K0 n1 O. }4 n _
按照模型的结果,此次疫情可能真的要持续到 三四月份。这个接触率 真的非常影响表现,模型给的是个常数,但是由于政府措施的原因,这应该是个变化的值。
. A. L& c% s, u/ _5 d# O" ?, p还有治愈率 也是。没有完美的模型,但是随着考虑因素的增多,就会越来越接近实际情况,从而指导政府的疫情方针政策的制定。
5 [; c, I3 s2 j0 u; x3 }/ v+ b: z! p3 M* w/ x% F$ _7 i
& E& v( J$ X( F* {
& p+ `: b+ H2 z8 G8 w
& l! G: m; x8 ^; B* c3 l. A |