def plot_result(self, key, fig_args=None, plot_args=None): ''' Simple method to plot a single result. Useful for results that aren't standard outputs. Args: key (str): the key of the result to plot fig_args (dict): passed to pl.figure() plot_args (dict): passed to pl.plot() **Example** :: sim.plot_result('doubling_time') ''' fig_args = sc.mergedicts({'figsize': (16, 10)}, fig_args) plot_args = sc.mergedicts({'lw': 3, 'alpha': 0.7}, plot_args) fig = pl.figure(**fig_args) pl.subplot(111) tvec = self.results['t'] res = self.results[key] y = res.values color = res.color pl.plot(tvec, y, c=color, **plot_args) return fig
def plot_result(sim, key, fig_args=None, plot_args=None, axis_args=None, scatter_args=None, font_size=18, font_family=None, grid=False, commaticks=True, setylim=True, as_dates=True, dateformat=None, interval=None, color=None, label=None, fig=None): ''' Plot a single result -- see Sim.plot_result() for documentation ''' # Handle inputs fig_args = sc.mergedicts({'figsize': (16, 8)}, fig_args) axis_args = sc.mergedicts({'top': 0.95}, axis_args) args = handle_args(fig_args, plot_args, scatter_args, axis_args) fig, figs, ax = create_figs(args, font_size, font_family, sep_figs=False, fig=fig) # Gather results res = sim.results[key] res_t = sim.results['t'] if color is None: color = res.color # Reuse the figure, if available try: if fig.axes[0].get_label() == 'plot_result': ax = fig.axes[0] except: pass if ax is None: # Otherwise, make a new one ax = pl.subplot(111, label='plot_result') # Do the plotting if label is None: label = res.name if res.low is not None and res.high is not None: ax.fill_between(res_t, res.low, res.high, color=color, **args.fill) # Create the uncertainty bound ax.plot(res_t, res.values, c=color, label=label, **args.plot) plot_data(sim, ax, key, args.scatter) # Plot the data plot_interventions(sim, ax) # Plot the interventions title_grid_legend(ax, res.name, grid, commaticks, setylim, args.legend) # Configure the title, grid, and legend reset_ticks( ax, sim, interval, as_dates ) # Optionally reset tick marks (useful for e.g. plotting weeks/months) return fig
def handle_args(fig_args=None, plot_args=None, scatter_args=None, axis_args=None, fill_args=None, legend_args=None, date_args=None, show_args=None, mpl_args=None, **kwargs): ''' Handle input arguments -- merge user input with defaults; see sim.plot for documentation ''' # Set defaults defaults = sc.objdict() defaults.fig = sc.objdict(figsize=(10, 8)) defaults.plot = sc.objdict(lw=1.5, alpha= 0.7) defaults.scatter = sc.objdict(s=20, marker='s', alpha=0.7, zorder=0) defaults.axis = sc.objdict(left=0.10, bottom=0.08, right=0.95, top=0.95, wspace=0.30, hspace=0.30) defaults.fill = sc.objdict(alpha=0.2) defaults.legend = sc.objdict(loc='best', frameon=False) defaults.date = sc.objdict(as_dates=True, dateformat=None, interval=None, rotation=None, start_day=None, end_day=None) defaults.show = sc.objdict(data=True, ticks=True, interventions=True, legend=True) defaults.mpl = sc.objdict(dpi=None, fontsize=None, fontfamily=None) # Use Covasim global defaults # Handle directly supplied kwargs for dkey,default in defaults.items(): keys = list(kwargs.keys()) for kw in keys: if kw in default.keys(): default[kw] = kwargs.pop(kw) # Merge arguments together args = sc.objdict() args.fig = sc.mergedicts(defaults.fig, fig_args) args.plot = sc.mergedicts(defaults.plot, plot_args) args.scatter = sc.mergedicts(defaults.scatter, scatter_args) args.axis = sc.mergedicts(defaults.axis, axis_args) args.fill = sc.mergedicts(defaults.fill, fill_args) args.legend = sc.mergedicts(defaults.legend, legend_args) args.date = sc.mergedicts(defaults.date, fill_args) args.show = sc.mergedicts(defaults.show, show_args) args.mpl = sc.mergedicts(defaults.mpl, mpl_args) # If unused keyword arguments remain, raise an error if len(kwargs): notfound = sc.strjoin(kwargs.keys()) valid = sc.strjoin(sorted(set([k for d in defaults.values() for k in d.keys()]))) # Remove duplicates and order errormsg = f'The following keywords could not be processed:\n{notfound}\n\n' errormsg += f'Valid keywords are:\n{valid}\n\n' errormsg += 'For more precise plotting control, use fig_args, plot_args, etc.' raise sc.KeyNotFoundError(errormsg) # Handle what to show show_keys = defaults.show.keys() args.show = {k:True for k in show_keys} if show_args in [True, False]: # Handle all on or all off args.show = {k:show_args for k in show_keys} else: args.show = sc.mergedicts(args.show, show_args) # Handle global Matplotlib arguments args.mpl_orig = sc.objdict() for key,value in args.mpl.items(): if value is not None: args.mpl_orig[key] = cvset.options.get(key) cvset.options.set(key, value) return args
def plot(self, windows=False, width=0.8, color='#F8A493', font_size=18, fig_args=None, axis_args=None, data_args=None): ''' Simple method for plotting the histograms. Args: windows (bool): whether to plot windows instead of cumulative counts width (float): width of bars color (hex or rgb): the color of the bars font_size (float): size of font fig_args (dict): passed to pl.figure() axis_args (dict): passed to pl.subplots_adjust() data_args (dict): 'width', 'color', and 'offset' arguments for the data ''' # Handle inputs fig_args = sc.mergedicts(dict(figsize=(24,15)), fig_args) axis_args = sc.mergedicts(dict(left=0.08, right=0.92, bottom=0.08, top=0.92), axis_args) d_args = sc.objdict(sc.mergedicts(dict(width=0.3, color='#000000', offset=0), data_args)) pl.rcParams['font.size'] = font_size # Initialize n_plots = len(self.states) n_rows = np.ceil(np.sqrt(n_plots)) # Number of subplot rows to have n_cols = np.ceil(n_plots/n_rows) # Number of subplot columns to have figs = [] # Handle windows and what to plot if windows: if self.window_hists is None: self.compute_windows() histsdict = self.window_hists else: histsdict = self.hists if not len(histsdict): errormsg = f'Cannot plot since no histograms were recorded (schuled days: {self.days})' raise ValueError(errormsg) # Make the figure(s) for date,hists in histsdict.items(): figs += [pl.figure(**fig_args)] pl.subplots_adjust(**axis_args) bins = hists['bins'] barwidth = width*(bins[1] - bins[0]) # Assume uniform width for s,state in enumerate(self.states): pl.subplot(n_rows, n_cols, s+1) pl.bar(bins, hists[state], width=barwidth, facecolor=color, label=f'Number {state}') if self.data and state in self.data: data = self.data[state] pl.bar(bins+d_args.offset, data, width=barwidth*d_args.width, facecolor=d_args.color, label='Data') pl.xlabel('Age') pl.ylabel('Count') pl.xticks(ticks=bins) pl.legend() preposition = 'from' if windows else 'by' pl.title(f'Number of people {state} {preposition} {date}') return figs
def test_mergedicts(): sc.heading('Test merging dictionaries') md = sc.mergedicts({'a': 1}, {'b': 2}) # Returns {'a':1, 'b':2} sc.mergedicts({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }) # Returns {'a':1, 'b':3, 'c':4} sc.mergedicts({ 'b': 3, 'c': 4 }, { 'a': 1, 'b': 2 }) # Returns {'a':1, 'b':2, 'c':4} with pytest.raises(KeyError): sc.mergedicts({ 'b': 3, 'c': 4 }, { 'a': 1, 'b': 2 }, overwrite=False) # Raises exception with pytest.raises(TypeError): sc.mergedicts({'b': 3, 'c': 4}, None, strict=True) # Raises exception return md
def __init__(self, sim=None, metapars=None, scenarios=None, basepars=None, filename=None): # For this object, metapars are the foundation default_pars = make_metapars() # Start with default pars super().__init__( default_pars) # Initialize and set the parameters as attributes # Handle filename self.created = sc.now() if filename is None: datestr = sc.getdate(obj=self.created, dateformat='%Y-%b-%d_%H.%M.%S') filename = f'covasim_scenarios_{datestr}.scens' self.filename = filename # Handle scenarios -- by default, create a baseline scenario if scenarios is None: scenarios = sc.dcp(cvd.default_scenario) self.scenarios = scenarios # Handle metapars self.metapars = sc.mergedicts({}, metapars) self.update_pars(self.metapars) # Create the simulation and handle basepars if sim is None: sim = cvsim.Sim() self.base_sim = sim self.basepars = sc.mergedicts({}, basepars) self.base_sim.update_pars(self.basepars) self.base_sim.validate_pars() self.base_sim.init_results() # Copy quantities from the base sim to the main object self.npts = self.base_sim.npts self.tvec = self.base_sim.tvec self.reskeys = self.base_sim.reskeys # Create the results object; order is: results key, scenario, best/low/high self.sims = sc.objdict() self.results = sc.objdict() for reskey in self.reskeys: self.results[reskey] = sc.objdict() for scenkey in scenarios.keys(): self.results[reskey][scenkey] = sc.objdict() for nblh in ['name', 'best', 'low', 'high']: self.results[reskey][scenkey][ nblh] = None # This will get populated below return
def __init__(self, daily_tests, symp_test=100.0, quar_test=1.0, quar_policy=None, subtarget=None, ili_prev=None, sensitivity=1.0, loss_prob=0, test_delay=0, start_day=0, end_day=None, swab_delay=None, **kwargs): super().__init__(**kwargs) # Initialize the Intervention object self._store_args( ) # Store the input arguments so the intervention can be recreated self.daily_tests = daily_tests # Should be a list of length matching time self.symp_test = symp_test # Set probability of testing symptomatics self.quar_test = quar_test # Probability of testing people in quarantine self.quar_policy = quar_policy if quar_policy else 'start' self.subtarget = subtarget # Set any other testing criteria self.ili_prev = ili_prev # Should be a list of length matching time or a float or a dataframe self.sensitivity = sensitivity self.loss_prob = loss_prob self.test_delay = test_delay self.start_day = start_day self.end_day = end_day self.pdf = cvu.get_pdf( **sc.mergedicts(swab_delay) ) # If provided, get the distribution's pdf -- this returns an empty dict if None is supplied return
def test_age_brackets_used_with_contact_matrix(): """ Test that the age brackets used in sp.Pop.generate() matches the contact matrices used. Note: This is a test to ensure that within sp.Pop.generate() uses the right age brackets. By default, without specifying nbrackets in sp.get_census_age_brackets(), the number of age brackets will not match the granularity of the contact matrix. """ sp.logger.info( "Test that the age brackets used in sp.Pop.generate() with the contact matrices have the same number of bins as the contact matrices." ) pop = sp.Pop(**pars) sheet_name = pop.sheet_name loc_pars = pop.loc_pars contact_matrices = sp.get_contact_matrices(sp.settings.datadir, sheet_name=sheet_name) contact_matrix_nbrackets = contact_matrices[list( contact_matrices.keys())[0]].shape[0] cm_age_brackets = sp.get_census_age_brackets( **sc.mergedicts(loc_pars, {'nbrackets': contact_matrix_nbrackets})) assert contact_matrix_nbrackets == len( cm_age_brackets ), f'Check failed, len(contact_matrix_nbrackets): {contact_matrix_nbrackets} does not match len(cm_age_brackets): {len(cm_age_brackets)}.' print( f'Check passed. The age brackets loaded match the number of age brackets for the contact matrices used for the location.' )
def __init__(self, symp_prob, asymp_prob=0.0, symp_quar_prob=None, asymp_quar_prob=None, quar_policy=None, subtarget=None, ili_prev=None, test_sensitivity=1.0, loss_prob=0.0, test_delay=0, start_day=0, end_day=None, swab_delay=None, **kwargs): super().__init__(**kwargs) # Initialize the Intervention object self._store_args( ) # Store the input arguments so the intervention can be recreated self.symp_prob = symp_prob self.asymp_prob = asymp_prob self.symp_quar_prob = symp_quar_prob if symp_quar_prob is not None else symp_prob self.asymp_quar_prob = asymp_quar_prob if asymp_quar_prob is not None else asymp_prob self.quar_policy = quar_policy if quar_policy else 'start' self.subtarget = subtarget self.ili_prev = ili_prev self.test_sensitivity = test_sensitivity self.loss_prob = loss_prob self.test_delay = test_delay self.start_day = start_day self.end_day = end_day self.pdf = cvu.get_pdf( **sc.mergedicts(swab_delay) ) # If provided, get the distribution's pdf -- this returns an empty dict if None is supplied return
def autolabel(ax, rects, h_offset=0, v_offset=0.3, **kwargs): """ Attach a text label above each bar in *rects*, displaying its height. Args: ax : Matplotlib.axes object rects : Matplotlib.container.BarContainer h_offset (float) : The position x to place the text at. v_offset (float) : The position y to place the text at. **fontsize (float) : Default fontsize Returns: None. Set the annotation according to the input parameters """ method_defaults = dict( fontsize=10) # in case kwargs does not have fontsize, add it kwargs = sc.mergedicts(method_defaults, kwargs) # let kwargs override method defaults kwargs = sc.objdict(kwargs) for rect in rects: height = rect.get_height() text = ax.annotate('{}'.format(round(height, 3)), xy=(rect.get_x() + rect.get_width() / 2, height), xytext=(h_offset, v_offset), textcoords="offset points", ha='center', va='bottom') text.set_fontsize(kwargs.fontsize)
def test_plot_school_sizes(do_show, do_save, artifact_dir): """ Test that the school size distribution by type plotting method in sp.Pop class works for a large population. This will be a longer test that is run as part of our end-to-end testing suite. Visually show how the school size distribution generated compares to the data for the location being simulated. Notes: The larger the population size, the better the generated school size distributions by school type can match the expected data. If generated populations are too small, larger schools will be missed and in general there won't be enough schools generated to apply statistical tests. """ sp.logger.info("Test that the school size distribution by type plotting method in sp.Pop class works. Note: For small population sizes, the expected and generated size distributions may not match very well given that the model is stochastic and demographics are based on much larger populations.") pop = sp.Pop(**pars) kwargs = sc.objdict(sc.mergedicts(pars, pop.loc_pars)) kwargs.figname = f"test_school_size_distributions_{kwargs.location}_pop" kwargs.do_show = do_show kwargs.do_save = do_save if artifact_dir: kwargs.figdir = artifact_dir kwargs.screen_height_factor = 0.20 kwargs.hspace = 0.8 kwargs.bottom = 0.09 kwargs.keys_to_exclude = ['uv'] kwargs.cmap = cmr.get_sub_cmap('cmo.curl', 0.08, 1) fig, ax = pop.plot_school_sizes(**kwargs) assert isinstance(fig, mplt.figure.Figure), 'End-to-end school sizes check failed.' print('Check passed. Figure made.') return fig, ax, pop
def __init__(self, sims=None, base_sim=None, quantiles=None, initialize=False, **kwargs): # Handle inputs if base_sim is None: if isinstance(sims, cvs.Sim): base_sim = sims sims = None elif isinstance(sims, list): base_sim = sims[0] else: errormsg = f'If base_sim is not supplied, sims must be either a single sim (treated as base_sim) or a list of sims, not {type(sims)}' raise TypeError(errormsg) if quantiles is None: quantiles = make_metapars()['quantiles'] # Set properties self.sims = sims self.base_sim = base_sim self.quantiles = quantiles self.run_args = sc.mergedicts(kwargs) self.results = None self.which = None # Whether the multisim is to be reduced, combined, etc. # Optionally initialize if initialize: self.init_sims() return
def test_household_head_ages_by_size(create_pop, do_show=False, do_save=False): """ Test that the household head age distribution by household size comparison plotting method in sp.Pop class works. Args: do_show (bool) : If True, show the plot do_save (bool) : If True, save the plot to disk Returns: Matplotlib figure, axes, and pop object. """ sp.logger.info( "Test the age distribution of household heads by the household size.") pop = create_pop kwargs = sc.objdict(sc.mergedicts(pars, pop.loc_pars)) kwargs.figname = f"test_household_head_ages_by_size_{kwargs.location}_pop" kwargs.do_show = do_show kwargs.do_save = do_save if kwargs.do_show: plt.switch_backend(mplt_org_backend) fig, ax = pop.plot_household_head_ages_by_size(**kwargs) assert isinstance( fig, mplt.figure.Figure), 'Check failed. Figure not generated.' print('Check passed. Figure made.') return fig, ax, pop
def test_plot_schools_sizes_without_types(do_show=False, do_save=False): """Test that without school types, all schools are put together in one group.""" sp.logger.info( "Creating schools where school types are not specified. Test school size distribution plotting method without school types. Note: For small population sizes, the expected and generated size distributions may not match very well given that the model is stochastic and demographics are based on much larger populations." ) pars.with_school_types = False # need to rerun the population pop = sp.Pop(**pars) kwargs = sc.objdict(sc.mergedicts(pars, pop.loc_pars)) kwargs.datadir = sp.settings.datadir kwargs.do_show = do_show kwargs.do_save = do_save kwargs.screen_width_factor = 0.30 kwargs.screen_height_factor = 0.20 kwargs.width = 5 kwargs.height = 3.2 kwargs.figname = f"test_all_school_size_distributions_{kwargs.location}_pop" fig, ax = pop.plot_school_sizes(**kwargs) enrollment_by_school_type = pop.count_enrollment_by_school_type() school_types = list(enrollment_by_school_type.keys()) assert school_types[0] is None and len( school_types ) == 1, f"Check 3 failed. School types created: {school_types}." return fig, ax, pop
def test_plot_with_sppeople(create_pop, do_show=False, do_save=False): """ Test plotting method works on synthpops.people.People object. Notes: With this pop type, you will need to supply more information to tell the method where to look for expected data. """ sp.logger.info( "Test that the age comparison plotting method works on sp.people.People and plotting styles can be easily updated." ) pop = create_pop people = pop.to_people() kwargs = sc.objdict(sc.mergedicts(pars, pop.loc_pars)) kwargs.datadir = sp.settings.datadir kwargs.figname = f"test_ages_{kwargs.location}_sppeople" kwargs.do_show = do_show kwargs.do_save = do_save # modify some plotting styles kwargs.color_1 = '#9966cc' kwargs.color_2 = 'indigo' kwargs.markersize = 4.5 fig, ax = sp.plot_ages(people, **kwargs) # fig, ax = sp.plot_ages(people) # to plot without extra information assert isinstance(fig, mplt.figure.Figure), 'Check failed.' print('Check passed. Figure made.') return fig, ax, people
def test_plot_ages(do_show=False, do_save=False): """ Test that the age comparison plotting method in sp.Pop class works. Note: With any popdict, you will need to supply more information to tell the method where to look for expected data. """ sp.logger.info( "Test that the age comparison plotting method with sp.Pop object.") pop = sp.Pop(**pars) kwargs = sc.objdict(sc.mergedicts(pars, pop.loc_pars)) kwargs.figname = f"test_pop_ages_{kwargs.location}_pop" kwargs.do_show = do_show kwargs.do_save = do_save fig, ax = pop.plot_ages(**kwargs) # fig, ax = pop.plot_ages() # to plot without extra information assert isinstance(fig, mplt.figure.Figure), 'Check 1 failed.' print('Check passed. Figure 1 made.') popdict = pop.to_dict() kwargs.datadir = sp.datadir # extra information required kwargs.figname = f"test_popdict_ages_{kwargs.location}_popdict" kwargs.do_show = False fig2, ax2 = sp.plot_ages(popdict, **kwargs) # fig2, ax2 = sp.plot_ages(popdict) # to plot without extra information if not kwargs.do_show: plt.close() assert isinstance(fig, mplt.figure.Figure), 'Check 2 failed.' print('Check passed. Figure 2 made.') return fig, ax, pop
def plot(self, sim, ax=None, **kwargs): ''' Call function during plotting This can be used to do things like add vertical lines on days when interventions take place. Can be disabled by setting self.do_plot=False. Args: sim: the Sim instance ax: the axis instance kwargs: passed to ax.axvline() Returns: None ''' line_args = sc.mergedicts(self.line_args, kwargs) if self.do_plot or self.do_plot is None: if ax is None: ax = pl.gca() for day in self.days: if day is not None: if self.show_label: # Choose whether to include the label in the legend label = self.label else: label = None ax.axvline(day, label=label, **line_args) return
def run(self, reduce=False, combine=False, **kwargs): ''' Run the actual sims Args: reduce (bool): whether or not to reduce after running (see reduce()) combine (bool): whether or not to combine after running (see combine(), not compatible with reduce) kwargs (dict): passed to multi_run() Returns: None (modifies MultiSim object in place) ''' # Handle which sims to use -- same as init_sims() if self.sims is None: sims = self.base_sim else: sims = self.sims # Run kwargs = sc.mergedicts(self.run_args, kwargs) self.sims = multi_run(sims, **kwargs) # Reduce or combine if reduce: self.reduce() elif combine: self.combine() return
def make_hybrid_contacts(pop_size, ages, contacts, school_ages=None, work_ages=None): ''' Create "hybrid" contacts -- microstructured contacts for households and random contacts for schools and workplaces, both of which have extremely basic age structure. A combination of both make_random_contacts() and make_microstructured_contacts(). ''' # Handle inputs and defaults layer_keys = ['h', 's', 'w', 'c'] contacts = sc.mergedicts({ 'h': 4, 's': 20, 'w': 20, 'c': 20 }, contacts) # Ensure essential keys are populated if school_ages is None: school_ages = [6, 22] if work_ages is None: work_ages = [22, 65] # Create the empty contacts list -- a list of {'h':[], 's':[], 'w':[]} contacts_list = [{key: [] for key in layer_keys} for i in range(pop_size)] # Start with the household contacts for each person h_contacts, _, clusters = make_microstructured_contacts( pop_size, {'h': contacts['h']}) # Make community contacts c_contacts, _ = make_random_contacts(pop_size, {'c': contacts['c']}) # Get the indices of people in each age bin ages = np.array(ages) s_inds = sc.findinds((ages >= school_ages[0]) * (ages < school_ages[1])) w_inds = sc.findinds((ages >= work_ages[0]) * (ages < work_ages[1])) # Create the school and work contacts for each person s_contacts, _ = make_random_contacts(len(s_inds), {'s': contacts['s']}) w_contacts, _ = make_random_contacts(len(w_inds), {'w': contacts['w']}) # Construct the actual lists of contacts for i in range(pop_size): contacts_list[i]['h'] = h_contacts[i][ 'h'] # Copy over household contacts -- present for everyone for i, ind in enumerate(s_inds): contacts_list[ind]['s'] = s_inds[s_contacts[i] ['s']] # Copy over school contacts for i, ind in enumerate(w_inds): contacts_list[ind]['w'] = w_inds[w_contacts[i] ['w']] # Copy over work contacts for i in range(pop_size): contacts_list[i]['c'] = c_contacts[i][ 'c'] # Copy over community contacts -- present for everyone return contacts_list, layer_keys, clusters
def compute_gofs(self, **kwargs): ''' Compute the goodness-of-fit ''' kwargs = sc.mergedicts(self.gof_kwargs, kwargs) for key in self.pair.keys(): actual = sc.dcp(self.pair[key].data) predicted = sc.dcp(self.pair[key].sim) self.gofs[key] = cvm.compute_gof(actual, predicted, **kwargs) return
def __init__(self, label=None, show_label=True, do_plot=None, line_args=None): self.label = label # e.g. "Close schools" self.show_label = show_label # Show the label by default self.do_plot = do_plot if do_plot is not None else True # Plot the intervention, including if None self.line_args = sc.mergedicts(dict(linestyle='--', c=[0,0,0]), line_args) # Do not set alpha by default due to the issue of overlapping interventions self.days = [] # The start and end days of the intervention self.initialized = False # Whether or not it has been initialized return
def __init__(self, *args, **kwargs): """Class constructor for plotting_kwargs.""" kwargs = sc.mergedicts(self.default_plotting_kwargs(), kwargs) self.update(kwargs) self.initialize() return
def test_plot_school_sizes(do_show=False, do_save=False): """ Test that the school size distribution by type plotting method in sp.Pop class works. Visually show how the school size distribution generated compares to the data for the location being simulated. Notes: The larger the population size, the better the generated school size distributions by school type can match the expected data. If generated populations are too small, larger schools will be missed and in general there won't be enough schools generated to apply statistical tests. """ sp.logger.info( "Test that the school size distribution by type plotting method in sp.Pop class works. Note: For small population sizes, the expected and generated size distributions may not match very well given that the model is stochastic and demographics are based on much larger populations." ) pop = sp.Pop(**pars) kwargs = sc.objdict(sc.mergedicts(pars, pop.loc_pars)) kwargs.figname = f"test_school_size_distributions_{kwargs.location}_pop" kwargs.do_show = do_show kwargs.do_save = do_save kwargs.screen_height_factor = 0.20 kwargs.hspace = 0.8 kwargs.bottom = 0.09 kwargs.keys_to_exclude = ['uv'] kwargs.cmap = cmr.get_sub_cmap('cmo.curl', 0.08, 1) fig, ax = pop.plot_school_sizes(**kwargs) assert isinstance(fig, mplt.figure.Figure), 'Check 1 failed.' print('Check passed. Figure 1 made.') # works on popdict sp.logger.info("Test school size distribution plotting method on popdict.") popdict = pop.popdict kwargs.datadir = sp.settings.datadir kwargs.do_show = False kwargs.figname = f"test_school_size_distributions_{kwargs.location}_popdict" fig2, ax2 = sp.plot_school_sizes(popdict, **kwargs) if not kwargs.do_show: plt.close() assert isinstance(fig2, mplt.figure.Figure), 'Check 2 failed.' print('Check passed. Figure 2 made.') sp.logger.info( "Test school size distribution plotting method with keys_to_exclude as a string and without comparison." ) kwargs.keys_to_exclude = 'uv' kwargs.comparison = False fig3, ax3 = pop.plot_school_sizes(**kwargs) assert isinstance(fig3, mplt.figure.Figure), 'Check 3 failed.' print( 'Check passed. Figure 3 made with keys_to_exclude as a string and without comparison.' ) return fig, ax, pop
def set_option(key=None, value=None, set_global=True, **kwargs): ''' Set a parameter or parameters. Use ``cv.options.set('defaults')`` to reset all values to default, or ``cv.options.set(dpi='default')`` to reset one parameter to default. See ``cv.options.help()`` for more information. Args: key (str): the parameter to modify, or 'defaults' to reset eerything to default values value (varies): the value to specify; use None or 'default' to reset to default set_global (bool): if true (default), sets plotting options globally (rather than just for Covasim) kwargs (dict): if supplied, set multiple key-value pairs Options are (see also ``cv.options.help()``): - verbose: default verbosity for simulations to use - font_size: the font size used for the plots - font_family: the font family/face used for the plots - dpi: the overall DPI for the figure - show: whether to show figures - close: whether to close the figures - backend: which Matplotlib backend to use - precision: the arithmetic to use in calculations - numba_parallel: whether to parallelize Numba **Examples**:: cv.options.set('font_size', 18) cv.options.set(font_size=18, show=False, backend='agg', precision=64) cv.options.set('defaults') # Reset to default options ''' if key is not None: kwargs = sc.mergedicts(kwargs, {key: value}) reload_required = False # Reset to defaults if key in ['default', 'defaults']: kwargs = orig_options # Reset everything to default # Reset options for key, value in kwargs.items(): if key not in options: keylist = orig_options.keys() keys = '\n'.join(keylist) errormsg = f'Option "{key}" not recognized; options are "defaults" or:\n{keys}\n\nSee help(cv.options.set) for more information.' raise sc.KeyNotFoundError(errormsg) else: if value in [None, 'default']: value = orig_options[key] options[key] = value if key in numba_keys: reload_required = True if key in matplotlib_keys and set_global: set_matplotlib_global(key, value) if reload_required: reload_numba() return
def __init__(self, sim, weights=None, keys=None, method=None, custom=None, compute=True, verbose=False, **kwargs): # Handle inputs self.weights = weights self.custom = sc.mergedicts(custom) self.verbose = verbose self.weights = sc.mergedicts({'cum_deaths':10, 'cum_diagnoses':5}, weights) self.keys = keys self.gof_kwargs = kwargs # Copy data if sim.data is None: errormsg = 'Model fit cannot be calculated until data are loaded' raise RuntimeError(errormsg) self.data = sim.data # Copy sim results if not sim.results_ready: errormsg = 'Model fit cannot be calculated until results are run' raise RuntimeError(errormsg) self.sim_results = sc.objdict() for key in sim.result_keys() + ['t', 'date']: self.sim_results[key] = sim.results[key] self.sim_npts = sim.npts # Number of time points in the sim # Copy other things self.sim_dates = sim.datevec.tolist() # These are populated during initialization self.inds = sc.objdict() # To store matching indices between the data and the simulation self.inds.sim = sc.objdict() # For storing matching indices in the sim self.inds.data = sc.objdict() # For storing matching indices in the data self.date_matches = sc.objdict() # For storing matching dates, largely for plotting self.pair = sc.objdict() # For storing perfectly paired points between the data and the sim self.diffs = sc.objdict() # Differences between pairs self.gofs = sc.objdict() # Goodness-of-fit for differences self.losses = sc.objdict() # Weighted goodness-of-fit self.mismatches = sc.objdict() # Final mismatch values self.mismatch = None # The final value if compute: self.compute() return
def show_locations(location=None, output=False): ''' Print a list of available locations. Args: location (str): if provided, only check if this location is in the list output (bool): whether to return the list (else print) **Examples**:: cv.data.show_locations() # Print a list of valid locations cv.data.show_locations('lithuania') # Check if Lithuania is a valid location cv.data.show_locations('Viet-Nam') # Check if Viet-Nam is a valid location ''' country_json = sc.dcp(cad.data) state_json = sc.dcp(sad.data) aliases = get_country_aliases() age_data = sc.mergedicts( state_json, country_json, aliases) # Countries will overwrite states, e.g. Georgia household_data = sc.dcp(hsd.data) loclist = sc.objdict() loclist.age_distributions = sorted(list(age_data.keys())) loclist.household_size_distributions = sorted(list(household_data.keys())) if location is not None: age_available = location.lower() in [ v.lower() for v in loclist.age_distributions ] hh_available = location.lower() in [ v.lower() for v in loclist.household_size_distributions ] age_sugg = '' hh_sugg = '' age_sugg = f'(closest match: {sc.suggest(location, loclist.age_distributions)})' if not age_available else '' hh_sugg = f'(closest match: {sc.suggest(location, loclist.household_size_distributions)})' if not hh_available else '' print(f'For location "{location}":') print( f' Population age distribution is available: {age_available} {age_sugg}' ) print( f' Household size distribution is available: {hh_available} {hh_sugg}' ) return if output: return loclist else: print( f'There are {len(loclist.age_distributions)} age distributions and {len(loclist.household_size_distributions)} household size distributions.' ) print('\nList of available locations (case insensitive):\n') sc.pp(loclist) return
def update_pars(self, pars=None, create=False, **kwargs): ''' Ensure that metaparameters get used properly before being updated ''' pars = sc.mergedicts(pars, kwargs) if pars: if 'use_layers' in pars: # Reset layers cvpars.set_contacts(pars) if 'prog_by_age' in pars: pars['prognoses'] = cvpars.get_prognoses(by_age=pars['prog_by_age']) # Reset prognoses super().update_pars(pars=pars, create=create) # Call update_pars() for ParsObj return
def update_pars(self, pars=None, create=False, **kwargs): ''' Ensure that metaparameters get used properly before being updated ''' pars = sc.mergedicts(pars, kwargs) if pars: if pars.get('pop_type'): cvpar.reset_layer_pars(pars, force=False) if pars.get('prog_by_age'): pars['prognoses'] = cvpar.get_prognoses(by_age=pars['prog_by_age']) # Reset prognoses super().update_pars(pars=pars, create=create) # Call update_pars() for ParsObj return
def plot_compare(df, log_scale=True, fig_args=None, plot_args=None, axis_args=None, scatter_args=None, font_size=18, font_family=None, grid=False, commaticks=True, setylim=True, as_dates=True, dateformat=None, interval=None, color=None, label=None, fig=None): ''' Plot a MultiSim comparison -- see MultiSim.plot_compare() for documentation ''' # Handle inputs fig_args = sc.mergedicts({'figsize':(16,16)}, fig_args) axis_args = sc.mergedicts({'left': 0.16, 'bottom': 0.05, 'right': 0.98, 'top': 0.98, 'wspace': 0.50, 'hspace': 0.10}, axis_args) args = handle_args(fig_args, plot_args, scatter_args, axis_args) fig, figs, ax = create_figs(args, font_size, font_family, sep_figs=False, fig=fig) # Map from results into different categories mapping = { 'cum': 'Cumulative counts', 'new': 'New counts', 'n': 'Number in state', 'r': 'R_eff', } category = [] for v in df.index.values: v_type = v.split('_')[0] if v_type in mapping: category.append(v_type) else: category.append('other') df['category'] = category # Plot for i,m in enumerate(mapping): not_r_eff = m != 'r' if not_r_eff: ax = fig.add_subplot(2, 2, i+1) else: ax = fig.add_subplot(8, 2, 10) dfm = df[df['category'] == m] logx = not_r_eff and log_scale dfm.plot(ax=ax, kind='barh', logx=logx, legend=False) if not(not_r_eff): ax.legend(loc='upper left', bbox_to_anchor=(0,-0.3)) ax.grid(True) return fig
def set_font(self, *args, **font): """Set font styles.""" default_font = dict(family=self.fontfamily, style=self.fontstyle, variant=self.fontvariant, weight=self.fontweight, size=self.fontsize) font = sc.mergedicts(default_font, font) mplt.rc('font', **font) return