Skip to content

Commit

Permalink
Merge pull request #490 from amath-idm/fix-webapp-plotting
Browse files Browse the repository at this point in the history
Fix webapp plotting
  • Loading branch information
cliffckerr authored May 9, 2020
2 parents 9a6c23b + 682ee59 commit 1e08cc9
Show file tree
Hide file tree
Showing 22 changed files with 347 additions and 140 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ What's new

All notable changes to the codebase are documented in this file. Note: in many cases, changes from multiple patch versions are grouped together, so numbering will not be strictly consecutive.

Version 1.0.1 (2020-05-09)
--------------------------
- Added argument ``as_date`` for ``sim.date()`` to return a ``datetime`` object instead of a string.
- Fixed plotting of interventions in the webapp.
- Removed default 1-hour time limit for simulations.
- GitHub info: PR `490 <https://github.com/amath-idm/covasim/pull/490>`__, previous head ``9a6c23b``


Version 1.0.0 (2020-05-08)
--------------------------
Expand Down
37 changes: 18 additions & 19 deletions covasim/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Parameters
==========

This file describes the expected behavior of each parameter in the model. Note: the term "overall infection rate" can be explored using ``sim.results['doubling_time']`` and ``sim.results['r_eff']`` (a higher infection rate means lower doubling times and higher *R\_eff*), as well as by simply looking at the epidemic curves.
This file describes each of the input parameters in Covasim. Note: the overall infection rate can be explored using ``sim.results['doubling_time']`` and ``sim.results['r_eff']`` (a higher infection rate means lower doubling times and higher *R\_eff*), as well as by simply looking at the epidemic curves.

Population parameters
---------------------
Expand Down Expand Up @@ -33,30 +33,29 @@ Basic disease transmission
* ``dynam_layer`` = Which layers are dynamic; set below
* ``beta_layer`` = Transmissibility per layer; set below
* ``n_imports`` = Average daily number of imported cases (actual number is drawn from Poisson distribution)
* ``beta_dist`` = Distribution to draw individual level transmissibility
* ``viral_dist`` = The time varying viral load (transmissibility)
* ``beta_dist`` = Distribution to draw individual level transmissibility; see https://wellcomeopenresearch.org/articles/5-67
* ``viral_dist`` = The time varying viral load (transmissibility); estimated from Lescure 2020, Lancet, https://doi.org/10.1016/S1473-3099(20)30200-0

Efficacy of protection measures
-------------------------------
* ``asymp_factor`` = Multiply beta by this factor for asymptomatic cases
* ``diag_factor`` = Multiply beta by this factor for diganosed cases
* ``asymp_factor`` = Multiply beta by this factor for asymptomatic cases; no statistically significant difference in transmissibility: https://www.sciencedirect.com/science/article/pii/S1201971220302502
* ``diag_factor`` = Multiply beta by this factor for diganosed cases; based on intervention strength
* ``quar_eff`` = Quarantine multiplier on transmissibility and susceptibility; set below
* ``quar_period`` = Number of days to quarantine for
* ``quar_period`` = Number of days to quarantine for; assumption based on standard policies

Time for disease progression
----------------------------
* ``exp2inf`` = Duration from exposed to infectious
* ``inf2sym`` = Duration from infectious to symptomatic
* ``sym2sev`` = Duration from symptomatic to severe symptoms
* ``sev2crit`` = Duration from severe symptoms to requiring ICU
* ``exp2inf`` = Duration from exposed to infectious; see Lauer et al., https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7081172/, subtracting inf2sim duration
* ``inf2sym`` = Duration from infectious to symptomatic; see Linton et al., https://doi.org/10.3390/jcm9020538
* ``sym2sev`` = Duration from symptomatic to severe symptoms; see Linton et al., https://doi.org/10.3390/jcm9020538Duration from severe symptoms to requiring ICU; see Wang et al., https://jamanetwork.com/journals/jama/fullarticle/2761044Duration from severe symptoms to requiring ICU

