utils
Numerical utilities and other helper functions
What is shared by the rest of the code, and doesn’t belong to any one part of it: named dictionaries of modules (ndict), warnings, cleaning input data, plotting arguments, and returning figures and tables.
Used with: agents and compartments; declared and coded modules
Classes
| Name | Description |
|---|---|
| Shrunk | What shrink() puts in place of an attribute it removes, so that reading it says it was removed on purpose (rather than being None, or missing) |
| method_wrapper | A method that Starsim wraps in a subclass (e.g. init(), see outermost); a subclass defines __call__(self, obj, *args, **kwargs), which calls self.func |
| ndict | A dictionary of named items, such as a sim’s modules (sim.diseases), which can also be called for its values (sim.diseases()) |
| outermost | The outermost call to a sim’s init() (Sim’s, or a subclass’s that calls it) first keeps a copy of the sim as it is, which sim.new() copies |
| skipkey | The name of a key for sc.search() to skip, which is equal only to a string of that name |
Shrunk
utils.Shrunk(obj=None, attr=None)What shrink() puts in place of an attribute it removes, so that reading it says it was removed on purpose (rather than being None, or missing)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| obj | object |
the object that was shrunken | None |
| attr | str |
the name of its attribute that was shrunken | None |
method_wrapper
utils.method_wrapper(func)A method that Starsim wraps in a subclass (e.g. init(), see outermost); a subclass defines __call__(self, obj, *args, **kwargs), which calls self.func
It is a class rather than a closure so that a class defined in a script, which dill pickles by value (e.g. to send a sim to ss.parallel()‘s workers), pickles with its own method and not with the globals of the module that wrapped it: dill copies those, and with them, by value, any module not installed in site-packages (e.g. Sciris or Starsim installed as editable), which may not pickle at all (“cannot pickle’_thread._local’ object”).
ndict
utils.ndict(*args, type=None, **kwargs)A dictionary of named items, such as a sim’s modules (sim.diseases), which can also be called for its values (sim.diseases())
Each item is added under its name (or the key given), and a key can be added only once, so that two modules can’t silently take the same name.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| type | type |
the type each item must be (a module of any type is also accepted) | None |
| args | list |
the items to add, each a named item, or a list or dict of them | |
| kwargs | dict |
more items to add, by key |
Examples
networks = ndict(ss.SexualNet(), ss.PrenatalNet())
networks = ndict([ss.SexualNet(), ss.PrenatalNet()])
networks = ndict({'sexual':ss.SexualNet(), 'prenatal':ss.PrenatalNet()})Methods
| Name | Description |
|---|---|
| append | Add an item, or a list or dict of them |
| copy | Shallow copy |
| extend | Add new items to the ndict, by item, list, or dict |
| get | Get an entry from this ndict |
| merge | Merge another dictionary with this one |
append
utils.ndict.append(arg, key=None)Add an item, or a list or dict of them
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| arg | Module / list / dict |
the item to add, or a list or dict of them | required |
| key | str |
the key to add it under (default: its name) | None |
copy
utils.ndict.copy()Shallow copy
extend
utils.ndict.extend(*args, **kwargs)Add new items to the ndict, by item, list, or dict
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | list |
the items to add | |
| kwargs | dict |
more items to add, by key |
get
utils.ndict.get(key, default=None, match_case=False)Get an entry from this ndict
See also sim.get_module(). Note: if a type is supplied that matches more than one entry, this function only returns the first match. Use Sim.get_modules() to handle multiple matches.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| key | str / type |
the object to get, either a type (e.g. ss.SIR) or a string (e.g. 'sir') |
required |
| default | obj |
what to return if not found (default None) | None |
| match_case | bool |
if False (default), ignore case with string matching | False |
Examples
sim = ss.Sim(diseases=ss.SIR(name='MySIR'), networks='random')
sim.run()
# All return sim.diseases.MySIR
sim.diseases.get('MySIR')
sim.diseases.get('mysir')
sim.diseases.get(ss.SIR)merge
utils.ndict.merge(other)Merge another dictionary with this one
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| other | dict |
the dictionary to merge | required |
outermost
utils.outermost(func)The outermost call to a sim’s init() (Sim’s, or a subclass’s that calls it) first keeps a copy of the sim as it is, which sim.new() copies
skipkey
utils.skipkey()The name of a key for sc.search() to skip, which is equal only to a string of that name
sc.search() compares each key it meets with the keys to skip, so a plain name would be compared with every key of every dict in a module, including dates (e.g. an analyzer’s results by date), and a date compared with a string that is not a date raises (see ss.date). Use skipkeys(names) for a list of them.
Functions
| Name | Description |
|---|---|
| assigned_name | The name of the variable an object being made is assigned to, e.g. “campaign” in campaign = ss.vaccinate(...), |
| call_fn | Call a user function with the arguments it takes (see fn_args()), from those given by name |
| caller_names | The names of the variables in the code that called the caller (depth levels above it), as a dict of the name of |
| copy_given | The sim’s inputs merged and copied together, so the modules given are never run themselves and can be given to other sims, and refer to each other as given; with copy_modules=False, the rest is copied, but not the modules |
| fn_args | The arguments a user function takes, in its order: those it names among names (by default self, the module whose |
| getframe | The frame depth levels above the caller, or None if the stack is not that deep (e.g. when called from R via reticulate) |
| given_modules | The modules among a sim’s inputs, alone or in lists or dicts (e.g. diseases=[sir, sis]) |
| load | Alias to Sciris sc.loadany(), for Starsim’s own objects |
| make_disease | Transitions given to the sim (alone or in a list, among modules) as one disease, named from its states (e.g. “sir”), whose results are also at the top of the sim’s (sim.results.I) |
| named | Name what is made inside by the variables of code in another language, as caller_names() does for Python’s |
| names_of | The name of each object in some namespaces (dicts of objects by name), by its id, for an object bound to exactly one name in the last space that has it, not starting with _ (see caller_names()) |
| nlist_to_dict | Convert a list of named items (e.g. modules, states) to a dictionary; not for the user |
| num_str | A time as text: a number (or a duration, by its value) without a needless decimal point (100, not 100.0), or a date |
| own_size | The bytes needed to pickle a module, not counting its parameters or what it shares with the rest of the sim: the sim, its people, the other modules (e.g. |
| plot_args | Split the keywords given to sim.plot() and other plotting functions among the parts of the plot they are for (the figure, the lines, the data, and so on) |
| resolve_data_col | Find the column to use for a given data key, allowing for known synonyms |
| return_fig | Do postprocessing on the figure: save it, show it, and return it; not for the user, but its arguments can be given to any plot of results |
| return_table | Return a table to show it: in a notebook or at an interactive prompt, which show what is returned, it is only returned, so it |
| same_value | Whether two settings have the same value (e.g. 10000 and 10000.0); False if they cannot be compared to one True or False |
| save | Alias to Sciris sc.save() |
| show | Show the figures made so far (plt.show()), e.g. at the end of a script that makes plots with ss.options.show = False |
| shrink | Replace one or more object attributes in-place with an ss.utils.Shrunk() placeholder. |
| skipkeys | Names of keys for sc.search(..., skip=dict(keys=...)) that are compared only with strings (see skipkey) |
| standardize_data | Standardize formats of input data |
| takes_uids | Whether a user function takes the agents (uids), so is a function of each agent, which exists only when run as agents (see fn_args()) |
| time_str | A time point as text, for what a run prints: a date as a date (without a time of day, e.g. 2020-01-08), a duration with its own unit (e.g. year 2000.02, as on a timeline in calendar years), or a number in the sim’s unit (e.g. day 27, or step 27 in a sim with no unit, whose times are days underneath) |
| warn | Show a warning, as ss.options.warnings says: as a Python warning (default), printed, raised as an error, or not at all |
assigned_name
utils.assigned_name(obj)The name of the variable an object being made is assigned to, e.g. “campaign” in campaign = ss.vaccinate(...), read from the code that made it (the frame above its constructors) as the instruction after the call; None if the result is not stored straight in a variable (e.g. it is returned, put in a list, or passed on). It is read when the object is made, so the name is that of its first variable, whatever variable holds it later (e.g. in a loop)
call_fn
utils.call_fn(func, names=fn_names, positions=None, **values)Call a user function with the arguments it takes (see fn_args()), from those given by name
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| func | callable |
the function | required |
| names | tuple |
the arguments it can take by name | fn_names |
| positions | dict |
for each number of arguments, which they are by position | None |
| values | dict |
the value of each argument, e.g. self=mod, sim=sim, uids=uids |
Example
ss.utils.call_fn(lambda self, sim: self.pars.beta*sim.people.n, self=sir, sim=sim) # Each argument is passed if it is namedcaller_names
utils.caller_names(depth=1, skip=None)The names of the variables in the code that called the caller (depth levels above it), as a dict of the name of each object by its id: its local variables, else its global ones, and only for an object bound to exactly one of them. A declared disease (ss.Disease(infection, recovery)) and a sim (ss.Sim(diseases=sir), sim.new(interventions=campaign)) look up the names of what they are given once, here; nothing else does. If the stack is not that deep (e.g. from R via reticulate), the names given by named(), if any. The code of the function skip, if that is the code above, does not count.
copy_given
utils.copy_given(*inputs, copy_modules=True)The sim’s inputs merged and copied together, so the modules given are never run themselves and can be given to other sims, and refer to each other as given; with copy_modules=False, the rest is copied, but not the modules
fn_args
utils.fn_args(func, names=fn_names, positions=None)The arguments a user function takes, in its order: those it names among names (by default self, the module whose function it is; sim; and uids, the agents it is for), so any of fn(sim), fn(self, sim), fn(sim, uids), fn(self, sim, uids) (or none) can be written; a function whose arguments have other names takes them by position (fn(s) is the sim, fn(s, u) the sim and the agents, fn(m, s, u) all three). Arguments with defaults (fn(sim, years=5)) are not counted. Worked out once for each function.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| func | callable |
the function | required |
| names | tuple |
the arguments it can take by name | fn_names |
| positions | dict |
for each number of arguments, which they are by position (by default, see fn_positions) |
None |
getframe
utils.getframe(depth=1)The frame depth levels above the caller, or None if the stack is not that deep (e.g. when called from R via reticulate)
given_modules
utils.given_modules(*inputs)The modules among a sim’s inputs, alone or in lists or dicts (e.g. diseases=[sir, sis])
load
utils.load(filename, **kwargs)Alias to Sciris sc.loadany(), for Starsim’s own objects
Since Starsim uses Sciris for saving objects, they can be loaded back using this function, e.g. a sim saved with ss.save('run.sim', sim). Another framework’s model is read with ss.import_from().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| filename | str / path |
the name of the file to load | required |
| kwargs | dict |
passed to sc.loadany() |
Examples
sim = ss.load('run.sim') # A sim saved with ss.save()make_disease
utils.make_disease(arg, names)Transitions given to the sim (alone or in a list, among modules) as one disease, named from its states (e.g. “sir”), whose results are also at the top of the sim’s (sim.results.I)
named
utils.named(*spaces)Name what is made inside by the variables of code in another language, as caller_names() does for Python’s
R-Starsim calls Starsim from R, whose variables Python can’t see, so it gives them here: e.g. Disease(infection, recovery) in R names the transitions infection and recovery, as in Python. Only a caller that is not Python (with no frame of its own) reads them.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| spaces | dict |
the objects by name, e.g. R’s global variables and then its local ones (the later ones win) |
names_of
utils.names_of(*spaces)The name of each object in some namespaces (dicts of objects by name), by its id, for an object bound to exactly one name in the last space that has it, not starting with _ (see caller_names())
nlist_to_dict
utils.nlist_to_dict(nlist, die=True)Convert a list of named items (e.g. modules, states) to a dictionary; not for the user
num_str
utils.num_str(x)A time as text: a number (or a duration, by its value) without a needless decimal point (100, not 100.0), or a date
own_size
utils.own_size(mod)The bytes needed to pickle a module, not counting its parameters or what it shares with the rest of the sim: the sim, its people, the other modules (e.g. a delivery’s transitions, which belong to their disease), and results (its own too, which grow with its stratifications as well as with time); used by sim.shrink() to check that a module has let go of its arrays
plot_args
utils.plot_args(kwargs=None, _debug=False, **defaults)Split the keywords given to sim.plot() and other plotting functions among the parts of the plot they are for (the figure, the lines, the data, and so on)
Note: the kwargs supplied to the parent function should be supplied as the first argument of this function; keyword arguments to this function are treated as default values that will be overwritten by user-supplied values in kwargs. The argument “_debug” is used internally to print debugging output, but is not typically set by the user.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| fig_kw | dict |
passed to sc.getrowscols(), then plt.subplots() and plt.figure() |
|
| plot_kw | dict |
passed to plt.plot() |
|
| data_kw | dict |
passed to plt.scatter(), for plotting the data |
|
| style_kw | dict |
passed to sc.options.with_style(), for controlling the detailed plotting style |
|
| **kwargs | dict |
parsed among the above dictionaries | None |
Returns
| Name | Type | Description |
|---|---|---|
| A dict-of-dicts with plotting arguments, for use with subsequent plotting commands |
Valid kwarg arguments are:
- fig: 'figsize', 'nrows', 'ncols', 'ratio', 'num', 'dpi', 'facecolor'
- plot: 'alpha', 'c', 'color', 'lw', 'linewidth', 'ls', 'linestyle', 'marker', 'markersize', 'ms'
- data: 'data_alpha', 'data_color', 'data_size'
- fill: 'fill_alpha', 'fill_color', 'fill_hatch', 'fill_lw'
- style: 'font', 'fontsize', 'interactive'
- return_fig: 'do_show', 'is_jupyter', 'filename' (see `ss.return_fig()`)
Examples
kw = ss.plot_args(kwargs, fig_kw=dict(figsize=(10,10))) # Explicit way to set figure size, passed to `plt.figure()` eventually
kw = ss.plot_args(kwargs, figsize=(10,10)) # Shortcut since known keywordresolve_data_col
utils.resolve_data_col(data, key, col)Find the column to use for a given data key, allowing for known synonyms
The configured column name always takes precedence; synonyms (see ss.utils.col_synonyms) are only consulted if it is absent from the data.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| data | dict |
the data, as a dict of columns | required |
| key | str |
the standardized key, e.g. “year” | required |
| col | str |
the configured column name, e.g. “Year” | required |
Returns
| Name | Type | Description |
|---|---|---|
| The name of the column to use |
return_fig
utils.return_fig(fig, given=False, **kwargs)Do postprocessing on the figure: save it, show it, and return it; not for the user, but its arguments can be given to any plot of results
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| fig | Figure |
the figure | required |
| given | bool |
whether the figure (or axes) was given by the caller, who then shows it, so it is not shown unless do_show=True |
False |
| do_show | bool |
whether to show the figure (by default, ss.options.show, and in Jupyter, always) |
|
| is_jupyter | bool |
whether in Jupyter (by default, ss.options.is_jupyter), where a figure that is shown is not also returned, so it is not displayed twice |
|
| filename | str |
if given, save the figure to this file (with sc.savefig()) |
Returns the figure, unless it is shown in Jupyter; so in Jupyter, fig = sim.plot(do_show=False) gives the figure, without showing it.
return_table
utils.return_table(df, **kwargs)Return a table to show it: in a notebook or at an interactive prompt, which show what is returned, it is only returned, so it isn’t shown twice; in a script, it is printed too (with df.to_string(**kwargs)). For summaries that print and return a table.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| df | dataframe |
the table | required |
| kwargs | dict |
passed to df.to_string() |
same_value
utils.same_value(a, b)Whether two settings have the same value (e.g. 10000 and 10000.0); False if they cannot be compared to one True or False
save
utils.save(filename, obj, **kwargs)Alias to Sciris sc.save()
While some Starsim objects have their own save methods, this function can be used to save any arbitrary object. It can then be loaded with ss.load(). Another framework’s format is written by ss.export_to().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| filename | str / path |
the name of the file to save | required |
| obj | any |
the object to save | required |
| kwargs | dict |
passed to sc.save() |
Examples
ss.save('run.sim', sim) # The sim, with its resultsshow
utils.show(**kwargs)Show the figures made so far (plt.show()), e.g. at the end of a script that makes plots with ss.options.show = False
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| kwargs | dict |
passed to plt.show() |
shrink
utils.shrink(obj=None, attrs=None, verbose=False)Replace one or more object attributes in-place with an ss.utils.Shrunk() placeholder.
Used to save memory or remove circular references, especially when running large numbers of sims. It is good practice to write shrink() methods for modules to remove any temporary arrays or other memory-intensive objects.
ss.utils.shrink() can be used to either manually replace an object, or replace multiple attributes of an object in-place. It is used rather than simply deleting attributes or setting them to None to make it clear that the attributes were intentionally removed.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| obj | object |
the object to shrink in-place (optional) | None |
| attrs | str / list |
the attributes of the object to shrink | None |
| verbose | bool |
if True, print warnings about missing attributes | False |
Examples
# "Shrink" (remove) the People object
sim = ss.Sim()
ss.utils.shrink(sim, 'people')
# Equivalent behavior, used manually
sim.people = ss.utils.shrink()skipkeys
utils.skipkeys(names)Names of keys for sc.search(..., skip=dict(keys=...)) that are compared only with strings (see skipkey)
standardize_data
utils.standardize_data(
data=None,
metadata=None,
min_year=1800,
out_of_range=0,
default_age=0,
default_year=2024,
)Standardize formats of input data
Input data can arrive in many different forms. This function accepts a variety of data structures, and converts them into a Pandas Series containing one variable, based on specified metadata, or an ss.Dist if the data is already an ss.Dist object.
The metadata is a dictionary that defines columns of the dataframe or keys of the dictionary to use as indices in the output Series. It should contain:
metadata['data_cols']['value']specifying the name of the column/key to draw values frommetadata['data_cols']['year']optionally specifying the column containing year values; otherwise the default year will be usedmetadata['data_cols']['age']optionally specifying the column containing age values; otherwise the default age will be usedmetadata['data_cols'][<arbitrary>]optionally specifying any other columns to use as indices. These will form part of the multi-index for the standardized Series output.
If a configured column is not present in the data, a known synonym is used instead if one is available (see ss.utils.col_synonyms); for example, year data labeled “Time” (as in UN World Population Prospects data) is used for a data column configured as “Year”. Explicitly configured column names always take precedence over synonyms.
If a sex column is part of the index, the metadata can also optionally specify a string mapping to convert the sex labels in the input data into the ‘m’/‘f’ labels used by Starsim. In that case, the metadata can contain an additional key like metadata['sex_keys'] = {'Female':'f','Male':'m'} which in this case would map the strings ‘Female’ and ‘Male’ in the original data into ‘m’/‘f’ for Starsim.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| data | (pandas.DataFrame, pandas.Series, dict, int, float) |
An associative array or a number, with the input data to be standardized. | None |
| metadata | dict |
Dictionary specifying index columns, the value column, and optionally mapping for sex labels | None |
| min_year | float |
Optionally specify a minimum year allowed in the data. Default is 1800. | 1800 |
| out_of_range | float |
Value to use for negative ages - typically 0 is a reasonable choice but other values (e.g., np.inf or np.nan) may be useful depending on the calculation. This will automatically be added to the dataframe with an age of -np.inf |
0 |
| default_age | float |
the age to use if the data has no age column | 0 |
| default_year | float |
the year to use if the data has no year column | 2024 |
Returns:
- A `pd.Series` for all supported formats of `data` *except* an `ss.Dist`. This series will contain index columns for 'year'
and 'age' (in that order) and then subsequent index columns for any other variables specified in the metadata, in the order
they appeared in the metadata (except for year and age appearing first).
- An `ss.Dist` instance - if the `data` input is an `ss.Dist`, that same object will be returned by this function
takes_uids
utils.takes_uids(func, **kwargs)Whether a user function takes the agents (uids), so is a function of each agent, which exists only when run as agents (see fn_args())
time_str
utils.time_str(x, sim=None, plural=False)A time point as text, for what a run prints: a date as a date (without a time of day, e.g. 2020-01-08), a duration with its own unit (e.g. year 2000.02, as on a timeline in calendar years), or a number in the sim’s unit (e.g. day 27, or step 27 in a sim with no unit, whose times are days underneath)
warn
utils.warn(msg, category=None, verbose=None, die=None)Show a warning, as ss.options.warnings says: as a Python warning (default), printed, raised as an error, or not at all
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| msg | str |
the text of the warning | required |
| category | Warning |
the type of warning (default: RuntimeWarning) |
None |
| verbose | bool |
whether to print it, if ss.options.warnings is ‘print’ (default: ss.options.verbose) |
None |
| die | bool |
if True, raise it as an error, whatever ss.options.warnings is |
None |