internals.arrays
Arrays of agents’ values: states such as age or infection status, and the UIDs that index them
A state holds a value for every agent, and reads only the active ones, so code can use it like a NumPy array as agents are born and die. A few classes here stand in for them when a run has groups of people rather than agents (GroupAges, GroupAlive).
Used with: agents and compartments; declared and coded modules
Classes
| Name | Description |
|---|---|
| Arr | Store a state of the agents (e.g. age, infection status, etc.) as an array. |
| BaseArr | An object that acts exactly like a NumPy array, except stores the values in self.values. |
| BoolArr | Subclass of ss.Arr with defaults for booleans. |
| BoolState | A boolean array being used as a state. |
| CatArr | A categorical state: one of a list of categories for each agent (e.g. a district, or a wealth quintile), read, compared and set by category |
| FloatArr | Subclass of ss.Arr with defaults for floats. |
| GroupAges | people.age in a compartmental run: one row per group, at the middle of its age group |
| GroupAlive | people.alive in a run without agents: one row per group of people, not one per person |
| IndexArr | A special class of Arr used for UIDs and RNG IDs; not to be used as a general integer array (for that, use IntArr) |
| IntArr | Subclass of ss.Arr with defaults for ints. |
| TimeArr | Times, as step numbers of the module’s timeline: when something happened or is due for each agent, e.g. a declared disease’s ti_X (when each entered state X) and ti_exit_X, as agents, and people.ti_dead |
| UIDArr | The UIDs of all agents (people.uid), which take no memory: a UID is the agent’s index, so raw is made when read |
| Unkept | A declared disease’s time of entering a state (ti_X) that is not kept, since the engine does not need it (see ss.Sim(keep_times=True)) |
| Watch | The link from a state to the engine that keeps count of who is in it (BoolState.watch), which is told of |
| uids | Class to specify that integers should be interpreted as UIDs. |
Arr
internals.arrays.Arr(
name=None,
dtype=None,
default=None,
nan=None,
label=None,
raw=None,
skip_init=False,
people=None,
mock=None,
columns=None,
shared=False,
)Store a state of the agents (e.g. age, infection status, etc.) as an array.
In practice, Arr objects can be used interchangeably with NumPy arrays. They have two main data interfaces: Arr.raw contains the “raw”, underlying NumPy array of the data. Arr.values contains the “active” values, which usually corresponds to agents who are alive.
By default, operations are performed on active agents only (specified by Arr.auids, which is a pointer to sim.people.auids). For example, sim.people.age.mean() will only use the ages of active agents. Thus, sim.people.age.mean() is equal to sim.people.age.values.mean(), not sim.people.age.raw.mean().
Arr.raw includes agents who have died, and also has slots that are not people yet: room at the end for agents born later, which hold the array’s nan (NaN for an ss.FloatArr, not its default). So np.sum(arr.raw) can be NaN for a float array; to sum, count, or average over the people, use the array itself (arr.sum(), arr.mean()) or arr.values, and to read particular agents, index by UID (arr[uids]).
If indexing by an int, integer array, or ss.uids object, Arr.raw is used (i.e. the ints are treated as UIDs). If indexing by a full slice ([:]) or a boolean array, Arr.values is used. Arr objects can’t be directly indexed by a list, as this would be ambiguous about whether values or raw is intended. For example, if there are 1000 people in a simulation and 100 of them have died, sim.people.age[999] and sim.people.age[ss.uids(999)] both return the age of the agent with UID 999, whereas sim.people.age.values[899] returns the age of the last active agent.
Note on terminology: the term “states” is often used to refer to all ss.Arr objects, (e.g. in module.define_states()), whether or not they are BoolStates.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| name | str |
The name for the state (also used as the dictionary key, so should not have spaces etc.) | None |
| dtype | class | The dtype to use for this instance (if None, infer from raw or default; str is stored as object) |
None |
| default | any |
Specify default value for new agents. This can be:
|
None |
| nan | any |
the value to use to represent NaN (not a number); also used as the default value if not supplied (if None, inferred from the dtype, e.g. int_nan for integers) |
None |
| label | str |
The human-readable name for the state | None |
| raw | array |
If provided, initialize the array with these raw values | None |
| skip_init | bool |
Whether to skip initialization with the People object (used for uid and slot states) | False |
| people | ss.People |
Optionally specify an initialized People object, used to construct temporary Arr instances | None |
| mock | int |
if provided, create a mock People object (of length mock, unless raw is provided) to initialize the array (for debugging purposes) |
None |
| columns | int / list |
if provided, create a 2D array with this many columns (e.g. one per variant), or a list of column names (e.g. ['wildtype', 'mutant']) |
None |
| shared | bool / Arr |
if True, the array takes no memory per agent until a value is set (other than by its default, which must be a scalar): until then every agent has the default, and raw is a read-only view of it; if an Arr, the same with that array’s values (as people.slot reads people.uid) |
False |
For 2D arrays (i.e. if columns is supplied), arr[uids] returns an array of shape (len(uids), ncols), arr[uids, v] or arr[:, v] returns column v, and arr.col(v) returns column v as a 1D Arr. If the columns are named, v can also be a name, e.g. arr.col('mutant'), arr['mutant'], or arr.mutant. A scalar default fills every column, and a callable default should return an array of shape (n, ncols). Arrays with more dimensions are also supported via a tuple, e.g. columns=(2,3) gives a raw array of shape (len_tot, 2, 3).
Examples
# Create a standalone Arr for quick testing
age = ss.Arr('age', default=0, mock=5) # 5 = length if not supplying a real People object
age[:] = [20, 30, 40, 50, 60]
age.mean() # Returns 40.0
# Use within a simulation
sim = ss.Sim(n_agents=100).init()
sim.people.age.mean() # Mean age of active agents
# Create a 2D array, e.g. immunity to each of 3 variants
imm = ss.FloatArr('imm', default=0, columns=3, mock=5)
imm[ss.uids([0, 1]), 2] = 0.8 # Set immunity to variant 2 for agents 0 and 1
imm[ss.uids([0, 1])] # Returns an array of shape (2, 3)
(imm.col(2) > 0.5).uids # Returns ss.uids([0, 1])
# Create a 2D array with named columns
imm = ss.FloatArr('imm', default=0, columns=['wildtype', 'mutant'], mock=5)
imm[ss.uids([0, 1]), 'mutant'] = 0.8 # Same as imm[ss.uids([0, 1]), 1] = 0.8
(imm.col('mutant') > 0.5).uids # Returns ss.uids([0, 1])Attributes
| Name | Description |
|---|---|
| auids | Link to the indices of active agents – sim.people.auids |
| isnan | Return BoolArr for NaN values |
| notnan | Return BoolArr for non-NaN values |
| values | Return the values of the active agents |
Methods
| Name | Description |
|---|---|
| asnew | Duplicate and copy (rather than link) data, optionally resetting the array |
| astype | Convert the Arr type, e.g. arr.astype(ss.IntArr); other types (e.g. int) return a NumPy array |
| col | Return column v of a 2D array as a 1D Arr, e.g. (imm.col(2) > 0.5).uids or imm.col('mutant') |
| convert | Check if an object is an array-like, and convert it to this array’s type if so. |
| count | Count the number of nonzero (truthy) values among active agents (for 2D arrays, use axis=0 to count each column) |
| false | Reverse of true(); return UIDs of falsy values |
| grow | Add new agents to an Arr |
| init_vals | Actually populate the initial values and mark as initialized; only to be used on initialization |
| isin | Return a BoolArr that is True wherever the array’s value is in values. |
| link_people | Link a People object to this state, to access auids |
| own | Give an array that has no memory of its own yet (see shared in ss.Arr) its own values, before one is set |
| set | Set the values for the specified UIDs |
| set_nan | Shortcut function to set values to NaN |
| shared_raw | The read-only values of an array that has no memory of its own yet (see shared in ss.Arr): its default, or the values of the array it reads |
| to_json | Export to JSON |
| true | Efficiently convert truthy values to UIDs |
asnew
internals.arrays.Arr.asnew(arr=None, cls=None, name=None, copy=None)Duplicate and copy (rather than link) data, optionally resetting the array
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| arr | array |
the values of the new array; if None, a copy of the active values | None |
| cls | type |
the class of the new array; if None, the same class | None |
| name | str |
the name of the new array | None |
| copy | bool |
whether to copy arr; if None, copy if arr is None or is a different size than the array |
None |
astype
internals.arrays.Arr.astype(cls, copy=False)Convert the Arr type, e.g. arr.astype(ss.IntArr); other types (e.g. int) return a NumPy array
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| cls | type |
the class to convert to, e.g. ss.IntArr, or a NumPy type such as int |
required |
| copy | bool |
whether to copy the values, for an ss.Arr class |
False |
col
internals.arrays.Arr.col(v)Return column v of a 2D array as a 1D Arr, e.g. (imm.col(2) > 0.5).uids or imm.col('mutant')
For arrays with more dimensions, returns an Arr with one fewer dimension. The new Arr is a view, so setting its values also changes the original array. It is not linked to People, so should not be stored (since it will not grow).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| v | int / str |
the column, by position or by name | required |
convert
internals.arrays.Arr.convert(obj, copy=None)Check if an object is an array-like, and convert it to this array’s type if so.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| obj | object to convert (BaseArr, np.ndarray, or raises TypeError) | required | |
| copy | bool |
whether to copy the data; passed through to asnew() | None |
count
internals.arrays.Arr.count(axis=None)Count the number of nonzero (truthy) values among active agents (for 2D arrays, use axis=0 to count each column)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| axis | int |
the axis to count along, for a 2D array; if None, count all values | None |
false
internals.arrays.Arr.false()Reverse of true(); return UIDs of falsy values
grow
internals.arrays.Arr.grow(new_uids=None, new_vals=None)Add new agents to an Arr
This method is normally only called via People.grow().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| new_uids | array |
Numpy array of UIDs for the new agents being added | None |
| new_vals | array |
If provided, assign these state values to the new UIDs | None |
init_vals
internals.arrays.Arr.init_vals()Actually populate the initial values and mark as initialized; only to be used on initialization
isin
internals.arrays.Arr.isin(values)Return a BoolArr that is True wherever the array’s value is in values.
An Arr-aware analogue of numpy.isin that also picks the faster algorithm: values may be a scalar or any array-like of test values. For a small number of test values the equality comparisons are OR-ed together (which avoids the sort/hash overhead of np.isin and is faster for the common handful-of-categories case); for larger sets it falls back to np.isin. The result is a BoolArr, so .uids, [uids], and boolean combination all work.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| values | a scalar, or an array-like (list/tuple/array) of values to test against. | required |
Returns
| Name | Type | Description |
|---|---|---|
| BoolArr | True wherever the array’s value equals one of values. |
Example
active = disease.state.isin([State.MILD, State.SEVERE]).uidslink_people
internals.arrays.Arr.link_people(people)Link a People object to this state, to access auids
own
internals.arrays.Arr.own()Give an array that has no memory of its own yet (see shared in ss.Arr) its own values, before one is set
set
internals.arrays.Arr.set(uids, new_vals=None)Set the values for the specified UIDs
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | ss.uids |
the agents to set values for | required |
| new_vals | array |
the values; if None, use the default of the array | None |
set_nan
internals.arrays.Arr.set_nan(uids)Shortcut function to set values to NaN
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | ss.uids |
the agents to set to NaN | required |
to_json
internals.arrays.Arr.to_json()Export to JSON
true
internals.arrays.Arr.true()Efficiently convert truthy values to UIDs
BaseArr
internals.arrays.BaseArr(values, *args, **kwargs)An object that acts exactly like a NumPy array, except stores the values in self.values.
It exists so that a state works in NumPy expressions while carrying what Starsim keeps with its values.
Methods
| Name | Description |
|---|---|
| asnew | Duplicate and copy (rather than link) data. |
| convert | Check if an object is an array, and convert if so |
| disp | Full display of object |
| to_json | Return a dictionary representation of the Arr |
| update | Update the attributes, skipping None values and raising an error if extra keys are added |
| validate_columns | Check that column names are unique strings that don’t clash with attributes, e.g. “low” |
asnew
internals.arrays.BaseArr.asnew(values=None, cls=None, **kwargs)Duplicate and copy (rather than link) data.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| values | array |
replacement data; if None, copies existing values | None |
| cls | type |
class to use for the new instance; defaults to the current class | None |
| kwargs | additional attributes to set on the new instance |
convert
internals.arrays.BaseArr.convert(obj)Check if an object is an array, and convert if so
disp
internals.arrays.BaseArr.disp()Full display of object
to_json
internals.arrays.BaseArr.to_json()Return a dictionary representation of the Arr
update
internals.arrays.BaseArr.update(
skip_none=True,
overwrite=True,
force=False,
**kwargs,
)Update the attributes, skipping None values and raising an error if extra keys are added
validate_columns
internals.arrays.BaseArr.validate_columns()Check that column names are unique strings that don’t clash with attributes, e.g. “low”
BoolArr
internals.arrays.BoolArr(name=None, **kwargs)Subclass of ss.Arr with defaults for booleans.
It exists so that a flag can be defined by name alone, and adds what is useful for booleans, such as .uids.
Examples
# Create a standalone BoolArr
infected = ss.BoolArr('infected', mock=5)
infected[ss.uids([0, 2, 4])] = True
infected.count() # Returns 3
infected.uids # Returns ss.uids([0, 2, 4])Attributes
| Name | Description |
|---|---|
| frac | The fraction of the people alive it is True for now (n over people.n), as (sir.I \| sir.R).frac gives it under every solver |
| isnan | Always False for BoolArr (booleans cannot be NaN). |
| n | The number of people it is True for now, each counted by their weight and the sim’s scale, as (sir.I \| sir.R).n counts them under every solver (count() is the number of agents) |
| notnan | Always True for BoolArr (booleans cannot be NaN). |
| uids | Alias to Arr.true |
Methods
| Name | Description |
|---|---|
| split | Return UIDs of values that are true and false as separate arrays |
split
internals.arrays.BoolArr.split()Return UIDs of values that are true and false as separate arrays
BoolState
internals.arrays.BoolState(name=None, **kwargs)A boolean array being used as a state.
Although functionally identical to BoolArr, a BoolState is handled differently in terms of automation: specifically, results are automatically generated from a BoolState (but not a BoolArr).
BoolStates are typically used to keep track of externally-facing variables (e.g. disease.susceptible), while BoolArrs can be used to keep track of internal ones (e.g. disease.has_immunity). A BoolState named “susceptible” will automatically generate a result named “susceptible” (the number susceptible), for example.
Note on terminology: the term “states” is often used to refer to all ss.Arr objects, (e.g. in module.define_states()), whether or not they are BoolStates.
Attributes
| Name | Description |
|---|---|
| n | The number of people in the state: its agents, each counted by the people it stands for (its weight and the sim’s scale), as I.n in a rate counts them, and as a compartmental run’s state gives it; count() is the number of agents |
| stays | For a state of a declared disease run as agents: how long each person who has been through it stayed there, e.g. |
| time_in | For a state of a declared disease run as agents: the time each person in it (or leaving it) has spent there, by |
Methods
| Name | Description |
|---|---|
| asnew | As Arr.asnew(); a copy whose values are not the state’s own is not counted (see watch) |
| grow | Add new agents (see Arr.grow()); they were not counted before, so whatever keeps count of the state counts those who start in it |
| set | Set the values for the specified UIDs, telling whatever keeps count of the state (see __setitem__()) |
asnew
internals.arrays.BoolState.asnew(arr=None, cls=None, name=None, copy=None)As Arr.asnew(); a copy whose values are not the state’s own is not counted (see watch)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| arr | array |
the values of the new array; if None, a copy of the active values | None |
| cls | type |
the class of the new array; if None, the same class | None |
| name | str |
the name of the new array | None |
| copy | bool |
whether to copy arr; see Arr.asnew() |
None |
grow
internals.arrays.BoolState.grow(new_uids=None, new_vals=None)Add new agents (see Arr.grow()); they were not counted before, so whatever keeps count of the state counts those who start in it
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| new_uids | ss.uids |
the UIDs of the new agents | None |
| new_vals | array |
their values; if None, use the default of the array | None |
set
internals.arrays.BoolState.set(uids, new_vals=None)Set the values for the specified UIDs, telling whatever keeps count of the state (see __setitem__())
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | ss.uids |
the agents to set values for | required |
| new_vals | array |
the values; if None, use the default of the array | None |
CatArr
internals.arrays.CatArr(name=None, categories=None, default=None, **kwargs)A categorical state: one of a list of categories for each agent (e.g. a district, or a wealth quintile), read, compared and set by category
A category can be used wherever a value can, e.g. arr == 'urban', arr.isin(['urban', 'periurban']), and arr[uids] = 'urban'. Each category is stored as its index in categories (its code), so it is as fast as an ss.IntArr, and an integer given is a code, as arr[uids] returns (so arr[children] = arr[mothers] works). Integer categories (e.g. wealth quintiles 1 to 5, or the members of an IntEnum) are stored as they are, so arr == 3 is category 3. The categories can be an enum, e.g. ss.CatArr('state', categories=State).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| name | str |
the name of the state | None |
| categories | list / Enum |
the categories, e.g. ['urban', 'rural'], [1, 2, 3, 4, 5] (integers, or none of them), or an enum |
None |
| default | any |
the value for new agents: a category, or a callable or ss.Dist returning categories or codes (e.g. ss.choice(a=categories, p=[0.3, 0.7])) |
None |
| kwargs | dict |
passed to ss.Arr |
Examples
loc = ss.CatArr('loc', categories=['urban', 'rural'], default='rural', mock=4)
loc[ss.uids([0, 1])] = 'urban'
(loc == 'urban').uids # ss.uids([0, 1])
loc.labels() # array(['urban', 'urban', 'rural', 'rural'], dtype=object)Methods
| Name | Description |
|---|---|
| code | The code of a category, or the codes of an array of them; integers (codes, or integer categories) are returned unchanged |
| index | The position of each agent’s category in categories (by default, for the active agents), or -1 if not set (as pandas’ Categorical.codes), e.g. to count by category or look up a value for each |
| labels | The categories of the agents given (by default, the active agents), with None for those not set |
code
internals.arrays.CatArr.code(value)The code of a category, or the codes of an array of them; integers (codes, or integer categories) are returned unchanged
index
internals.arrays.CatArr.index(uids=None)The position of each agent’s category in categories (by default, for the active agents), or -1 if not set (as pandas’ Categorical.codes), e.g. to count by category or look up a value for each
labels
internals.arrays.CatArr.labels(uids=None)The categories of the agents given (by default, the active agents), with None for those not set
FloatArr
internals.arrays.FloatArr(name=None, **kwargs)Subclass of ss.Arr with defaults for floats.
It exists so that a state can be defined by name alone, e.g. ss.FloatArr('cd4').
GroupAges
internals.arrays.GroupAges(name=None, **kwargs)people.age in a compartmental run: one row per group, at the middle of its age group
Comparing it with an age inside a group (sim.people.age > 50 with groups [0, 15, 65]) raises, since the answer would be the same for everyone in the group; an edge works.
GroupAlive
internals.arrays.GroupAlive(name=None, **kwargs)people.alive in a run without agents: one row per group of people, not one per person
Counting it (.sum(), .count()) counts agents, which a run without agents does not have, so these raise, as do .uids and .true(), saying what to use instead: the number of people alive under every solver is people.n (or people.alive.n).
Attributes
| Name | Description |
|---|---|
| n | The number of people alive: the people in the compartments (the same in every declared disease), or with none, the population |
IndexArr
internals.arrays.IndexArr(name=None, label=None, shared=False)A special class of Arr used for UIDs and RNG IDs; not to be used as a general integer array (for that, use IntArr)
The values are stored as 32-bit integers, to save memory (so there can be up to 2^31 agents), and read as UIDs (ss.uids, which keep 32-bit integers as they are).
Attributes
| Name | Description |
|---|---|
| raw | The stored values; while they are those of people.uid (see shared in ss.Arr), the UIDs, made when read |
| uids | Alias to self.values, to allow Arr.uids like BoolArr |
| values | The values of the active agents, as UIDs |
Methods
| Name | Description |
|---|---|
| grow | Change the size of the array |
grow
internals.arrays.IndexArr.grow(new_uids=None, new_vals=None)Change the size of the array
IntArr
internals.arrays.IntArr(name=None, **kwargs)Subclass of ss.Arr with defaults for ints.
It exists so that a state can be defined by name alone, e.g. ss.IntArr('n_doses').
Note: Because integer arrays do not handle NaN values natively, users are recommended to use ss.FloatArr() in most cases instead.
TimeArr
internals.arrays.TimeArr(name=None, **kwargs)Times, as step numbers of the module’s timeline: when something happened or is due for each agent, e.g. a declared disease’s ti_X (when each entered state X) and ti_exit_X, as agents, and people.ti_dead
Stored as float32 step numbers, NaN for “never” (not set), so ti_I > 0 and (sim.ti - ti_I)*sim.t.dt give the right answer. due() gives whose time has come, with a time between steps rounded up or down at random in proportion (by each agent’s phase, as the agent engine rounds a dwell time: see ss.People.phase()), so it keeps its mean; isclose(sim.ti) gives whose time is taken at this step, and .years converts them to years. == and != raise, since a time between steps never equals a step: eq() is exact equality. For plain arithmetic on the times, compare them: e.g. ti_x < sim.ti + 0.5 is due at the nearest step, and ti_x <= sim.ti at the first step at or after it.
A declared state’s time of entering (ti_X) reads as the index of the step the move happened in, whatever kind of move it was (a progression at the step’s start, an infection by contact at its end), so ti_X.isclose(sim.ti) is “entered this step”. The engine keeps its own time of the move, which .raw gives (see shift). So the difference of two of them is not always a duration: an infection by contact happens at the end of its step and a scheduled move at the start of its step, so ti_I - ti_E of an SEIR reads one step longer than the latent period was. For the time people spent in a state, use the state’s stays (e.g. np.mean(seir.E.stays), the mean latent period), which is built from the engine’s times.
Example
sim = ss.Sim(ss.SIR(), unit='day', stop=100, solver='abm').run()
sir = sim.diseases.sir
infected_today = sir.ti_I.isclose(sim.ti).uids
years = sir.ti_I.years # The year of infection of each agent (NaN if never infected)
# In a disease written as code
self.define_states(ss.BoolState('exposed'), ss.TimeArr('ti_infectious'))
self.ti_infectious[uids] = self.ti + dur # In steps
mask = self.exposed & self.ti_infectious.due() # Those whose time has comeAttributes
| Name | Description |
|---|---|
| years | The values as years (e.g. 2020.5), with NaN where not set |
Methods
| Name | Description |
|---|---|
| due | Whose time has come by the current step, as a boolean array (e.g. mask = self.exposed & self.ti_infectious.due()): a time between steps is rounded up or down at random in proportion, by each agent’s phase (see ss.People.phase()) |
| eq | Whose time is exactly other, as a boolean array (== and != are refused: see __eq__()) |
| isclose | Whose time is taken at step other (e.g. self.ti_infectious.isclose(self.ti), those who became infectious this step, as due() counts them), as a boolean array |
| shifted | Add the shift to some values read, by agent (all of them, or those of key) |
due
internals.arrays.TimeArr.due()Whose time has come by the current step, as a boolean array (e.g. mask = self.exposed & self.ti_infectious.due()): a time between steps is rounded up or down at random in proportion, by each agent’s phase (see ss.People.phase())
eq
internals.arrays.TimeArr.eq(other)Whose time is exactly other, as a boolean array (== and != are refused: see __eq__())
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| other | float / array |
the timestep or timesteps to compare with | required |
isclose
internals.arrays.TimeArr.isclose(other)Whose time is taken at step other (e.g. self.ti_infectious.isclose(self.ti), those who became infectious this step, as due() counts them), as a boolean array
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| other | int |
the timestep | required |
shifted
internals.arrays.TimeArr.shifted(vals, key=None)Add the shift to some values read, by agent (all of them, or those of key)
UIDArr
internals.arrays.UIDArr(name=None, label=None, shared=False)The UIDs of all agents (people.uid), which take no memory: a UID is the agent’s index, so raw is made when read
It is np.arange() up to the number of agents made, and -1 (not set) in the room for agents not yet made, as a stored IndexArr would be. Code that only needs its length reads len_used or len_tot instead.
Methods
| Name | Description |
|---|---|
| grow | Add new agents, whose UIDs are the next ones |
| isin | Which agents’ UIDs are in values (see ss.Arr.isin()): as a UID is the agent’s index, by setting those places, with no search |
grow
internals.arrays.UIDArr.grow(new_uids=None, new_vals=None)Add new agents, whose UIDs are the next ones
isin
internals.arrays.UIDArr.isin(values)Which agents’ UIDs are in values (see ss.Arr.isin()): as a UID is the agent’s index, by setting those places, with no search
Unkept
internals.arrays.Unkept(name, mod)A declared disease’s time of entering a state (ti_X) that is not kept, since the engine does not need it (see ss.Sim(keep_times=True))
Setting it does nothing, and reading it raises an AttributeError that says how to keep it. It takes no memory, and is not one of the module’s states.
Methods
| Name | Description |
|---|---|
| unkept | The error for reading it |
unkept
internals.arrays.Unkept.unkept()The error for reading it
Watch
internals.arrays.Watch(engine)The link from a state to the engine that keeps count of who is in it (BoolState.watch), which is told of every change to it (see ss.solvers.Agents.wrote()). It is never searched for distributions (see ss.dists.no_dists()), so the link does not change where they are found, which gives each its random numbers.
uids
internals.arrays.uids()Class to specify that integers should be interpreted as UIDs.
It exists so that indexing an ss.Arr with these reads those agents by UID, not by position.
For all practical purposes, behaves like a NumPy integer array. However, has additional methods uids.concatenate(), uids.remove(), and uids.intersect() to simplify common UID operations.
The following operators are supported:
+/+=: if the RHS is auids, concatenation (equivalent touids.concatenate()); otherwise element-wise addition (e.g.uids([1,2]) + 1→uids([2,3])). Concatenation preserves duplicates.-/-=: set difference — equivalent touids.remove(). Duplicate entries in the original will also be removed (usesnp.setdiff1dinternally).|/|=: union (unique elements from both arrays). Note that duplicate entries in the original are removed (use+instead to keep them).&/&=: intersection^/^=: symmetric difference
Note: because uids is a subclass of np.ndarray, set-like in-place operators (+= with a uids RHS, -=, |=, &=, ^=) must return a new array and rebind the variable. However, += with a scalar or array RHS modifies the array in-place and preserves id(self).
Examples
a = ss.uids([1, 2, 3])
b = ss.uids([3, 4, 5])
a + b # Concatenate: uids([1, 2, 3, 3, 4, 5])
a | b # Union: uids([1, 2, 3, 4, 5])
a & b # Intersect: uids([3])
a - b # Difference: uids([1, 2])Methods
| Name | Description |
|---|---|
| concatenate | Concatenate uids into a single instance |
| intersect | Keep only UIDs that are also present in the other array |
| remove | Remove provided UIDs from current array |
| to_numpy | Return a view as a standard NumPy array |
| union | Return all UIDs present in either array, sorted, without repeats (as np.union1d() gives them, faster: see unique()) |
| unique | Return unique UIDs; equivalent to np.unique() |
| xor | Return UIDs present in only one of the arrays |
concatenate
internals.arrays.uids.concatenate(*args)Concatenate uids into a single instance
Can be called as either an instance method or an unbound class method.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| *args | ss.uids |
the UIDs to join, as arrays or as one list of arrays; None is treated as empty |
Examples
x.concatenate(y) # instance method
x.concatenate(None) # None is treated as empty
ss.uids.concatenate(x, y) # unbound; identical to instance call
ss.uids.concatenate([x, y]) # single iterable of arrays
ss.uids.concatenate([None, x]) # None elements are filtered out
ss.uids.concatenate() # returns empty ss.uids()intersect
internals.arrays.uids.intersect(other, **kw)Keep only UIDs that are also present in the other array
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| other | ss.uids/ss.BoolArr |
the UIDs to keep, if they are also in this array | required |
| **kw | dict |
passed to np.intersect1d() |
remove
internals.arrays.uids.remove(other, **kw)Remove provided UIDs from current array
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| other | ss.uids/ss.BoolArr |
the UIDs to remove | required |
| **kw | dict |
passed to np.setdiff1d() |
to_numpy
internals.arrays.uids.to_numpy()Return a view as a standard NumPy array
union
internals.arrays.uids.union(other)Return all UIDs present in either array, sorted, without repeats (as np.union1d() gives them, faster: see unique())
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| other | ss.uids/ss.BoolArr |
the UIDs to add | required |
unique
internals.arrays.uids.unique(return_index=False)Return unique UIDs; equivalent to np.unique()
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| return_index | bool |
whether to also return the position of the first time each UID appears | False |
xor
internals.arrays.uids.xor(other, **kw)Return UIDs present in only one of the arrays
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| other | ss.uids/ss.BoolArr |
the UIDs to compare with | required |
| **kw | dict |
passed to np.setxor1d() |
Functions
| Name | Description |
|---|---|
| nb_count | The number of active agents whose value is truthy (as np.count_nonzero()), without gathering the values first |
| nb_indexer | Roughly 30% faster than NumPy for large numbers of indices (>10k) |
| nb_true | Return the UIDs where bool(vals[k]) equals want (True for true(), False for false()). |
| nb_true_raw | As nb_true(), reading each agent’s value from the full array, so the values of the active agents are not gathered first |
| np_indexer | Much faster than Numba for small numbers of indices (<1k) |
nb_count
internals.arrays.nb_count(auids, raw)The number of active agents whose value is truthy (as np.count_nonzero()), without gathering the values first
nb_indexer
internals.arrays.nb_indexer(arr, inds)Roughly 30% faster than NumPy for large numbers of indices (>10k)
NB: in some versions of Numba (>0.62), the explicit loop is much faster than array indexing.
nb_true
internals.arrays.nb_true(auids, vals, want)Return the UIDs where bool(vals[k]) equals want (True for true(), False for false()).
~4-5x faster than auids[vals.astype(bool)] for large arrays: a single branchless pass with no intermediate mask allocation. vals[k] != 0 matches .astype(bool) for every dtype, including NaN floats (NaN != 0 is True, i.e. NaN is truthy), so results are identical.
nb_true_raw
internals.arrays.nb_true_raw(auids, raw, want)As nb_true(), reading each agent’s value from the full array, so the values of the active agents are not gathered first
np_indexer
internals.arrays.np_indexer(arr, inds)Much faster than Numba for small numbers of indices (<1k)