def tidy_up(fig, figs, sep_figs, do_save, fig_path, do_show, args): ''' Handle saving, figure showing, and what value to return ''' # Handle saving if do_save: if fig_path is not None: # No figpath provided - see whether do_save is a figpath fig_path = sc.makefilepath( fig_path) # Ensure it's valid, including creating the folder cvm.savefig(filename=fig_path) # Save the figure # Show the figure, or close it do_show = cvset.handle_show(do_show) if cvset.options.close and not do_show: if sep_figs: for fig in figs: pl.close(fig) else: pl.close(fig) # Reset Matplotlib defaults for key, value in args.mpl_orig.items(): cvset.options.set(key, value) # Return the figure or figures if sep_figs: return figs else: return fig
def tidy_up(fig, figs, sep_figs, do_save, fig_path, do_show, args): ''' Handle saving, figure showing, and what value to return ''' figlist = sc.mergelists( fig, figs) # Usually just one figure, but here for completeness # Optionally maximize -- does not work on all systems if args.show['maximize']: for f in figlist: sc.maximize(fig=f) pl.pause(0.01) # Force refresh # Use tight layout for all figures if args.show['tight']: for f in figlist: sc.figlayout(fig=f) # Handle saving if do_save: if isinstance( fig_path, str): # No figpath provided - see whether do_save is a figpath fig_path = sc.makefilepath( fig_path) # Ensure it's valid, including creating the folder cvm.savefig(fig=figlist, filename=fig_path) # Save the figure return handle_show_return(do_show, fig=fig, figs=figs)
def tidy_up(fig, figs, sep_figs, do_save, fig_path, do_show, default_name='covasim.png'): ''' Handle saving, figure showing, and what value to return ''' # Handle saving if do_save: if fig_path is None: # No figpath provided - see whether do_save is a figpath fig_path = default_name # Just give it a default name fig_path = sc.makefilepath( fig_path) # Ensure it's valid, including creating the folder pl.savefig(fig_path) # Show or close the figure if do_show: pl.show() else: pl.close(fig) # Return the figure or figures if sep_figs: return figs else: return fig
def tidy_up(fig, figs, sep_figs, do_save, fig_path, do_show): ''' Handle saving, figure showing, and what value to return ''' # Handle saving if do_save: if fig_path is not None: # No figpath provided - see whether do_save is a figpath fig_path = sc.makefilepath( fig_path) # Ensure it's valid, including creating the folder cvm.savefig(filename=fig_path) # Save the figure # Show the figure, or close it if do_show is None: do_show = cvo.show if do_show: pl.show() elif cvo.close: if sep_figs: for fig in figs: pl.close(fig) else: pl.close(fig) # Return the figure or figures if sep_figs: return figs else: return fig
def finalize_figure(fig, plkwargs, **new_plkwargs): """ Update any parameters and then return figpath. Args: fig (matplotlib.Figure) : figure plkwargs (plotting_kwargs) : plotting kwargs class new_plkwargs (dict) : dictionary of new plotting kwargs to update with Returns: Matplotlib figure. """ plkwargs = sc.dcp(plkwargs) plkwargs.update(new_plkwargs) if plkwargs.do_save: plkwargs.figpath = sc.makefilepath(filename=plkwargs.figname, folder=plkwargs.figdir, ext=plkwargs.format) fig.savefig(plkwargs.figpath, format=plkwargs.format, dpi=plkwargs.save_dpi) if plkwargs.do_show: plt.show() return fig
def save(self, filename=None, keep_people=False, skip_attrs=None, **kwargs): ''' Save to disk as a gzipped pickle. Args: filename (str or None): the name or path of the file to save to; if None, uses stored kwargs: passed to makefilepath() Returns: filename (str): the validated absolute path to the saved file **Example**:: sim.save() # Saves to a .sim file with the date and time of creation by default ''' if filename is None: filename = self.simfile filename = sc.makefilepath(filename=filename, **kwargs) self.filename = filename # Store the actual saved filename if skip_attrs or not keep_people: obj = self.shrink(skip_attrs=skip_attrs, in_place=False) else: obj = self sc.saveobj(filename=filename, obj=obj) return filename
def git_info(filename=None, check=False, comments=None, old_info=None, die=False, indent=2, verbose=True, frame=2, **kwargs): ''' Get current git information and optionally write it to disk. Simplest usage is cv.git_info(__file__) Args: filename (str): name of the file to write to or read from check (bool): whether or not to compare two git versions comments (dict): additional comments to include in the file old_info (dict): dictionary of information to check against die (bool): whether or not to raise an exception if the check fails indent (int): how many indents to use when writing the file to disk verbose (bool): detail to print frame (int): how many frames back to look for caller info kwargs (dict): passed to sc.loadjson() (if check=True) or sc.savejson() (if check=False) **Examples**:: cv.git_info() # Return information cv.git_info(__file__) # Writes to disk cv.git_info('covasim_version.gitinfo') # Writes to disk cv.git_info('covasim_version.gitinfo', check=True) # Checks that current version matches saved file ''' # Handle the case where __file__ is supplied as the argument if isinstance(filename, str) and filename.endswith('.py'): filename = filename.replace('.py', '.gitinfo') # Get git info calling_file = sc.makefilepath(sc.getcaller(frame=frame, tostring=False)['filename']) cv_info = {'version':cvv.__version__} cv_info.update(sc.gitinfo(__file__, verbose=False)) caller_info = sc.gitinfo(calling_file, verbose=False) caller_info['filename'] = calling_file info = {'covasim':cv_info, 'called_by':caller_info} if comments: info['comments'] = comments # Just get information and optionally write to disk if not check: if filename is not None: output = sc.savejson(filename, info, indent=indent, **kwargs) else: output = info return output # Check if versions match, and optionally raise an error else: if filename is not None: old_info = sc.loadjson(filename, **kwargs) string = '' old_cv_info = old_info['covasim'] if 'covasim' in old_info else old_info if cv_info != old_cv_info: # pragma: no cover string = f'Git information differs: {cv_info} vs. {old_cv_info}' if die: raise ValueError(string) elif verbose: print(string) return
def make_students(sim, save_pop=False, popfile=None, verbose=None, die=True, reset=False): ''' An analog to population.make_people. It borrows some code from this function to ensure the simulation runs smoothly. ''' # Set inputs and defaults pop_size = int(sim['pop_size']) # Shorten if verbose is None: verbose = sim['verbose'] if popfile is None: popfile = sim.popfile if sim.people and not reset: return sim.people # If it's already there, just return elif sim.popdict and not reset: popdict = sim.popdict # Use stored one sim.popdict = None # Once loaded, remove elif sim['pop_type'] == 'campus': popdict = make_campus(sim) else: raise RuntimeWarning( "populationCampus.make_students only supports the value \'campus\' for \'pop_type\'" ) popdict = make_campus(sim) # Ensure prognoses are set if sim['prognoses'] is None: sim['prognoses'] = cvpars.get_prognoses(sim['prog_by_age']) # Actually create the people people = stu.Students( sim, uid=popdict['uid'], age=popdict['age'], sex=popdict['sex'], contacts=popdict['contacts']) # List for storing the people average_age = sum(popdict['age'] / pop_size) sc.printv( f'Created {pop_size} people, average age {average_age:0.2f} years', 2, verbose) if save_pop: if popfile is None: errormsg = 'Please specify a file to save to using the popfile kwarg' raise FileNotFoundError(errormsg) else: filepath = sc.makefilepath(filename=popfile) sc.saveobj(filepath, people) if verbose: print( f'Saved population of type "{pop_type}" with {pop_size:n} people to {filepath}' ) return people
def load_population(self, popfile=None, **kwargs): ''' Load the population dictionary from file -- typically done automatically as part of sim.initialize(). Supports loading either saved population dictionaries (popdicts, file ending .pop by convention), or ready-to-go People objects (file ending .ppl by convention). Either object an also be supplied directly. Once a population file is loaded, it is removed from the Sim object. Args: popfile (str or obj): if a string, name of the file; otherwise, the popdict or People object to load kwargs (dict): passed to sc.makefilepath() ''' # Set the file path if not is provided if popfile is None and self.popfile is not None: popfile = self.popfile # Handle the population (if it exists) if popfile is not None: # Load from disk or use directly if isinstance(popfile, str): # It's a string, assume it's a filename filepath = sc.makefilepath(filename=popfile, **kwargs) obj = cvm.load(filepath) if self['verbose']: print(f'Loading population from {filepath}') else: obj = popfile # Use it directly # Process the input if isinstance(obj, dict): self.popdict = obj n_actual = len(self.popdict['uid']) layer_keys = self.popdict['layer_keys'] elif isinstance(obj, cvb.BasePeople): self.people = obj self.people.set_pars( self.pars ) # Replace the saved parameters with this simulation's n_actual = len(self.people) layer_keys = self.people.layer_keys() else: errormsg = f'Cound not interpret input of {type(obj)} as a population file: must be a dict or People object' raise ValueError(errormsg) # Perform validation n_expected = self['pop_size'] if n_actual != n_expected: errormsg = f'Wrong number of people ({n_expected:n} requested, {n_actual:n} actual) -- please change "pop_size" to match or regenerate the file' raise ValueError(errormsg) self.reset_layer_pars( force=False, layer_keys=layer_keys ) # Ensure that layer keys match the loaded population self.popfile = None # Once loaded, remove to save memory return
def save(self, filename=None, folder=None, verbose=2): ''' Save the current project, by default using its name, and without results ''' fullpath = sc.makefilepath(filename=filename, folder=folder, default=[self.filename, self.name], ext='prj', sanitize=True) self.filename = fullpath # Store file path sc.saveobj(fullpath, self, verbose=verbose) return fullpath
def save_population(self, filename, **kwargs): ''' Save the population dictionary to file. Args: filename (str): name of the file to save to. ''' filepath = sc.makefilepath(filename=filename, **kwargs) sc.saveobj(filepath, self.popdict) return filepath
def test_baseline(): ''' Compare the current default sim against the saved baseline ''' # Load existing baseline filepath = sc.makefilepath(filename=baseline_filename, folder=sc.thisdir(__file__)) baseline = sc.loadjson(filepath) old = baseline[baseline_key] # Calculate new baseline sim = cv.Sim(verbose=0) sim.run() new = sim.summary # Compare keys errormsg = '' old_keys = set(old.keys()) new_keys = set(new.keys()) if old_keys != new_keys: errormsg = f"Keys don't match!\n" missing = old_keys - new_keys extra = new_keys - old_keys if missing: errormsg += f' Missing old keys: {missing}\n' if extra: errormsg += f' Extra new keys: {extra}\n' mismatches = {} for key in old_keys.union(new_keys): old_val = old[key] if key in old else 'not present' new_val = new[key] if key in new else 'not present' if old_val != new_val: mismatches[key] = {'old': old_val, 'new': new_val} if len(mismatches): errormsg = '\nThe following values have changed between old and new!\n' errormsg += 'Please rerun "tests/unittests/update_baseline" if this is intentional.\n' errormsg += 'Mismatches:\n' space = ' ' * 17 for mkey, mval in mismatches.items(): errormsg += f' {mkey}:\n' errormsg += f'{space}old = {mval["old"]}\n' errormsg += f'{space}new = {mval["new"]}\n' # Raise an error if mismatches were found if errormsg: prefix = '\nThe following values have changed between the previous baseline and now!\n' prefix += 'If this is intentional, please rerun "update_baseline" and commit.\n\n' err = prefix + errormsg raise ValueError(err) else: print('Baseline matches') return new
def save(self, filename=None, keep_sims=True, keep_people=False, **kwargs): ''' Save to disk as a gzipped pickle. Args: filename (str or None): the name or path of the file to save to; if None, uses stored keep_sims (bool): whether or not to store the actual Sim objects in the Scenarios object keep_people (bool): whether or not to store the population in the Sim objects (NB, very large) keywords: passed to makefilepath() Returns: filename (str): the validated absolute path to the saved file **Example** :: scens.save() # Saves to a .scens file with the date and time of creation by default ''' if filename is None: filename = self.filename filename = sc.makefilepath(filename=filename, **kwargs) self.filename = filename # Store the actual saved filename # Store sims seperately sims = self.sims self.sims = None # Remove for now obj = sc.dcp(self) # This should be quick once we've removed the sims if not keep_people: obj.base_sim.shrink(in_place=True) if keep_sims: if keep_people: if not obj._kept_people: print( 'Warning: there are no people because they were not saved during the run. ' 'If you want people, please rerun with keep_people=True.' ) obj.sims = sims # Just restore the object in full print('Note: saving people, which may produce a large file!') else: obj.sims = sc.objdict() for key in sims.keys(): obj.sims[key] = [] for sim in sims[key]: obj.sims[key].append(sim.shrink(in_place=False)) sc.saveobj(filename=filename, obj=obj) # Actually save self.sims = sims # Restore return filename
def train(beta: float = 0.015, pop_infected: int = 10, rel_death_prob: float = 1.0, rel_severe_prob: float = 1.0, rel_crit_prob: float = 1.0, start_day: str = '2019-12-25', datafile='tests/example_data.csv') -> None: """ Perform hyperparameter sweep with Weights and Biases https://docs.wandb.com/sweeps """ sc.makefilepath(datafile, checkexists=True) pars = dict( beta=beta, pop_infected=pop_infected, rel_death_prob=rel_death_prob, rel_crit_prob=rel_crit_prob, start_day=start_day, ) # instantiate wandb run wb_handle = wandb.init(config=pars, project="covasim") run_id = wandb.run.id # Create and run the simulation sc.heading('Hyperparmeter Sweep') sim = cv.Sim(pars=pars, datafile=datafile) sim.run(verbose=False) likelihood = sim.likelihood() # log relevant metrics and artifacts wandb.log({'likelihood': likelihood}) sim.plot(do_show=False, do_save=True, fig_path=sc.makefilepath(folder=wandb.run.dir, filename=f'{run_id}.png')) wandb.save(datafile) sc.saveobj(folder=wandb.run.dir, filename=f'pars_{run_id}.pkl', f)
def load_people(self, filename, **kwargs): ''' Load the population dictionary from file. Args: filename (str): name of the file to load. ''' filepath = sc.makefilepath(filename=filename, **kwargs) self.popdict = sc.loadobj(filepath) n_actual = len(self.popdict['uid']) n_expected = self['n'] if n_actual != n_expected: errormsg = f'Wrong number of people ({n_expected} requested, {n_actual} actual) -- please change "n" to match or regenerate the file' raise ValueError(errormsg) return
def load(filename, **kwargs): ''' Load from disk from a gzipped pickle. Args: filename (str): the name or path of the file to save to keywords: passed to makefilepath() Returns: scens (Scenarios): the loaded scenarios object Example: sim = cv.Scenarios.load('my-scenarios.scens') ''' filename = sc.makefilepath(filename=filename, **kwargs) scens = sc.loadobj(filename=filename) return scens
def load(filename, **kwargs): ''' Load from disk from a gzipped pickle. Args: filename (str): the name or path of the file to save to keywords: passed to makefilepath() Returns: sim (Sim): the loaded simulation object Example: sim = cv.Sim.load('my-simulation.sim') ''' filename = sc.makefilepath(filename=filename, **kwargs) sim = sc.loadobj(filename=filename) return sim
def output(self): self.log.info(f"Final columns: {', '.join(self.df.columns)}") self.log.info("First rows of data:") self.log.info(self.df.head()) here = sc.thisdir(__file__) data_home = os.path.join(here, self.output_folder) for g in self.grouping: key_value = g[0] filename = f'{sc.sanitizefilename(key_value)}.csv' filepath = sc.makefilepath(filename=filename, folder=data_home) self.log.info(f'Creating {filepath}') mini_df = self.df[self.df.key == key_value] mini_df.to_csv(filepath) self.log.info( f"There are {len(self.grouping)} entities in this dataset.") self.log.info(f"Saved {len(self.df)} records.")
def load_population(self, popfile=None, **kwargs): ''' Load the population dictionary from file -- typically done automatically as part of sim.initialize(load_pop=True). Args: popfile (str): name of the file to load ''' if popfile is None and self.popfile is not None: popfile = self.popfile if popfile is not None: filepath = sc.makefilepath(filename=popfile, **kwargs) self.popdict = sc.loadobj(filepath) n_actual = len(self.popdict['uid']) n_expected = self['pop_size'] if n_actual != n_expected: errormsg = f'Wrong number of people ({n_expected:n} requested, {n_actual:n} actual) -- please change "pop_size" to match or regenerate the file' raise ValueError(errormsg) if self['verbose']: print(f'Loaded population from {filepath}') return
def save(self, filename=None, **kwargs): ''' Save to disk as a gzipped pickle. Args: filename (str or None): the name or path of the file to save to; if None, uses stored keywords: passed to makefilepath() Returns: filename (str): the validated absolute path to the saved file Example: sim.save() # Saves to a .sim file with the date and time of creation by default ''' if filename is None: filename = self.filename filename = sc.makefilepath(filename=filename, **kwargs) self.filename = filename # Store the actual saved filename sc.saveobj(filename=filename, obj=self) return filename
def save(self, filename=None, keep_people=False, **kwargs): ''' Save to disk as a gzipped pickle. Load with cv.load(filename) or cv.MultiSim.load(filename). Args: filename (str) : the name or path of the file to save to; if None, uses default keep_people (bool) : whether or not to store the population in the Sim objects (NB, very large) kwargs (dict) : passed to makefilepath() Returns: scenfile (str): the validated absolute path to the saved file **Example**:: msim.save() # Saves to an .msim file ''' if filename is None: filename = 'covasim.msim' msimfile = sc.makefilepath(filename=filename, **kwargs) self.filename = filename # Store the actual saved filename # Store sims seperately sims = self.sims self.sims = None # Remove for now obj = sc.dcp(self) # This should be quick once we've removed the sims if keep_people: obj.sims = sims # Just restore the object in full print('Note: saving people, which may produce a large file!') else: obj.base_sim.shrink(in_place=True) obj.sims = [] for sim in sims: obj.sims.append(sim.shrink(in_place=False)) cvm.save(filename=msimfile, obj=obj) # Actually save self.sims = sims # Restore return msimfile
def save(self, filename=None, keep_people=None, skip_attrs=None, **kwargs): ''' Save to disk as a gzipped pickle. Args: filename (str or None): the name or path of the file to save to; if None, uses stored kwargs: passed to sc.makefilepath() Returns: filename (str): the validated absolute path to the saved file **Example**:: sim.save() # Saves to a .sim file with the date and time of creation by default ''' # Set keep_people based on whether or not we're in the middle of a run if keep_people is None: if self.initialized and not self.results_ready: keep_people = True else: keep_people = False # Handle the filename if filename is None: filename = self.simfile filename = sc.makefilepath(filename=filename, **kwargs) self.filename = filename # Store the actual saved filename # Handle the shrinkage and save if skip_attrs or not keep_people: obj = self.shrink(skip_attrs=skip_attrs, in_place=False) else: obj = self cvm.save(filename=filename, obj=obj) return filename
def make_people(sim, popdict=None, save_pop=False, popfile=None, die=True, reset=False, verbose=None, **kwargs): ''' Make the actual people for the simulation. Usually called via sim.initialize(), not directly by the user. Args: sim (Sim) : the simulation object popdict (dict) : if supplied, use this population dictionary rather than generate a new one save_pop (bool) : whether to save the population to disk popfile (bool) : if so, the filename to save to die (bool) : whether or not to fail if synthetic populations are requested but not available reset (bool) : whether to force population creation even if self.popdict/self.people exists verbose (bool) : level of detail to print kwargs (dict) : passed to make_randpop() or make_synthpop() Returns: people (People): people ''' # Set inputs and defaults pop_size = int(sim['pop_size']) # Shorten pop_type = sim['pop_type'] # Shorten if verbose is None: verbose = sim['verbose'] if popfile is None: popfile = sim.popfile # Check which type of population to produce if pop_type == 'synthpops': if not cvreq.check_synthpops(): errormsg = f'You have requested "{pop_type}" population, but synthpops is not available; please use random, clustered, or hybrid' if die: raise ValueError(errormsg) else: print(errormsg) pop_type = 'random' location = sim['location'] if location: print( f'Warning: not setting ages or contacts for "{location}" since synthpops contacts are pre-generated' ) # Actually create the population if sim.people and not reset: return sim.people # If it's already there, just return elif sim.popdict and not reset: popdict = sim.popdict # Use stored one sim.popdict = None # Once loaded, remove elif popdict is None: # Main use case: no popdict is supplied # Create the population if pop_type in ['random', 'clustered', 'hybrid']: popdict = make_randpop(sim, microstructure=pop_type, **kwargs) elif pop_type == 'synthpops': popdict = make_synthpop(sim, **kwargs) elif pop_type is None: errormsg = f'You have set pop_type=None. This is fine, but you must ensure sim.popdict exists before calling make_people().' raise ValueError(errormsg) else: errormsg = f'Population type "{pop_type}" not found; choices are random, clustered, hybrid, or synthpops' raise ValueError(errormsg) # Ensure prognoses are set if sim['prognoses'] is None: sim['prognoses'] = cvpars.get_prognoses(sim['prog_by_age']) # Actually create the people people = cvppl.People( sim.pars, uid=popdict['uid'], age=popdict['age'], sex=popdict['sex'], contacts=popdict['contacts']) # List for storing the people average_age = sum(popdict['age'] / pop_size) sc.printv( f'Created {pop_size} people, average age {average_age:0.2f} years', 2, verbose) if save_pop: if popfile is None: errormsg = 'Please specify a file to save to using the popfile kwarg' raise FileNotFoundError(errormsg) else: filepath = sc.makefilepath(filename=popfile) cvm.save(filepath, people) if verbose: print( f'Saved population of type "{pop_type}" with {pop_size:n} people to {filepath}' ) return people
# Use internal data to create dates; only keep `date` column df['date'] = pd.to_datetime(df[['year', 'month', 'day']]) df.drop(['dateRep', 'day', 'month', 'year'], inplace=True, axis=1) # Just to be safe, let's sort by name and date. Probably not # necessary but better safe than sorry!. df = df.sort_values(['countriesAndTerritories', 'date']) # Each data set has a unique name. Let's create groups. g = df.groupby('countriesAndTerritories') # The parameter 'day' is the number of days since the first # day of data collection for the group. df['day'] = g['date'].transform(lambda x: (x - min(x))).apply(lambda x: x.days) # We'll 'rename' some of the columns to be consistent # with the parameters file. df['new_positives'] = df.cases df['new_death'] = df.deaths df['population'] = df.popData2018 df.drop(['cases', 'deaths', 'popData2018'], inplace=True, axis=1) # And save it to the data directory. here = sc.thisdir(__file__) data_home = os.path.join(here, subfolder) filepath = sc.makefilepath(filename=outputfile, folder=data_home) log.info(f"Saving to {filepath}") df.to_csv(filepath) log.info(f"Script complete")
def plot(self, to_plot=None, do_save=None, fig_path=None, fig_args=None, plot_args=None, axis_args=None, fill_args=None, legend_args=None, as_dates=True, dateformat=None, interval=None, n_cols=1, font_size=18, font_family=None, grid=True, commaticks=True, do_show=True, sep_figs=False, verbose=None): ''' Plot the results -- can supply arguments for both the figure and the plots. Args: to_plot (dict): Dict of results to plot; see default_scen_plots for structure do_save (bool): Whether or not to save the figure fig_path (str): Path to save the figure fig_args (dict): Dictionary of kwargs to be passed to pl.figure() plot_args (dict): Dictionary of kwargs to be passed to pl.plot() axis_args (dict): Dictionary of kwargs to be passed to pl.subplots_adjust() fill_args (dict): Dictionary of kwargs to be passed to pl.fill_between() legend_args (dict): Dictionary of kwargs to be passed to pl.legend() as_dates (bool): Whether to plot the x-axis as dates or time points dateformat (str): Date string format, e.g. '%B %d' interval (int): Interval between tick marks n_cols (int): Number of columns of subpanels to use for subplot font_size (int): Size of the font font_family (str): Font face grid (bool): Whether or not to plot gridlines commaticks (bool): Plot y-axis with commas rather than scientific notation do_show (bool): Whether or not to show the figure sep_figs (bool): Whether to show separate figures for different results instead of subplots verbose (bool): Display a bit of extra information Returns: fig: Figure handle ''' if verbose is None: verbose = self['verbose'] sc.printv('Plotting...', 1, verbose) if to_plot is None: to_plot = cvd.default_scen_plots to_plot = sc.dcp(to_plot) # In case it's supplied as a dict # Handle input arguments -- merge user input with defaults fig_args = sc.mergedicts({'figsize': (16, 14)}, fig_args) plot_args = sc.mergedicts({'lw': 3, 'alpha': 0.7}, plot_args) axis_args = sc.mergedicts( { 'left': 0.10, 'bottom': 0.05, 'right': 0.95, 'top': 0.90, 'wspace': 0.25, 'hspace': 0.25 }, axis_args) fill_args = sc.mergedicts({'alpha': 0.2}, fill_args) legend_args = sc.mergedicts({'loc': 'best'}, legend_args) if sep_figs: figs = [] else: fig = pl.figure(**fig_args) pl.subplots_adjust(**axis_args) pl.rcParams['font.size'] = font_size if font_family: pl.rcParams['font.family'] = font_family n_rows = np.ceil(len(to_plot) / n_cols) # Number of subplot rows to have for rk, reskey in enumerate(to_plot): title = self.base_sim.results[ reskey].name # Get the name of this result from the base simulation if sep_figs: figs.append(pl.figure(**fig_args)) ax = pl.subplot(111) else: ax = pl.subplot(n_rows, n_cols, rk + 1) resdata = self.results[reskey] for scenkey, scendata in resdata.items(): pl.fill_between(self.tvec, scendata.low, scendata.high, **fill_args) pl.plot(self.tvec, scendata.best, label=scendata.name, **plot_args) pl.title(title) if rk == 0: pl.legend(**legend_args) pl.grid(grid) if commaticks: sc.commaticks() if self.base_sim.data is not None and reskey in self.base_sim.data: data_t = np.array( (self.base_sim.data.index - self.base_sim['start_day']) / np.timedelta64(1, 'D')) pl.plot(data_t, self.base_sim.data[reskey], 'sk', **plot_args) # Optionally reset tick marks (useful for e.g. plotting weeks/months) if interval: xmin, xmax = ax.get_xlim() ax.set_xticks(pl.arange(xmin, xmax + 1, interval)) # Set xticks as dates if as_dates: @ticker.FuncFormatter def date_formatter(x, pos): return (self.base_sim['start_day'] + dt.timedelta(days=x)).strftime('%b-%d') ax.xaxis.set_major_formatter(date_formatter) if not interval: ax.xaxis.set_major_locator( ticker.MaxNLocator(integer=True)) # Ensure the figure actually renders or saves if do_save: if fig_path is None: # No figpath provided - see whether do_save is a figpath fig_path = 'covasim_scenarios.png' # Just give it a default name fig_path = sc.makefilepath( fig_path) # Ensure it's valid, including creating the folder pl.savefig(fig_path) if do_show: pl.show() else: pl.close(fig) return fig
def plot(self, to_plot=None, do_save=None, fig_path=None, fig_args=None, plot_args=None, scatter_args=None, axis_args=None, as_dates=True, interval=None, dateformat=None, font_size=18, font_family=None, use_grid=True, use_commaticks=True, do_show=True, verbose=None): ''' Plot the results -- can supply arguments for both the figure and the plots. Args: to_plot (dict): Nested dict of results to plot; see default_sim_plots for structure do_save (bool or str): Whether or not to save the figure. If a string, save to that filename. fig_path (str): Path to save the figure fig_args (dict): Dictionary of kwargs to be passed to pl.figure() plot_args (dict): Dictionary of kwargs to be passed to pl.plot() scatter_args (dict): Dictionary of kwargs to be passed to pl.scatter() axis_args (dict): Dictionary of kwargs to be passed to pl.subplots_adjust() as_dates (bool): Whether to plot the x-axis as dates or time points interval (int): Interval between tick marks dateformat (str): Date string format, e.g. '%B %d' font_size (int): Size of the font font_family (str): Font face use_grid (bool): Whether or not to plot gridlines use_commaticks (bool): Plot y-axis with commas rather than scientific notation do_show (bool): Whether or not to show the figure verbose (bool): Display a bit of extra information Returns: fig: Figure handle ''' if verbose is None: verbose = self['verbose'] sc.printv('Plotting...', 1, verbose) if to_plot is None: to_plot = default_sim_plots to_plot = sc.odict(to_plot) # In case it's supplied as a dict # Handle input arguments -- merge user input with defaults fig_args = sc.mergedicts({'figsize': (16, 12)}, fig_args) plot_args = sc.mergedicts({'lw': 3, 'alpha': 0.7}, plot_args) scatter_args = sc.mergedicts({'s': 150, 'marker': 's'}, scatter_args) axis_args = sc.mergedicts( { 'left': 0.1, 'bottom': 0.05, 'right': 0.9, 'top': 0.97, 'wspace': 0.2, 'hspace': 0.25 }, axis_args) fig = pl.figure(**fig_args) pl.subplots_adjust(**axis_args) pl.rcParams['font.size'] = font_size if font_family: pl.rcParams['font.family'] = font_family res = self.results # Shorten since heavily used # Plot everything colors = sc.gridcolors(max([len(tp) for tp in to_plot.values()])) # Define the data mapping. Must be here since uses functions if self.data is not None and len(self.data): data_mapping = { 'cum_exposed': pl.cumsum(self.data['new_infections']), 'cum_diagnosed': pl.cumsum(self.data['new_positives']), 'cum_tested': pl.cumsum(self.data['new_tests']), 'infections': self.data['new_infections'], 'tests': self.data['new_tests'], 'diagnoses': self.data['new_positives'], } else: data_mapping = {} for p, title, keylabels in to_plot.enumitems(): ax = pl.subplot(2, 1, p + 1) for i, key, label in keylabels.enumitems(): this_color = colors[i] y = res[key].values pl.plot(res['t'], y, label=label, **plot_args, c=this_color) if key in data_mapping: pl.scatter(self.data['day'], data_mapping[key], c=[this_color], **scatter_args) if self.data is not None and len(self.data): pl.scatter(pl.nan, pl.nan, c=[(0, 0, 0)], label='Data', **scatter_args) pl.grid(use_grid) cvu.fixaxis(self) if use_commaticks: sc.commaticks() pl.title(title) # Optionally reset tick marks (useful for e.g. plotting weeks/months) if interval: xmin, xmax = ax.get_xlim() ax.set_xticks(pl.arange(xmin, xmax + 1, interval)) # Set xticks as dates if as_dates: xticks = ax.get_xticks() xticklabels = self.inds2dates(xticks, dateformat=dateformat) ax.set_xticklabels(xticklabels) # Plot interventions for intervention in self['interventions']: intervention.plot(self, ax) # Ensure the figure actually renders or saves if do_save: if fig_path is None: # No figpath provided - see whether do_save is a figpath if isinstance(do_save, str): fig_path = do_save # It's a string, assume it's a filename else: fig_path = 'covasim.png' # Just give it a default name fig_path = sc.makefilepath( fig_path) # Ensure it's valid, including creating the folder pl.savefig(fig_path) if do_show: pl.show() else: pl.close(fig) return fig
def plot(self, to_plot=None, do_save=None, fig_path=None, fig_args=None, plot_args=None, axis_args=None, fill_args=None, as_dates=True, interval=None, dateformat=None, font_size=18, font_family=None, grid=True, commaticks=True, do_show=True, sep_figs=False, verbose=None): ''' Plot the results -- can supply arguments for both the figure and the plots. Args: to_plot (dict): Dict of results to plot; see default_scen_plots for structure do_save (bool): Whether or not to save the figure fig_path (str): Path to save the figure fig_args (dict): Dictionary of kwargs to be passed to pl.figure() plot_args (dict): Dictionary of kwargs to be passed to pl.plot() axis_args (dict): Dictionary of kwargs to be passed to pl.subplots_adjust() fill_args (dict): Dictionary of kwargs to be passed to pl.fill_between() as_dates (bool): Whether to plot the x-axis as dates or time points interval (int): Interval between tick marks dateformat (str): Date string format, e.g. '%B %d' font_size (int): Size of the font font_family (str): Font face grid (bool): Whether or not to plot gridlines commaticks (bool): Plot y-axis with commas rather than scientific notation do_show (bool): Whether or not to show the figure sep_figs (bool): Whether to show separate figures for different results instead of subplots verbose (bool): Display a bit of extra information Returns: fig: Figure handle ''' if verbose is None: verbose = self['verbose'] sc.printv('Plotting...', 1, verbose) if to_plot is None: to_plot = default_scen_plots to_plot = sc.odict(sc.dcp(to_plot)) # In case it's supplied as a dict # Handle input arguments -- merge user input with defaults fig_args = sc.mergedicts({'figsize': (16, 12)}, fig_args) plot_args = sc.mergedicts({'lw': 3, 'alpha': 0.7}, plot_args) axis_args = sc.mergedicts({'left': 0.10, 'bottom': 0.05, 'right': 0.95, 'top': 0.90, 'wspace': 0.5, 'hspace': 0.25}, axis_args) fill_args = sc.mergedicts({'alpha': 0.2}, fill_args) if sep_figs: figs = [] else: fig = pl.figure(**fig_args) pl.subplots_adjust(**axis_args) pl.rcParams['font.size'] = font_size if font_family: pl.rcParams['font.family'] = font_family # %% Plotting for rk,reskey,title in to_plot.enumitems(): if sep_figs: figs.append(pl.figure(**fig_args)) ax = pl.subplot(111) else: ax = pl.subplot(len(to_plot), 1, rk + 1) resdata = self.allres[reskey] for scenkey, scendata in resdata.items(): pl.fill_between(self.tvec, scendata.low, scendata.high, **fill_args) pl.plot(self.tvec, scendata.best, label=scendata.name, **plot_args) pl.title(title) if rk == 0: pl.legend(loc='best') pl.grid(grid) if commaticks: sc.commaticks() # Optionally reset tick marks (useful for e.g. plotting weeks/months) if interval: xmin,xmax = ax.get_xlim() ax.set_xticks(pl.arange(xmin, xmax+1, interval)) # Set xticks as dates if as_dates: xticks = ax.get_xticks() xticklabels = self.base_sim.inds2dates(xticks, dateformat=dateformat) ax.set_xticklabels(xticklabels) # Ensure the figure actually renders or saves if do_save: if fig_path is None: # No figpath provided - see whether do_save is a figpath fig_path = 'covasim_scenarios.png' # Just give it a default name fig_path = sc.makefilepath(fig_path) # Ensure it's valid, including creating the folder pl.savefig(fig_path) if do_show: pl.show() else: pl.close(fig) return fig
def plot(self, to_plot=None, do_save=None, fig_path=None, fig_args=None, plot_args=None, scatter_args=None, axis_args=None, legend_args=None, as_dates=True, dateformat=None, interval=None, n_cols=1, font_size=18, font_family=None, use_grid=True, use_commaticks=True, do_show=True, verbose=None): ''' Plot the results -- can supply arguments for both the figure and the plots. Args: to_plot (dict): Nested dict of results to plot; see default_sim_plots for structure do_save (bool or str): Whether or not to save the figure. If a string, save to that filename. fig_path (str): Path to save the figure fig_args (dict): Dictionary of kwargs to be passed to pl.figure() plot_args (dict): Dictionary of kwargs to be passed to pl.plot() scatter_args (dict): Dictionary of kwargs to be passed to pl.scatter() axis_args (dict): Dictionary of kwargs to be passed to pl.subplots_adjust() legend_args (dict): Dictionary of kwargs to be passed to pl.legend() as_dates (bool): Whether to plot the x-axis as dates or time points dateformat (str): Date string format, e.g. '%B %d' interval (int): Interval between tick marks n_cols (int): Number of columns of subpanels to use for subplot font_size (int): Size of the font font_family (str): Font face use_grid (bool): Whether or not to plot gridlines use_commaticks (bool): Plot y-axis with commas rather than scientific notation do_show (bool): Whether or not to show the figure verbose (bool): Display a bit of extra information Returns: fig: Figure handle ''' if verbose is None: verbose = self['verbose'] sc.printv('Plotting...', 1, verbose) if to_plot is None: to_plot = cvd.default_sim_plots to_plot = sc.odict(to_plot) # In case it's supplied as a dict # Handle input arguments -- merge user input with defaults fig_args = sc.mergedicts({'figsize': (16, 14)}, fig_args) plot_args = sc.mergedicts({'lw': 3, 'alpha': 0.7}, plot_args) scatter_args = sc.mergedicts({'s': 70, 'marker': 's'}, scatter_args) axis_args = sc.mergedicts( { 'left': 0.1, 'bottom': 0.05, 'right': 0.9, 'top': 0.97, 'wspace': 0.2, 'hspace': 0.25 }, axis_args) legend_args = sc.mergedicts({'loc': 'best'}, legend_args) fig = pl.figure(**fig_args) pl.subplots_adjust(**axis_args) pl.rcParams['font.size'] = font_size if font_family: pl.rcParams['font.family'] = font_family res = self.results # Shorten since heavily used # Plot everything n_rows = np.ceil(len(to_plot) / n_cols) # Number of subplot rows to have for p, title, keylabels in to_plot.enumitems(): ax = pl.subplot(n_rows, n_cols, p + 1) for key in keylabels: label = res[key].name this_color = res[key].color y = res[key].values pl.plot(res['t'], y, label=label, **plot_args, c=this_color) if self.data is not None and key in self.data: data_t = ( self.data.index - self['start_day'] ) / np.timedelta64( 1, 'D' ) # Convert from data date to model output index based on model start date pl.scatter(data_t, self.data[key], c=[this_color], **scatter_args) if self.data is not None and len(self.data): pl.scatter(pl.nan, pl.nan, c=[(0, 0, 0)], label='Data', **scatter_args) pl.legend(**legend_args) pl.grid(use_grid) sc.setylim() if use_commaticks: sc.commaticks() pl.title(title) # Optionally reset tick marks (useful for e.g. plotting weeks/months) if interval: xmin, xmax = ax.get_xlim() ax.set_xticks(pl.arange(xmin, xmax + 1, interval)) # Set xticks as dates if as_dates: @ticker.FuncFormatter def date_formatter(x, pos): return (self['start_day'] + dt.timedelta(days=x)).strftime('%b-%d') ax.xaxis.set_major_formatter(date_formatter) if not interval: ax.xaxis.set_major_locator( ticker.MaxNLocator(integer=True)) # Plot interventions for intervention in self['interventions']: intervention.plot(self, ax) # Ensure the figure actually renders or saves if do_save: if fig_path is None: # No figpath provided - see whether do_save is a figpath if isinstance(do_save, str): fig_path = do_save # It's a string, assume it's a filename else: fig_path = 'covasim.png' # Just give it a default name fig_path = sc.makefilepath( fig_path) # Ensure it's valid, including creating the folder pl.savefig(fig_path) if do_show: pl.show() else: pl.close(fig) return fig
def plot_school_sizes(pop, **kwargs): """ Plot a comparison of the expected and generated school size distribution for each type of school expected. Args: pop (pop object) : population, either synthpops.pop.Pop, or dict **with_school_types (type) : If True, plot school size distributions by type, else plot overall school size distributions **keys_to_exclude (str or list) : school types to exclude **left (float) : Matplotlib.figure.subplot.left **right (float) : Matplotlib.figure.subplot.right **top (float) : Matplotlib.figure.subplot.top **bottom (float) : Matplotlib.figure.subplot.bottom **hspace (float) : Matplotlib.figure.subplot.hspace **subplot_height (float) : height of subplot in inches **subplot_width (float) : width of subplot in inches **screen_height_factor (float) : fraction of the screen height to use for display **location_text_y (float) : height to add location text to figure **fontsize (float) : Matplotlib.figure.fontsize **rotation (float) : rotation angle for xticklabels **cmap (str) : colormap **figname (str) : name to save figure to disk **comparison (bool) : If True, plot comparison to the generated population Returns: Matplotlib figure and axes. Note: If using pop with type covasim.people.Pop or dict, args must be supplied for the location parameters to get the expected distribution. **Example**:: pars = {'n': 10e3, location='seattle_metro', state_location='Washington', country_location='usa'} pop = sp.Pop(**pars) fig, ax = pop.plot_school_sizes_by_type() popdict = pop.to_dict() kwargs = pars.copy() kwargs['datadir'] = sp.datadir fig, ax = sp.plot_school_sizes(popdict, **kwargs) """ plkwargs = get_plkwargs(pop) # method specific plotting defaults method_defaults = dict( with_school_types=False, keys_to_exclude=['uv'], left=0.11, right=0.94, top=0.96, bottom=0.08, hspace=0.75, subplot_height=2.8, subplot_width=4.2, screen_height_factor=0.85, location_text_y=113, fontsize=8, rotation=25, cmap='cmo.curl', figname='school_size_distribution_by_type', comparison=True, school_type_labels=spsch.get_school_type_labels(), ) plkwargs.update_defaults(method_defaults, kwargs) plkwargs.set_font() if isinstance(plkwargs.keys_to_exclude, str): plkwargs.keys_to_exclude = [plkwargs.keys_to_exclude ] # ensure this is treated as a list # define after plkwargs gets updated if isinstance(pop, sppop.Pop): plkwargs.loc_pars = pop.loc_pars plkwargs.smooth_ages = pop.smooth_ages plkwargs.window_length = pop.window_length popdict = sc.dcp(pop.to_dict()) elif isinstance(pop, dict): popdict = sc.dcp(pop) else: raise ValueError( f"This method does not support pop objects with the type {type(pop)}. Please look at the notes and try another supported pop type." ) # now check for missing plkwargs and use default values if not found plkwargs.set_default_pop_pars() if plkwargs.with_school_types: expected_school_size_dist = spdata.get_school_size_distr_by_type( **plkwargs.loc_pars) else: plkwargs.school_type_labels = {None: ''} expected_school_size_dist = { None: spdata.get_school_size_distr_by_brackets(**plkwargs.loc_pars) } school_size_brackets = spdata.get_school_size_brackets(**plkwargs.loc_pars) bins = spsch.get_bin_edges(school_size_brackets) bin_labels = spsch.get_bin_labels(school_size_brackets) # calculate how many students are in each school if plkwargs.comparison: enrollment_by_school_type = spsch.get_enrollment_by_school_type( popdict, **dict(with_school_types=plkwargs.with_school_types, keys_to_exclude=plkwargs.keys_to_exclude)) generated_school_size_dist = sc.objdict( spsch.get_generated_school_size_distributions( enrollment_by_school_type, bins)) for school_type in plkwargs.keys_to_exclude: expected_school_size_dist.pop(school_type, None) plkwargs.school_type_labels.pop(school_type, None) sorted_school_types = sorted(expected_school_size_dist.keys()) n_school_types = len(sorted_school_types) plkwargs.nrows = n_school_types # location text if plkwargs.location is not None: location_text = f"{plkwargs.location.replace('_', ' ').title()}" else: location_text = f"{cfg.default_location.replace('_', ' ').title()}" # create fig, ax, set cmap fig, ax = plt.subplots(n_school_types, 1, figsize=(plkwargs.display_width, plkwargs.display_height), dpi=plkwargs.display_dpi) cmap = mplt.cm.get_cmap(plkwargs.cmap) # readjust figure parameters if plkwargs.nrows == 1: ax = [ax] fig.set_size_inches(plkwargs.display_width, plkwargs.display_height * 0.47) plkwargs.update(dict(top=0.88, bottom=0.18, left=0.12)) plkwargs.location_text_y = 105.5 # default value for singular school type -- you have the ability to change this by supplying the kwarg location_text_y # update the fig fig.subplots_adjust(**plkwargs.axis) for ns, school_type in enumerate(plkwargs.school_type_labels.keys()): x = np.arange( len(school_size_brackets) ) # potentially will use different bins for each school type so placeholder for now c = ns / n_school_types c2 = min(c + 0.12, 1) sorted_bins = sorted(expected_school_size_dist[school_type].keys()) ax[ns].bar(x, [ expected_school_size_dist[school_type][b] * 100 for b in sorted_bins ], color=cmap(c), edgecolor=cmap(c2), label='Expected', zorder=0) if plkwargs.comparison: ax[ns].plot(x, [ generated_school_size_dist[school_type][b] * 100 for b in sorted_bins ], color=cmap(c2), ls='--', marker='o', markerfacecolor=cmap(c2), markeredgecolor='white', markeredgewidth=.5, markersize=plkwargs.markersize, label='Generated', zorder=1) leg = ax[ns].legend(loc=1, fontsize=plkwargs.fontsize) leg.draw_frame(False) ax[ns].set_xticks(x) ax[ns].set_xticklabels(bin_labels, rotation=plkwargs.rotation, fontsize=plkwargs.fontsize, verticalalignment='center_baseline') ax[ns].set_xlim(-0.6 + x[0], x[-1] + 0.6) ax[ns].set_ylim(0, 100) ax[ns].set_ylabel('%', fontsize=plkwargs.fontsize + 1) ax[ns].tick_params(labelsize=plkwargs.fontsize - 1) if school_type is None: title = "Without school types defined" else: title = f"{plkwargs.school_type_labels[school_type]}" if ns == 0: ax[ns].text(-0.6, plkwargs.location_text_y, location_text, horizontalalignment='left', fontsize=plkwargs.fontsize + 1, verticalalignment='top') ax[ns].set_title(title, fontsize=plkwargs.fontsize + 1, verticalalignment='top') ax[ns].set_xlabel('School size', fontsize=plkwargs.fontsize + 1, verticalalignment='center_baseline') # for multipanel figures, first display then re-adjust it and save to disk if plkwargs.do_show: plt.show() # update fig before saving to disk since display will modify things if plkwargs.do_save: if len(ax) == 1: fig.set_size_inches(plkwargs.width, plkwargs.height) else: cfg.logger.info( "Setting default plotting parameters to save figure to disk. If these settings produce figures you would prefer to change, this method returns the figure and ax for you to modify and save to disk." ) fig.set_size_inches(plkwargs.display_width, plkwargs.display_height) plkwargs.update(dict(bottom=0.075, hspace=0.52, left=0.12)) fig.subplots_adjust(**plkwargs.axis) plkwargs.figpath = sc.makefilepath(filename=plkwargs.figname, folder=plkwargs.figdir, ext=plkwargs.format) fig.savefig(plkwargs.figpath, format=plkwargs.format, dpi=plkwargs.save_dpi) return fig, ax
def make_people(sim, save_pop=False, popfile=None, verbose=None, die=True, reset=False): ''' Make the actual people for the simulation. Args: sim (Sim): the simulation object verbose (bool): level of detail to print id_len (int): length of ID for each person (default: calculate required length based on the number of people) die (bool): whether or not to fail if synthetic populations are requested but not available reset (bool): whether to force population creation even if self.popdict exists Returns: None. ''' # Set inputs and defaults pop_size = int(sim['pop_size']) # Shorten pop_type = sim['pop_type'] # Shorten if verbose is None: verbose = sim['verbose'] if popfile is None: popfile = sim.popfile # Check which type of population to produce if pop_type == 'synthpops': if not cvreq.check_synthpops(): errormsg = f'You have requested "{pop_type}" population, but synthpops is not available; please use random, clustered, or hybrid' if die: raise ValueError(errormsg) else: print(errormsg) pop_type = 'random' location = sim['location'] if location: print( f'Warning: not setting ages or contacts for "{location}" since synthpops contacts are pre-generated' ) # Actually create the population if sim.popdict and not reset: popdict = sim.popdict # Use stored one layer_keys = list(popdict['contacts'] [0].keys()) # Assume there's at least one contact! sim.popdict = None # Once loaded, remove else: # Create the population if pop_type in ['random', 'clustered', 'hybrid']: popdict, layer_keys = make_randpop(sim, microstructure=pop_type) elif pop_type == 'synthpops': popdict, layer_keys = make_synthpop(sim) else: errormsg = f'Population type "{pop_type}" not found; choices are random, clustered, hybrid, or synthpops' raise ValueError(errormsg) # Ensure prognoses are set if sim['prognoses'] is None: sim['prognoses'] = cvpars.get_prognoses(sim['prog_by_age']) # Actually create the people sim.layer_keys = layer_keys people = cvppl.People(sim.pars, **popdict) # List for storing the people sim.people = people average_age = sum(popdict['age'] / pop_size) sc.printv( f'Created {pop_size} people, average age {average_age:0.2f} years', 2, verbose) if save_pop: if popfile is None: errormsg = 'Please specify a file to save to using the popfile kwarg' raise FileNotFoundError(errormsg) else: filepath = sc.makefilepath(filename=popfile) sc.saveobj(filepath, popdict) if verbose: print( f'Saved population of type "{pop_type}" with {pop_size:n} people to {filepath}' ) return