-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
249 lines (220 loc) · 7.72 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
<!DOCTYPE HTML>
<html>
<head>
<title>SEIR モデル シミュレータ</title>
<link href="./js/c3.css" rel="stylesheet">
<script src="https://d3js.org/d3.v5.js"></script>
<script src="./js/c3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<h1>SEIR モデル シミュレータ</h1>
<div id="stage"></div>
<div id="app">
<div>初期無免疫者 S: <input v-model="Sinit" type="number"/></div>
<div>初期感染者 I: <input v-model="Iinit" type="number"/></div>
<div>基本再生産数 R0: <input v-model="R0" type="number"/>
<button type="button" v-on:click="clickR0muins">- 0.1</button>
<button type="button" v-on:click="clickR0plus">+ 0.1</button>
</div>
<div>感染率 beta: <input v-model="beta" disable="false"/></div>
<div>回復率 gamma: <input v-model="gamma" disable="false"/></div>
<div>潜伏期間 lp: <input v-model="lp" type="number"/></div>
<div>発症期間 ip: <input v-model="ip" type="number"/></div>
<div>検査後隔離 T: <input v-model.number="T" type="number"/>
<button type="button" v-on:click="clickTmuins">- 0.01</button>
<button type="button" v-on:click="clickTplus">+ 0.01</button>
</div>
<div>全体の期間 t_max: <input v-model="t_max" type="number"/></div>
<div><button type="button" v-on:click='clickCalc' class="btn">再計算</button></div>
</div>
<div>
<h2>使い方</h2>
<ul>
<li>初期免疫者(S)と初期感染者(I)を入力して「再計算」する</li>
<li>基本再生産数 R0 を 10から1.0に変化させてシミュレーションする</li>
</ul>
<h2>考察</h2>
<ul>
<li>基本再生産数(R0)を1に近づけることにより、発症者数(Infected)のピークを右にずらすことができる</li>
<li>新型コロナウィルスの R0 は 1.4~2.5程度だが、クラスターの発生(R0≒10程度)により急激に発症者数(Infected)が増える</li>
<li>発症者数(Infected)に対して検査(T:0.0~1.0)を行い隔離(免疫者扱いにする)。</li>
<li>このシミュレーションでは、全員検査(1.0)をしなくても、2割程度で発症者のピークを抑えられる。</li>
</ul>
<h2>コード</h2>
<ul>
<li><a href="https://github.com/moonmile/seir-model">https://github.com/moonmile/seir-model</a></li>
</ul>
</div>
</body>
<script>
</script>
<script>
var chart ;
/**
* チャート表示
*/
function dispChart(lst) {
var lst_s = ['Susceptible'] ;
var lst_e = ['Exposed'] ;
var lst_i = ['Infected'] ;
var lst_r = ['Recovered'] ;
for ( var i=0; i<lst.length; i++ ) {
lst_s.push(lst[i][0] );
lst_e.push(lst[i][1] );
lst_i.push(lst[i][2] );
lst_r.push(lst[i][3] );
}
if ( chart == null ) {
chart = c3.generate({
bindto: '#stage',
data: {
columns: [ lst_s, lst_e, lst_i, lst_r ]
}});
} else {
chart.load({
columns: [
lst_s,
lst_e,
lst_i,
lst_r,
]});
}
}
</script>
<script>
/*
var Sinit = 3000 // 無免疫者(総数)
var Einit = 0 // 潜伏期間
var Iinit = 5 // 発病者
var Rinit = 0 // 有免疫
var R0 = 10.0 // 基本再生産数
var lp = 14 // 潜伏期間
var ip = 7 // 発症期間
var D = R0*ip // 回復期間
var T = 0.0 // 検査で隔離
var alpha = 1/lp // 潜伏率
var gamma = 1/ip // 回復率
var beta = R0*gamma // 感染率
var lst = [] ;
*/
var vm = new Vue({
el: '#app',
data: {
// 初期値
Sinit: 3000, // 無免疫者(総数)
Einit: 0, // 潜伏期間者
Iinit: 5, // 発病者
Rinit: 0, // 免疫獲得者
R0: 2.5, // 基本再生産数
lp: 14, // 潜伏期間
ip: 7, // 発症期間
D: this.R0 * this.ip, // 回復期間
T: 0.0, // 検査で隔離
alpha: 1/this.lp, // 潜伏率
gamma: 1/this.ip, // 回復率
beta: this.R0 * this.gamma, // 感染率
t_max: 500, // 全体の日数
},
mounted: function() {
this.clickCalc();
},
methods: {
/**
* 差分の計算
*/
seir_eq: function(v,t,alpha,beta,gamma,N) {
S = v[0]
E = v[1]
I = v[2]
R = v[3]
ds = - beta * I / N * S // dS/dt = -βI/N*S
de = beta * I / N * S - alpha * E // dE/dt = βI/N*S-αE
di = alpha * E - gamma * I // dI/dt = αE - γI
dr = gamma * I // dR/dt = γI
return [ds,de,di,dr];
},
/**
* 全体の計算
*/
calc: function(state,alpha,beta,gamma) {
var dt = 1 ;
var lst = []
var N = Sinit + Einit + Iinit + Rinit
console.log( state );
for ( var i=0; i<this.t_max; i++ ) {
var d = this.seir_eq( state, i, alpha,beta,gamma, N )
var Si = state[0]+d[0]
var Ei = state[1]+d[1]
var Ii = state[2]+d[2]
var Ri = state[3]+d[3]
// 感染者を発見して隔離する
dx = Ii * T
Ii = Ii - dx
Ri = Ri + dx // 免疫者に加算
// マイナス値を調節する
if ( Si < 0 ) {
Ei = Ei + Si; Si = 0;
}
if ( Ei < 0 ) {
Ii = Ii + Ei; Ei = 0;
}
if ( Ii < 0 ) {
Ri = Ri + Ii; Ii = 0;
}
state = [ Si, Ei, Ii, Ri ]
// console.log( state );
lst.push( state );
}
return lst ;
},
clickCalc: function() {
Sinit = Number(this.Sinit) ;
Einit = 0
Iinit = Number(this.Iinit) ;
Rinit = 0
R0 = Number(this.R0) ;
lp = Number(this.lp) ;
ip = Number(this.ip) ;
this.D = D = R0*ip // 回復期間
T = Number(this.T);
this.alpha = alpha = 1/lp // 潜伏率
this.gamma = gamma = 1/ip // 回復率
this.beta = beta = R0*gamma // 感染率
console.log("R0: " + R0 );
console.log("beta: " + beta );
console.log("lp: " + lp );
console.log("ip: " + ip );
console.log("T: " + T );
var state = [Sinit,Einit,Iinit,Rinit];
var lst = this.calc(state,alpha,beta,gamma);
dispChart(lst);
},
clickTmuins: function() {
if ( 0.0 < this.T ) {
this.T = Math.floor((this.T - 0.011)*100)/100.0;
this.clickCalc();
}
},
clickTplus: function() {
if ( this.T < 1.0 ) {
this.T = Math.floor((this.T + 0.011)*100)/100.0;
console.log( this.T );
this.clickCalc();
}
},
clickR0muins: function() {
if ( 0.0 < this.R0 ) {
this.R0 = Math.floor((this.R0 - 0.11)*10)/10.0;
this.clickCalc();
}
},
clickR0plus: function() {
this.R0 = Math.floor((this.R0 + 0.11)*10)/10.0;
console.log( this.T );
this.clickCalc();
}
},
})
</script>
</html>