Time for disease recovery
-------------------------
* ``asym2rec`` = Duration for asymptomatics to recover
* ``mild2rec`` = Duration from mild symptoms to recovered
* ``sev2rec`` = Duration from severe symptoms to recovered
* ``crit2rec`` = Duration from critical symptoms to recovered
* ``crit2die`` = Duration from critical symptoms to death
* ``asym2rec`` = Duration for asymptomatic people to recover; see Wölfel et al., https://www.nature.com/articles/s41586-020-2196-x
* ``mild2rec`` = Duration for people with mild symptoms to recover; see Wölfel et al., https://www.nature.com/articles/s41586-020-2196-x
* ``sev2rec`` = Duration for people with severe symptoms to recover, 22.6 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf
* ``crit2rec`` = Duration for people with critical symptoms to recover, 22.6 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf
* ``crit2die`` = Duration from critical symptoms to death, 17.8 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf

Severity parameters
-------------------
Expand All @@ -66,15 +65,15 @@ Severity parameters
* ``rel_crit_prob`` = Scale factor for proportion of severe cases that become critical
* ``rel_death_prob`` = Scale factor for proportion of critical cases that result in death
* ``prog_by_age`` = Whether to set disease progression based on the person's age
* ``prognoses`` = Populate this later
* ``prognoses`` = The actual arrays of prognoses by age; this is populated later

Events and interventions
------------------------
* ``interventions`` = List of Intervention instances
* ``interventions`` = The interventions present in this simulation; populated by the user
* ``interv_func`` = Custom intervention function
* ``timelimit`` = Time limit for a simulation (seconds)
* ``timelimit`` = Time limit for the simulation (seconds)
* ``stopping_func`` = A function to call to stop the sim partway through

Health system parameters
--------------------------
* ``n_beds`` = Baseline assumption is that there's no upper limit on the number of beds i.e. there's enough for everyone
* ``n_beds`` = The number of beds available for severely/critically ill patients (default is no constraint)
27 changes: 17 additions & 10 deletions covasim/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
Base classes for Covasim.
'''

import datetime as dt
import numpy as np
import pylab as pl
import pandas as pd
import sciris as sc
import datetime as dt
from . import utils as cvu
from . import misc as cvm
from . import defaults as cvd
Expand Down Expand Up @@ -233,32 +232,40 @@ def day(self, day, *args):
return days


def date(self, ind, *args, dateformat=None):
def date(self, ind, *args, dateformat=None, as_date=False):
'''
Convert an integer or list of integer simulation days to a date/list of dates.
Convert one or more integer days of simulation time to a date/list of dates --
by default returns a string, or returns a datetime Date object if as_date is True.
Args:
ind (int, list, or array): the day(s) in simulation time
as_date (bool): whether to return as a datetime date instead of a string
Returns:
dates (str or list): the date relative to the simulation start day, as an integer
dates (str, Date, or list): the date(s) corresponding to the simulation day(s)
**Example**::
**Examples**::
sim.date(35) # Returns '2020-04-05'
sim.date(34) # Returns '2020-04-04'
sim.date([34, 54]) # Returns ['2020-04-04', '2020-04-24']
sim.date(34, 54, as_dt=True) # Returns [datetime.date(2020, 4, 4), datetime.date(2020, 4, 24)]
'''

# Handle inputs
if sc.isnumber(ind): # If it's a number, convert it to a list
ind = sc.promotetolist(ind)
ind.extend(args)

if dateformat is None:
dateformat = '%Y-%m-%d'

# Do the conversion
dates = []
for i in ind:
tmp = self['start_day'] + dt.timedelta(days=int(i))
dates.append(tmp.strftime(dateformat))
date_obj = self['start_day'] + dt.timedelta(days=int(i))
if as_date:
dates.append(date_obj)
else:
dates.append(date_obj.strftime(dateformat))

# Return a string rather than a list if only one provided
if len(ind)==1:
Expand Down
7 changes: 6 additions & 1 deletion covasim/interventions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import inspect
'''
Specify the core interventions available in Covasim. Other interventions can be
defined by the user by inheriting from these classes.
'''

import numpy as np
import pandas as pd
import pylab as pl
import sciris as sc
import inspect
import datetime as dt
from . import utils as cvu
from . import base as cvb
Expand Down
2 changes: 1 addition & 1 deletion covasim/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
Miscellaneous functions that do not belong anywhere else
'''

