Testing frequency to ensure workplace safety
Background
In a population where there is constant threat of community infection, there is a need to test everyone to ensure workplace safety.
The goal of this simulation is to determine how many in the cohort would be infected with periodic testing with specified intervals.
Assumptions
- Risk of community spread is
20/10,000
to50/10,000
per day, default to 0.0022. - Cohort of size 10 to 40, with no virus at the beginning.
- Test everyone in the cohor either every week, every 3 days or every 2 weeks.
Simulations
The simulation could be performed with the following command
outbreak_simulator --rep 10000 --popsize {ps} --handle-symptomatic remove --stop-if 't>90' --logfile p{ps}_t{ti}.log \
--plugin community_infection --probability 0.005 --interval 1 \
--plugin testing --interval {ti} --proportion 1 --handle-positive remove \
--plugin stat --at 14 90
where
- Simulation will last 90 days.
- Plugin
community_infection
infects everyone at a given probability at given interval - Plugin
testing
tests everyone (proportion=1
) and remove infected individuals. - Plugin
stat
to output population statistics.
Here we use sos workflow
to execute simulations for population size (ps
) 10, 20, 30, 40, and testing frequency (ti
) 0 (no test at all), 3 (every 3 days), 7, and 14.
input: for_each=[dict(ps=[10, 20, 30, 40]), dict(ti=[0, 3, 7, 14])]
task: queue='localhost'
sh: expand=True, template_name='conda', env_name='ictr'
outbreak_simulator --rep 10000 --popsize {ps} --handle-symptomatic remove --stop-if 't>90' --logfile p{ps}_t{ti}.log \
--plugin community_infection --probability 0.0022 --interval 1 \
--plugin testing --interval {ti} --proportion 1 --handle-positive remove \
--plugin stat --at 14 90
Results
The log file of the simulations contains event STAT
with population size and INFECTION
for infections, and the by
parameter can be used to differentiate infection within the cohort (by a certain ID) or from community.
The following table shows
- Average number of uninfected: Mean remaining population size after 90 days.
- Std of uninfected: Standard deviation of remaining population size.
- proportion of none infected: Proportion of simulations with no infection at the end.
- Average number of community infection: Average number of community infections detected.
- Average number of within-cohort infection: Average number of within cohort infection.
%preview summary.csv
import pandas as pd
def param(data, name):
stat = data[(data['event'] == 'STAT') & (data['time'] == 90)]
return stat.apply(lambda x: int(x['params'].split(name+'=')[1].split(',')[0]), axis=1)
def infect(data):
infect = data[data['event'] == 'INFECTION']
by = infect.apply(lambda x: 'by' in x['params'], axis=1)
return infect.shape[0] - sum(by), sum(by)
with open('summary.csv', 'w') as sc:
sc.write(','.join([
'Cohort size',
'Community infection rate',
'Test frequency (0 for no test)',
'Average number of uninfected',
'Std of uninfected',
'Proportion of none infected',
'Average number of community infection',
'Average number of within-cohort infection']) + '\n')
for ps in [10, 20, 30, 40]:
for ti in [0, 3, 7, 14]:
data = pd.read_csv(f'p{ps}_t{ti}.log', sep='\t')
es = param(data, 'n_popsize') - param(data, 'n_infected')
pna = 100 * sum(es == ps) / 10000
community_infect, within_infect = infect(data)
sc.write(f'{ps}, {0.0022}, {ti}, {es.mean():.1f}, {es.std():.1f}, {pna:.2f}%, {community_infect/10000:.1f}, {within_infect/10000:.1f}\n')
Cohort size | Community infection rate | Test frequency (0 for no test) | Average number of uninfected | Std of uninfected | Proportion of none infected | Average number of community infection | Average number of within-cohort infection |
---|---|---|---|---|---|---|---|
10 | 0.0022 | 0 | 3.7 | 2.6 | 1.01% | 2.9 | 3.5 |
10 | 0.0022 | 3 | 5.9 | 1.8 | 1.14% | 3.6 | 0.5 |
10 | 0.0022 | 7 | 5.2 | 2.1 | 1.14% | 3.4 | 1.5 |
10 | 0.0022 | 14 | 4.5 | 2.4 | 1.15% | 3.1 | 2.4 |
20 | 0.0022 | 0 | 6.2 | 3.9 | 0.00% | 5.3 | 8.5 |
20 | 0.0022 | 3 | 11.8 | 2.5 | 0.02% | 7.1 | 1.2 |
20 | 0.0022 | 7 | 10.1 | 3.2 | 0.03% | 6.6 | 3.3 |
20 | 0.0022 | 14 | 8.3 | 3.7 | 0.00% | 6.1 | 5.6 |
30 | 0.0022 | 0 | 8.5 | 5.0 | 0.00% | 7.7 | 13.8 |
30 | 0.0022 | 3 | 17.6 | 3.1 | 0.00% | 10.6 | 1.9 |
30 | 0.0022 | 7 | 15.0 | 3.9 | 0.00% | 9.9 | 5.2 |
30 | 0.0022 | 14 | 12.0 | 4.8 | 0.00% | 9.0 | 9.0 |
40 | 0.0022 | 0 | 10.8 | 5.9 | 0.00% | 10.0 | 19.3 |
40 | 0.0022 | 3 | 23.4 | 3.6 | 0.00% | 14.2 | 2.5 |
40 | 0.0022 | 7 | 19.9 | 4.6 | 0.00% | 13.2 | 7.0 |
40 | 0.0022 | 14 | 15.9 | 5.6 | 0.00% | 11.9 | 12.3 |
Not surprisingly, the results show that
- Risk of infection increases with larger cohort size
- More frequent tests will reduce the number of within-cohort infection
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.