Social distancing and sampling effect

Background

This example tries to simulate a decent sized population with

  1. Effect of increased social distancing following government guidelines.
  2. Estimation of population incidence rate and seroprevalence through sampling.

Assumptions

  1. The population has size popsize in which everyone is equally susceptible.
  2. Initial seroprevalence is initial_seroprevalence, with initial_incidence_rate active cases.
  3. Incomplete social distancing in that while 80% of symptomatic cases are quarantined for 14-days, the rest of 20% of symptomatic cases are not, perhaps due to the mildness of symptoms.
  4. Starting from day 15, stronger social distancing was adviced, which reduces the effective production number of everyone by distancing.
  5. Although sampling in real world would be far less frequent, we sample sample_size individuals from the population every day to estimate the true incidence and seroprevalence of the population.

Simulation of sampling

# this is a papermill parameter cell.

# 50k population size
popsize = 50000
# 0.6% reported cases
initial_incidence_rate = 0.006
# assuming that seoprevalence is 5 times higher than incidence rate
initial_seroprevalence = 0.03
# effect of social distancing
distancing = 0.5
# 1% population size
sample_size = 500

This scenario can be simulated with the following command:

outbreak_simulator --popsize {popsize} --handle-symptomatic quarantine_14 0.8 --stop-if 't>45' --logfile distancing.log \
    --plugin init --at 0 --seroprevalence {seroprevalance} --incidence-rate {incidence_rate} --leadtime any \
    --plugin sample --interval 1 --size {sample_size} \
    --plugin stat --interval 1 \
    --plugin setparam --at 15 --symptomatic-r0 {1.4 * distancing} {2.8 * distancing} --asymptomatic-r0 {0.28 * distancing} {0.56 * distancing} \
    > distancing.txt

where plugins init and sample are used to initialize and sample from the population, setparam is used to set $R_0$ for sympatomatic and asymptomatic cases at day 15, and stat is to report true population incidence rate and seroprevalence every day.

%expand
outbreak_simulator --popsize {popsize} --handle-symptomatic quarantine_14 0.8 \
    --stop-if 't>45' --repeat 10 --logfile distancing.log \
    --plugin init --at 0 --leadtime any --seroprevalence {initial_seroprevalence} --incidence-rate {initial_incidence_rate} \
    --plugin setparam --at 15 --symptomatic-r0 {1.4 * distancing} {2.8 * distancing} --asymptomatic-r0 {0.28 * distancing} {0.56 * distancing} \
    --plugin sample --interval 1 --size {sample_size} \
    --plugin stat --interval 1 \
    > distancing.txt
100%|███████████████████████████████████████████| 10/10 [01:08<00:00,  6.81s/it]

Results

Although a large number of statistics such as number of affected, recovered, seroprevalence are reported, we extract the following statistics from the output of the simulations (distancing.txt) for this report:

  1. incidence_rate_0.00 to incidence_rate_45.00 as population incidence rate for 10 replicate simulations.
  2. sample_incidence_rate_0.00 to sample_incidence_rate_45.00 as sample incidence rate for 10 replicate simulations.
import pandas as pd
def get_seq(filename, field_name):
    result = {}
    with open(filename) as stat:
        for line in stat:
            if line.startswith(field_name):
                key, value = line.strip().split('\t')
                t = int(key[len(field_name)+1:].split('.')[0])
                if ':' in value:
                    value = eval('{' + value + '}')
                else:
                    value = {idx+1:value for idx, value in enumerate(eval(value))}
                result[t] = value

    return pd.DataFrame(result).transpose()[[x for x in range(1, 11)]]