import datetime as dt
import numpy as np
import pandas as pd
import sciris as sc
import datetime as dt
import scipy.stats as sps
from . import version as cvver

Expand Down
16 changes: 8 additions & 8 deletions covasim/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ def make_pars(set_prognoses=False, prog_by_age=True, **kwargs):
pars['dur']['sev2crit'] = {'dist':'lognormal_int', 'par1':3.0, 'par2':7.4} # Duration from severe symptoms to requiring ICU; see Wang et al., https://jamanetwork.com/journals/jama/fullarticle/2761044

# Duration parameters: time for disease recovery
pars['dur']['asym2rec'] = {'dist':'lognormal_int', 'par1':8.0, 'par2':2.0} # Duration for asymptomatics to recover; see Wölfel et al., https://www.nature.com/articles/s41586-020-2196-x
pars['dur']['mild2rec'] = {'dist':'lognormal_int', 'par1':8.0, 'par2':2.0} # Duration from mild symptoms to recovered; see Wölfel et al., https://www.nature.com/articles/s41586-020-2196-x
pars['dur']['sev2rec'] = {'dist':'lognormal_int', 'par1':14.0, 'par2':2.4} # Duration from severe symptoms to recovered, 22.6 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf
pars['dur']['crit2rec'] = {'dist':'lognormal_int', 'par1':14.0, 'par2':2.4} # Duration from critical symptoms to recovered, 22.6 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf
pars['dur']['asym2rec'] = {'dist':'lognormal_int', 'par1':8.0, 'par2':2.0} # Duration for asymptomatic people to recover; see Wölfel et al., https://www.nature.com/articles/s41586-020-2196-x
pars['dur']['mild2rec'] = {'dist':'lognormal_int', 'par1':8.0, 'par2':2.0} # Duration for people with mild symptoms to recover; see Wölfel et al., https://www.nature.com/articles/s41586-020-2196-x
pars['dur']['sev2rec'] = {'dist':'lognormal_int', 'par1':14.0, 'par2':2.4} # Duration for people with severe symptoms to recover, 22.6 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf
pars['dur']['crit2rec'] = {'dist':'lognormal_int', 'par1':14.0, 'par2':2.4} # Duration for people with critical symptoms to recover, 22.6 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf
pars['dur']['crit2die'] = {'dist':'lognormal_int', 'par1':6.2, 'par2':1.7} # Duration from critical symptoms to death, 17.8 days total; see Verity et al., https://www.medrxiv.org/content/10.1101/2020.03.09.20033357v1.full.pdf

# Severity parameters: probabilities of symptom progression
Expand All @@ -76,16 +76,16 @@ def make_pars(set_prognoses=False, prog_by_age=True, **kwargs):
pars['rel_crit_prob'] = 1.0 # Scale factor for proportion of severe cases that become critical
pars['rel_death_prob'] = 1.0 # Scale factor for proportion of critical cases that result in death
pars['prog_by_age'] = prog_by_age # Whether to set disease progression based on the person's age
pars['prognoses'] = None # Populate this later
pars['prognoses'] = None # The actual arrays of prognoses by age; this is populated later

# Events and interventions
pars['interventions'] = [] # List of Intervention instances
pars['interventions'] = [] # The interventions present in this simulation; populated by the user
pars['interv_func'] = None # Custom intervention function
pars['timelimit'] = 3600 # Time limit for a simulation (seconds)
pars['timelimit'] = None # Time limit for the simulation (seconds)
pars['stopping_func'] = None # A function to call to stop the sim partway through

# Health system parameters
pars['n_beds'] = None # Baseline assumption is that there's no upper limit on the number of beds i.e. there's enough for everyone
pars['n_beds'] = None # The number of beds available for severely/critically ill patients (default is no constraint)

# Update with any supplied parameter values and generate things that need to be generated
pars.update(kwargs)
Expand Down
17 changes: 12 additions & 5 deletions covasim/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,18 +589,20 @@ def get_individual_states(sim):
plotly_legend = dict(legend_orientation="h", legend=dict(x=0.0, y=1.18))


