@@ -70,7 +70,7 @@ def __init__(self, p: Parameters):
70
70
71
71
self .i_day = 0 # seed to the full length
72
72
self .run_projection (p , [(self .beta , p .n_days )])
73
- self .i_day = i_day = int (get_argmin_ds (self .census_df , p .current_hospitalized ))
73
+ self .i_day = i_day = int (get_argmin_ds (self .raw [ "census_hospitalized" ] , p .current_hospitalized ))
74
74
75
75
self .run_projection (p , self .gen_policy (p ))
76
76
@@ -120,6 +120,13 @@ def __init__(self, p: Parameters):
120
120
)
121
121
raise AssertionError ('doubling_time or date_first_hospitalized must be provided.' )
122
122
123
+ self .raw ["date" ] = self .raw ["day" ].astype ("timedelta64[D]" ) + np .datetime64 (p .current_date )
124
+
125
+ self .raw_df = pd .DataFrame (data = self .raw )
126
+ self .dispositions_df = self .raw_df
127
+ self .admits_df = self .raw_df
128
+ self .census_df = self .raw_df
129
+
123
130
logger .info ('len(np.arange(-i_day, n_days+1)): %s' , len (np .arange (- self .i_day , p .n_days + 1 )))
124
131
logger .info ('len(raw_df): %s' , len (self .raw_df ))
125
132
@@ -139,9 +146,9 @@ def __init__(self, p: Parameters):
139
146
140
147
self .sim_sir_w_date_df = build_sim_sir_w_date_df (self .raw_df , p .current_date , self .keys )
141
148
142
- self .sim_sir_w_date_floor_df = build_floor_df (self .sim_sir_w_date_df , self .keys )
143
- self .admits_floor_df = build_floor_df (self .admits_df , p .dispositions .keys ())
144
- self .census_floor_df = build_floor_df (self .census_df , p .dispositions .keys ())
149
+ self .sim_sir_w_date_floor_df = build_floor_df (self .sim_sir_w_date_df , self .keys , "" )
150
+ self .admits_floor_df = build_floor_df (self .admits_df , p .dispositions .keys (), "admits_" )
151
+ self .census_floor_df = build_floor_df (self .census_df , p .dispositions .keys (), "census_" )
145
152
146
153
self .daily_growth_rate = get_growth_rate (p .doubling_time )
147
154
self .daily_growth_rate_t = get_growth_rate (self .doubling_time_t )
@@ -156,7 +163,7 @@ def get_argmin_doubling_time(self, p: Parameters, dts):
156
163
self .run_projection (p , self .gen_policy (p ))
157
164
158
165
# Skip values the would put the fit past peak
159
- peak_admits_day = self .admits_df . hospitalized .argmax ()
166
+ peak_admits_day = self .raw [ "admits_hospitalized" ] .argmax ()
160
167
if peak_admits_day < 0 :
161
168
continue
162
169
@@ -186,7 +193,7 @@ def gen_policy(self, p: Parameters) -> Sequence[Tuple[float, int]]:
186
193
]
187
194
188
195
def run_projection (self , p : Parameters , policy : Sequence [Tuple [float , int ]]):
189
- self .raw_df = sim_sir_df (
196
+ self .raw = sim_sir (
190
197
self .susceptible ,
191
198
self .infected ,
192
199
p .recovered ,
@@ -195,23 +202,24 @@ def run_projection(self, p: Parameters, policy: Sequence[Tuple[float, int]]):
195
202
policy
196
203
)
197
204
198
- self .dispositions_df = build_dispositions_df (self .raw_df , self .rates , p .market_share , p .current_date )
199
- self .admits_df = build_admits_df (self .dispositions_df )
200
- self .census_df = build_census_df (self .admits_df , self .days )
201
- self .current_infected = self .raw_df .infected .loc [self .i_day ]
205
+ calculate_dispositions (self .raw , self .rates , p .market_share )
206
+ calculate_admits (self .rates , self .raw )
207
+ calculate_census (self .raw , self .days )
208
+
209
+ self .current_infected = self .raw ["infected" ][self .i_day ]
202
210
203
211
def get_loss (self ) -> float :
204
212
"""Squared error: predicted vs. actual current hospitalized."""
205
- predicted = self .census_df . hospitalized . loc [self .i_day ]
213
+ predicted = self .raw [ "census_hospitalized" ] [self .i_day ]
206
214
return (self .current_hospitalized - predicted ) ** 2.0
207
215
208
216
209
- def get_argmin_ds (census_df : pd . DataFrame , current_hospitalized : float ) -> float :
217
+ def get_argmin_ds (census , current_hospitalized : float ) -> float :
210
218
# By design, this forbids choosing a day after the peak
211
219
# If that's a problem, see #381
212
- peak_day = census_df . hospitalized .argmax ()
213
- losses_df = (census_df . hospitalized [:peak_day ] - current_hospitalized ) ** 2.0
214
- return losses_df .argmin ()
220
+ peak_day = census .argmax ()
221
+ losses = (census [:peak_day ] - current_hospitalized ) ** 2.0
222
+ return losses .argmin ()
215
223
216
224
217
225
def get_beta (
@@ -259,31 +267,56 @@ def sir(
259
267
260
268
def gen_sir (
261
269
s : float , i : float , r : float , gamma : float , i_day : int , policies : Sequence [Tuple [float , int ]]
262
- ) -> Generator [ Tuple [ int , float , float , float ], None , None ] :
270
+ ):
263
271
"""Simulate SIR model forward in time yielding tuples.
264
272
Parameter order has changed to allow multiple (beta, n_days)
265
273
to reflect multiple changing social distancing policies.
266
274
"""
267
275
s , i , r = (float (v ) for v in (s , i , r ))
268
276
n = s + i + r
269
277
d = i_day
278
+
279
+ total_days = 1
280
+ for beta , days in policies :
281
+ total_days += days
282
+
283
+ d_a = np .empty (total_days , "int" )
284
+ s_a = np .empty (total_days , "float" )
285
+ i_a = np .empty (total_days , "float" )
286
+ r_a = np .empty (total_days , "float" )
287
+
288
+ index = 0
270
289
for beta , n_days in policies :
271
290
for _ in range (n_days ):
272
- yield d , s , i , r
291
+ d_a [index ] = d
292
+ s_a [index ] = s
293
+ i_a [index ] = i
294
+ r_a [index ] = r
295
+ index += 1
296
+
273
297
s , i , r = sir (s , i , r , beta , gamma , n )
274
298
d += 1
275
- yield d , s , i , r
276
299
300
+ d_a [index ] = d
301
+ s_a [index ] = s
302
+ i_a [index ] = i
303
+ r_a [index ] = r
304
+ return {
305
+ "day" : d_a ,
306
+ "susceptible" : s_a ,
307
+ "infected" : i_a ,
308
+ "recovered" : r_a ,
309
+ "ever_infected" : i_a + r_a
310
+ }
277
311
278
- def sim_sir_df (
312
+
313
+ def sim_sir (
279
314
s : float , i : float , r : float ,
280
315
gamma : float , i_day : int , policies : Sequence [Tuple [float , int ]]
281
316
) -> pd .DataFrame :
282
317
"""Simulate the SIR model forward in time."""
283
- return pd .DataFrame (
284
- data = gen_sir (s , i , r , gamma , i_day , policies ),
285
- columns = ("day" , "susceptible" , "infected" , "recovered" ),
286
- )
318
+ data = gen_sir (s , i , r , gamma , i_day , policies )
319
+ return data
287
320
288
321
289
322
def build_sim_sir_w_date_df (
@@ -302,58 +335,50 @@ def build_sim_sir_w_date_df(
302
335
})
303
336
304
337
305
- def build_floor_df (df , keys ):
338
+ def build_floor_df (df , keys , prefix ):
306
339
"""Build floor sim sir w date."""
307
340
return pd .DataFrame ({
308
341
"day" : df .day ,
309
342
"date" : df .date ,
310
343
** {
311
- key : np .floor (df [key ])
344
+ prefix + key : np .floor (df [prefix + key ])
312
345
for key in keys
313
346
}
314
347
})
315
348
316
349
317
- def build_dispositions_df (
318
- raw_df : pd . DataFrame ,
350
+ def calculate_dispositions (
351
+ raw : Dict ,
319
352
rates : Dict [str , float ],
320
353
market_share : float ,
321
- current_date : datetime ,
322
- ) -> pd .DataFrame :
354
+ ):
323
355
"""Build dispositions dataframe of patients adjusted by rate and market_share."""
324
- patients = raw_df .infected + raw_df .recovered
325
- day = raw_df .day
326
- return pd .DataFrame ({
327
- "day" : day ,
328
- "date" : day .astype ('timedelta64[D]' ) + np .datetime64 (current_date ),
329
- ** {
330
- key : patients * rate * market_share
331
- for key , rate in rates .items ()
332
- }
333
- })
356
+ for key , rate in rates .items ():
357
+ raw ["ever_" + key ] = raw ["ever_infected" ] * rate * market_share
358
+ raw [key ] = raw ["ever_infected" ] * rate * market_share
334
359
335
360
336
- def build_admits_df ( dispositions_df : pd . DataFrame ) -> pd . DataFrame :
361
+ def calculate_admits ( rates , raw : Dict ) :
337
362
"""Build admits dataframe from dispositions."""
338
- admits_df = dispositions_df - dispositions_df .shift (1 )
339
- admits_df .day = dispositions_df .day
340
- admits_df .date = dispositions_df .date
341
- return admits_df
363
+ for key in rates .keys ():
364
+ ever = raw ["ever_" + key ]
365
+ admit = np .empty_like (ever )
366
+ admit [0 ] = np .nan
367
+ admit [1 :] = ever [1 :] - ever [:- 1 ]
368
+ raw ["admits_" + key ] = admit
369
+ raw [key ] = admit
342
370
343
371
344
- def build_census_df (
345
- admits_df : pd . DataFrame ,
372
+ def calculate_census (
373
+ raw : Dict ,
346
374
lengths_of_stay : Dict [str , int ],
347
- ) -> pd . DataFrame :
375
+ ):
348
376
"""Average Length of Stay for each disposition of COVID-19 case (total guesses)"""
349
- return pd .DataFrame ({
350
- 'day' : admits_df .day ,
351
- 'date' : admits_df .date ,
352
- ** {
353
- key : (
354
- admits_df [key ].cumsum ()
355
- - admits_df [key ].cumsum ().shift (los ).fillna (0 )
356
- )
357
- for key , los in lengths_of_stay .items ()
358
- }
359
- })
377
+ n_days = raw ["day" ].shape [0 ]
378
+ for key , los in lengths_of_stay .items ():
379
+ cumsum = np .empty (n_days + los )
380
+ cumsum [:los + 1 ] = 0.0
381
+ cumsum [los + 1 :] = raw ["admits_" + key ][1 :].cumsum ()
382
+
383
+ census = cumsum [los :] - cumsum [:- los ]
384
+ raw ["census_" + key ] = census
0 commit comments