%preview incidence_rate
incidence_rate = get_seq('distancing.txt', 'incidence_rate')
sample_incidence_rate = get_seq('distancing.txt', 'sample_incidence_rate')
> incidence_rate: DataFrame of shape (46, 10)
1 2 3 4 5 6 7 8 9 10
0 0.0060 0.0060 0.0060 0.0060 0.0060 0.0060 0.0060 0.0060 0.0060 0.0060
1 0.0056 0.0057 0.0055 0.0058 0.0058 0.0057 0.0059 0.0057 0.0059 0.0057
2 0.0056 0.0055 0.0053 0.0057 0.0058 0.0055 0.0059 0.0058 0.0058 0.0056
3 0.0057 0.0054 0.0054 0.0055 0.0058 0.0057 0.0060 0.0058 0.0058 0.0057
4 0.0055 0.0056 0.0053 0.0052 0.0057 0.0059 0.0061 0.0057 0.0060 0.0059
5 0.0059 0.0055 0.0053 0.0050 0.0058 0.0060 0.0060 0.0058 0.0061 0.0060
6 0.0059 0.0057 0.0059 0.0054 0.0058 0.0060 0.0063 0.0061 0.0063 0.0065
7 0.0062 0.0057 0.0063 0.0052 0.0058 0.0064 0.0066 0.0060 0.0064 0.0068
8 0.0067 0.0058 0.0066 0.0055 0.0060 0.0065 0.0069 0.0063 0.0067 0.0072
9 0.0071 0.0059 0.0071 0.0055 0.0059 0.0067 0.0072 0.0065 0.0069 0.0077
10 0.0072 0.0062 0.0077 0.0056 0.0061 0.0068 0.0077 0.0065 0.0072 0.0080
11 0.0074 0.0064 0.0086 0.0058 0.0062 0.0071 0.0082 0.0066 0.0080 0.0084
12 0.0076 0.0067 0.0096 0.0063 0.0065 0.0075 0.0086 0.0067 0.0082 0.0090
13 0.0082 0.0070 0.0106 0.0069 0.0067 0.0080 0.0091 0.0070 0.0085 0.0096
14 0.0087 0.0072 0.0116 0.0074 0.0071 0.0085 0.0097 0.0073 0.0089 0.0101
15 0.0091 0.0074 0.0125 0.0077 0.0073 0.0089 0.0098 0.0076 0.0091 0.0103
16 0.0094 0.0077 0.0133 0.0079 0.0072 0.0089 0.0100 0.0078 0.0093 0.0106
17 0.0094 0.0078 0.0142 0.0085 0.0072 0.0093 0.0103 0.0076 0.0093 0.0110
18 0.0096 0.0080 0.0148 0.0086 0.0071 0.0096 0.0103 0.0076 0.0094 0.0110
19 0.0097 0.0080 0.0154 0.0084 0.0070 0.0095 0.0103 0.0073 0.0093 0.0107
20 0.0095 0.0083 0.0158 0.0082 0.0068 0.0095 0.0100 0.0070 0.0091 0.0107
21 0.0092 0.0080 0.0159 0.0079 0.0064 0.0093 0.0097 0.0067 0.0087 0.0102
22 0.0091 0.0074 0.0154 0.0078 0.0062 0.0090 0.0095 0.0063 0.0084 0.0099
23 0.0087 0.0071 0.0152 0.0076 0.0058 0.0087 0.0089 0.0060 0.0081 0.0090
24 0.0086 0.0070 0.0148 0.0071 0.0054 0.0080 0.0083 0.0056 0.0079 0.0085
25 0.0080 0.0066 0.0140 0.0067 0.0050 0.0074 0.0077 0.0050 0.0074 0.0079
26 0.0076 0.0062 0.0133 0.0063 0.0044 0.0068 0.0070 0.0044 0.0067 0.0073
27 0.0070 0.0056 0.0126 0.0057 0.0039 0.0062 0.0066 0.0038 0.0063 0.0067
28 0.0063 0.0052 0.0118 0.0051 0.0035 0.0055 0.0061 0.0034 0.0057 0.0059
29 0.0058 0.0047 0.0107 0.0047 0.0032 0.0047 0.0054 0.0030 0.0052 0.0051
30 0.0052 0.0040 0.0098 0.0042 0.0026 0.0043 0.0048 0.0025 0.0046 0.0045
31 0.0046 0.0033 0.0087 0.0039 0.0024 0.0037 0.0043 0.0021 0.0041 0.0040
32 0.0043 0.0030 0.0078 0.0035 0.0020 0.0030 0.0038 0.0017 0.0037 0.0037
33 0.0040 0.0027 0.0073 0.0032 0.0018 0.0027 0.0035 0.0015 0.0034 0.0032
34 0.0035 0.0024 0.0067 0.0026 0.0016 0.0022 0.0033 0.0013 0.0030 0.0027
35 0.0032 0.0021 0.0060 0.0024 0.0013 0.0019 0.0030 0.0011 0.0025 0.0024
36 0.0028 0.0018 0.0055 0.0021 0.0011 0.0016 0.0025 0.0009 0.0022 0.0020
37 0.0026 0.0015 0.0050 0.0019 0.0010 0.0014 0.0023 0.0008 0.0018 0.0017
38 0.0024 0.0014 0.0044 0.0016 0.0009 0.0013 0.0022 0.0007 0.0016 0.0014
39 0.0022 0.0013 0.0041 0.0015 0.0007 0.0011 0.0019 0.0006 0.0014 0.0012
40 0.0020 0.0011 0.0038 0.0013 0.0005 0.0009 0.0016 0.0005 0.0012 0.0010
41 0.0019 0.0010 0.0037 0.0013 0.0004 0.0008 0.0013 0.0004 0.0010 0.0009
42 0.0017 0.0009 0.0034 0.0012 0.0003 0.0006 0.0012 0.0004 0.0009 0.0007
43 0.0015 0.0008 0.0032 0.0010 0.0002 0.0006 0.0011 0.0003 0.0007 0.0007
44 0.0013 0.0007 0.0029 0.0008 0.0002 0.0005 0.0010 0.0003 0.0007 0.0006
45 0.0012 0.0006 0.0027 0.0007 0.0002 0.0005 0.0008 0.0003 0.0006 0.0005

Incidence rates

The following figure shows the change of incidence rate for 10 replicate simulations. As you can see, the incidence rates increase rapidly at first. After day 15, due to the increase of social distancing, reflected by lowered production number, the population incidence rates start to decline after a few days of lag.

incidence_rate.plot(xlabel='days', ylabel='incidence rate')

png

The following figure shows sample incidence rates from 3 of the simulations, estimated from sampling 10% of the population (~ 500 people). Due to the overall low incidence rate, the estimates from the samples vary greatly.

sample_incidence_rate[[1, 2, 3]].plot(xlabel='days', ylabel='sample incidence rate')

png

Availability

This notebook is available under the Applications directory of the GitHub repository of the COVID19 Outbreak Simulator. It can be executed with sos-papermill with the following parameters, or using a docker image bcmictr/outbreak-simulator-notebook as described in here.