def plotly_interventions(sim, fig):
def plotly_interventions(sim, fig, add_to_legend=False):
''' Add vertical lines for interventions to the plot '''
if sim['interventions']:
for interv in sim['interventions']:
if hasattr(interv, 'days'):
for interv_day in interv.days:
if interv_day and interv_day < sim['n_days']:
fig.add_shape(dict(type="line", xref="x", yref="paper", x0=interv_day, x1=interv_day, y0=0, y1=1, name='Intervention', line=dict(width=0.5, dash='dash')))
fig.update_layout(annotations=[dict(x=interv_day, y=1.07, xref="x", yref="paper", text="Intervention change", showarrow=False)])
interv_date = sim.date(interv_day, as_date=True)
fig.add_shape(dict(type="line", xref="x", yref="paper", x0=interv_date, x1=interv_date, y0=0, y1=1, line=dict(width=0.5, dash='dash')))
if add_to_legend:
fig.add_trace(go.Scatter(x=[interv_date], y=[0], mode='lines', name='Intervention change', line=dict(width=0.5, dash='dash')))
return

def plotly_sim(sim):
def plotly_sim(sim, do_show=False):
''' Main simulation results -- parallel of sim.plot() '''

plots = []
Expand All @@ -618,10 +620,15 @@ def plotly_sim(sim):
ydata = sim.data[key]
fig.add_trace(go.Scatter(x=xdata, y=ydata, mode='markers', name=label + ' (data)', line_color=this_color))

plotly_interventions(sim, fig)
plotly_interventions(sim, fig, add_to_legend=(p==0)) # Only add the intervention label to the legend for the first plot
fig.update_layout(title={'text':title}, yaxis_title='Count', autosize=True, **plotly_legend)

plots.append(fig)

if do_show:
for fig in plots:
fig.show()

return plots


Expand Down
2 changes: 1 addition & 1 deletion covasim/population.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
#%% Imports
import numpy as np # Needed for a few things not provided by pl
import sciris as sc
from collections import defaultdict
from . import requirements as cvreq
from . import utils as cvu
from . import data as cvdata
from . import defaults as cvd
from . import parameters as cvpars
from . import people as cvppl
from collections import defaultdict


# Specify all externally visible functions this file defines
Expand Down
2 changes: 1 addition & 1 deletion covasim/sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ def run(self, do_plot=False, until=None, verbose=None, **kwargs):

# Check if we were asked to stop
elapsed = sc.toc(T, output=True)
if elapsed > self['timelimit']:
if self['timelimit'] and elapsed > self['timelimit']:
sc.printv(f"Time limit ({self['timelimit']} s) exceeded", 1, verbose)
break
elif self['stopping_func'] and self['stopping_func'](self):
Expand Down
2 changes: 1 addition & 1 deletion covasim/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np # For numerics
from . import defaults as cvd

# What to import -- not all functions are externally available
# What functions are externally visible -- note, this gets populated in each section below
__all__ = []

# Set dtypes
Expand Down
4 changes: 2 additions & 2 deletions covasim/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

__all__ = ['__version__', '__versiondate__', '__license__']

__version__ = '1.0.0'
__versiondate__ = '2020-05-08'
__version__ = '1.0.1'
__versiondate__ = '2020-05-09'
__license__ = f'Covasim {__version__} ({__versiondate__}) — © 2020 by IDM'
16 changes: 11 additions & 5 deletions tests/devtests/plotly_tests.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
'''
Test Plotly plotting outside of the webapp.
'''

import plotly.io as pio
import covasim as cv

pio.renderers.default = "browser" # Or can use a Jupyter notebook

pio.renderers.default = "browser"

sim = cv.Sim(pop_size=10e3)

ce = cv.clip_edges(**{'start_day': 10, 'change': 0.5})
sim = cv.Sim(pop_size=100, n_days=60, datafile='../example_data.csv', interventions=ce, verbose=0)
sim.run()
fig = cv.animate_people(sim, do_show=True)

f1list = cv.plotly_sim(sim, do_show=True)
f2 = cv.plotly_people(sim, do_show=True)
f3 = cv.plotly_animate(sim, do_show=True)
Loading

0 comments on commit 1e08cc9

Please sign in to comment.