people
The people in a sim (ss.People), lazy references to their properties (ss.person), and how they are made
People hold what every module shares, such as age, sex, and who is alive. In a compartmental run, each row is a group of people rather than an agent.
Used with: agents and compartments; declared and coded modules
Classes
| Name | Description |
|---|---|
| AgentScale | The number of people each agent stands for relative to the others (people.scale); setting it other than 1 makes results, and the counts in a declared model’s rates (I.n, I.frac, and the fraction infectious in a transmission), add up the agents’ weights |
| LazyPeople | The people’s properties, before there are any people: ss.person.<name> is a lazy reference to the property <name> of each person (e.g. ss.person.age, ss.person.urban), checked when the sim is built |
| MadeOnUse | One of the four fundamental arrays of ss.People, made on first use (see People.make_uids()) |
| People | The people in a sim: their ages, sexes, who is alive, and the states that modules add to them; also how agents are born, die, and are counted |
| Person | All the properties of one agent (see People.get_person()), as a dict that can be turned into a dataframe |
AgentScale
people.AgentScale(name=None, **kwargs)The number of people each agent stands for relative to the others (people.scale); setting it other than 1 makes results, and the counts in a declared model’s rates (I.n, I.frac, and the fraction infectious in a transmission), add up the agents’ weights
LazyPeople
people.LazyPeople()The people’s properties, before there are any people: ss.person.<name> is a lazy reference to the property <name> of each person (e.g. ss.person.age, ss.person.urban), checked when the sim is built
It can be used in a condition (ss.person.age < 15, for who=) and, as the person’s value, in a rate, a branch probability, or a result (expit(b0 + b1*ss.person.urban)); a condition in a rate is 1 for the people it holds for and 0 for the others (0.3*(ss.person.age > 65)).
Examples
p_use = expit(b0 + b_age*ss.person.age + b_urban*ss.person.urban)
kids = ss.person.age < 15Methods
| Name | Description |
|---|---|
| get | ss.person.<name>, or default for everyone if nothing adds the property <name> to the people, e.g. one that a |
get
people.LazyPeople.get(name, default)ss.person.<name>, or default for everyone if nothing adds the property <name> to the people, e.g. one that a module adds (people.add_state()), read by a rate whether or not that module is in the sim
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| name | str |
the name of the property | required |
| default | float |
the value for everyone if nothing adds the property | required |
Example
onset = S >> D @ ss.peryear(0.01)*(1 + 0.5*ss.person.get('unstable_housing', 0))MadeOnUse
people.MadeOnUse()One of the four fundamental arrays of ss.People, made on first use (see People.make_uids())
A descriptor rather than a branch of People.__getattr__(), so that a subclass with a __getattr__() of its own can’t hide it. It defines only __get__(), so once the arrays are made, the attribute in the instance’s __dict__ is found first and this costs nothing.
People
people.People(
n_agents,
age_data=None,
extra_states=None,
p_female=0.5,
mock=False,
)The people in a sim: their ages, sexes, who is alive, and the states that modules add to them; also how agents are born, die, and are counted
The sim usually makes this itself, from n_agents= (and total_pop=, the people they stand for). Give one yourself to give agents ages from data of your own (ss.People(n_agents, age_data=...)) or extra states; for the population of a place, use ss.population('Kenya'). In a compartmental run, each row is a group of people rather than an agent.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| n_agents | int |
the initial number of agents in the model | required |
| age_data | dataframe/series/array/str/ss.Dist |
the age distribution: a dataframe with columns age (the lower edge of each age bin) and value (the number or fraction of people in it), a series of values indexed by age, a two-column array, a CSV file with the dataframe’s columns, or a distribution of ages (default: the world’s ages). This gives agents ages; for age groups in a compartmental model, use disease.stratify(age={0: 0.3, 15: 0.6, 65: 0.1}) instead |
None |
| extra_states | list |
more properties of each person, e.g. [ss.FloatArr('income')] (modules can also add them, with people.add_state()) |
None |
| p_female | float |
the fraction of agents who are female | 0.5 |
| mock | bool |
if True, link the people to a mock sim, so they can be used without one (for testing and debugging) | False |
Examples
ppl = ss.People(2000)
kids = ss.person.age < 15 # A lazy condition, for a sim run as agents or as compartments (see ss.person)Attributes
| Name | Description |
|---|---|
| auto_state_list | The people’s own ss.BoolState states (e.g. alive, female), each of which makes a result of the same name, the number of people in it |
| dead | Whether each agent has died |
| grouped | Whether these are the groups of a compartmental run (one row per group), not agents |
| male | Whether each agent is male (~people.female; female is a state) |
| n | The number of people alive now, under every solver, as sir.I.n counts the people in a state (the same as people.alive.n): |
| n_agents | The number of active agents, as len(people) (for the number of people alive, see people.n) |
| n_uids | The number of agents ever made, alive or not (each has its own UID) |
Methods
| Name | Description |
|---|---|
| add_active | Add agents to the active agents, in place where there is room (so a few births do not copy all of them) |
| add_module | Add a module to the people: its states are registered, so that they grow with the agents, and the module is found in two ways |
| add_state | Add a property of each person, from a module (e.g. a social determinant of health that several modules read, as ss.person.<name>) |
| age_bins | The bin of each agent’s age, as np.digitize(age, edges), by UID: everyone gets older at each step, but few leave their bin, so only those who did are worked out again |
| born | Tell each module that defines on_birth() about these newborns (see ss.Module.on_birth()); called wherever children are born |
| brief | Print a one-line summary of the people |
| death_modules | The modules whose on_death() is called at a death (see ss.Module.on_death()): the diseases, then any other declared module, then any other module that defines it |
| died_of | The agents who died in this step of a cause (a module or a name), once each death is resolved to one cause (see request_death()) |
| disp | Print every attribute of the people |
| dying | The agents who died or left in this step (see step_die()), who are active until the end of it |
| finish_step | Remove dead agents and run post-step updates (in one pass over the agents, unless update_post() is changed) |
| get_age_dist | The distribution that new agents’ ages are drawn from, made from age data |
| get_person | All the properties of one agent, as an ss.people.Person |
| grow | Add agents, e.g. newborns or immigrants, and return their UIDs |
| init_mock | Link the people to a mock sim and give them their values, so they can be used without a sim (for testing and debugging) |
| init_results | Make the results of the people; called by Sim.init_results() |
| init_vals | Give each state its initial values (e.g. agents’ ages), the final step of initialization |
| keys | The names of the people’s states, including those of modules (e.g. ‘sir.S’) |
| link_sim | Link the people to the sim, and their distributions (e.g. of ages) to its random numbers; called by sim.init() |
| make_uids | Make the four fundamental arrays: UIDs for tracking agents, slots for tracking random |
| phase | Each agent’s phase, in [0, 1), by UID: a time between steps, t, is taken at step floor(t + phase), so it is rounded up |
| plot | Plot the distribution of each of the people’s states: a histogram for numbers, and a bar chart for true/false |
| plot_ages | Plot the age pyramid: the number or fraction of females and males in each age group |
| record_events | Record the people who died or left in the step, at its index; they are taken out at the end of it (see finish_step()), whenever in the step it was requested |
| record_states | Record the population counts at the start of the step (each ss.BoolState, e.g. alive, female), at its index (see ss.loop.Loop) |
| remove_dead | Remove agents who are exiting the population (death, migration, etc) |
| request_death | Request that agents die at this step; how a module kills agents |
| request_removal | Request that agents leave the sim at this step for a reason other than death (e.g. emigration), so they are counted as having left (results.emigrated) rather than as having died |
| resolve_causes | Give each agent who dies in this step one cause, from the deaths requested (see request_death()); called by step_die() |
| scale_flows | The number of people some agents stand for, by their weights (people.scale), e.g. for a result that counts people rather than agents |
| slot_raw | The slot of every agent, by UID, for a kernel; or None while the slots are the UIDs (see make_uids()), which have no array of their own |
| step_die | Carry out the deaths and removals of this step (those requested in it, or set for it before), and return who died or left |
| taken_names | The names of the attributes every People has (e.g. “age”, “version”), which a module can’t take from its variable |
| to_df | Export to dataframe; 2D states have one column per value, e.g. sus_imm_0 (or sus_imm_wild if the columns are named) |
| unborn_uids | The active agents not yet born, with a negative age (e.g. made at conception by ss.Pregnancy), who are not counted among the people (e.g. in the result alive) |
| update_post | Updates at the end of each step: agents get older (see finish_step()) |
add_active
people.People.add_active(new_uids)Add agents to the active agents, in place where there is room (so a few births do not copy all of them)
add_module
people.People.add_module(module)Add a module to the people: its states are registered, so that they grow with the agents, and the module is found in two ways
People.<module>will be a reference to the module itself, enabling access viapeople.sir.SPeople.states['<module>.<state>']will be a reference to the state, enabling access viapeople.states['sir.S']
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| module | Module | the module to add | required |
add_state
people.People.add_state(state)Add a property of each person, from a module (e.g. a social determinant of health that several modules read, as ss.person.<name>)
Idempotent: if the people have a property of that name already (added by another module, say), it is returned as it is, so each module that reads it can add it. Call it before the people’s values are made, e.g. in a module’s init_pre(); added later, its values are made at once.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| state | Arr | the array, e.g. ss.BoolArr('housing', default=False) or ss.FloatArr('adherence', default=ss.uniform()) |
required |
Returns
| Name | Type | Description |
|---|---|---|
| The property’s array (the one given, or the one added before) |
Example
def init_pre(self, sim):
super().init_pre(sim)
self.housing = sim.people.add_state(ss.BoolArr('housing', default=ss.bernoulli(0.8)))age_bins
people.People.age_bins(edges)The bin of each agent’s age, as np.digitize(age, edges), by UID: everyone gets older at each step, but few leave their bin, so only those who did are worked out again
born
people.People.born(children)Tell each module that defines on_birth() about these newborns (see ss.Module.on_birth()); called wherever children are born
brief
people.People.brief(output=False)Print a one-line summary of the people
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| output | bool |
if True, return the text rather than printing it | False |
death_modules
people.People.death_modules()The modules whose on_death() is called at a death (see ss.Module.on_death()): the diseases, then any other declared module, then any other module that defines it
died_of
people.People.died_of(cause)The agents who died in this step of a cause (a module or a name), once each death is resolved to one cause (see request_death())
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| cause | Module / str |
the module, or its name | required |
disp
people.People.disp()Print every attribute of the people
dying
people.People.dying()The agents who died or left in this step (see step_die()), who are active until the end of it
finish_step
people.People.finish_step()Remove dead agents and run post-step updates (in one pass over the agents, unless update_post() is changed)
get_age_dist
people.People.get_age_dist(age_data)The distribution that new agents’ ages are drawn from, made from age data
The data are a two-column array, a pandas series with age as the index and the number (or fraction) of people as the value, a dataframe with “age” and “value” columns, or the name of a CSV file with those columns, e.g.:
age value
0 220.548
1 206.188
2 195.792
3 187.442
The ages are the lower edges of the bins, so ages within a bin are spread evenly. The last bin is as wide as the one before it; to set its upper edge, add a row with that age and a value of 0. Counts and fractions give the same distribution.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| age_data | array/series/dataframe/str/ss.Dist |
the data, as above, or a distribution of ages, used as it is (default: the world’s ages, ss.data.age('world'), as for a sim with no named population) |
required |
Returns
| Name | Type | Description |
|---|---|---|
An ss.Dist of ages |
get_person
people.People.get_person(ind)All the properties of one agent, as an ss.people.Person
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| ind | int |
the UID of the agent | required |
Examples
sim = ss.Sim(diseases='sir', networks='random', n_agents=100).run()
print(sim.people.get_person(5)) # The agent with UID 5grow
people.People.grow(n=None, new_slots=None, placed=False, parent=None, age=None)Add agents, e.g. newborns or immigrants, and return their UIDs
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| n | int |
the number of agents to add (default: one for each of new_slots) |
None |
| new_slots | array |
the slots of the new agents, which say which random numbers each draws (default: their UIDs) | None |
| placed | bool |
whether the caller gives them their levels of the sim’s stratifications; if not, they are drawn from each one’s split | False |
| parent | array |
the parent of each (e.g. for newborns), set before their levels are drawn, so they can be born where their parent is | None |
| age | array |
their age (e.g. 0 for newborns), likewise | None |
init_mock
people.People.init_mock()Link the people to a mock sim and give them their values, so they can be used without a sim (for testing and debugging)
init_results
people.People.init_results()Make the results of the people; called by Sim.init_results()
Each of the people’s own ss.BoolState states makes a result of the number in it (e.g. alive, the number alive); for a state that makes no result, use ss.BoolArr instead. There are also the flows of people who died and who left (died and emigrated; results.died.cum is the cumulative total).
With sim.pars.record_people=False, there are none of these (and record_states() and record_events() are skipped in the loop), which saves a little time and memory.
init_vals
people.People.init_vals()Give each state its initial values (e.g. agents’ ages), the final step of initialization
keys
people.People.keys()The names of the people’s states, including those of modules (e.g. ‘sir.S’)
link_sim
people.People.link_sim(sim, init=False)Link the people to the sim, and their distributions (e.g. of ages) to its random numbers; called by sim.init()
make_uids
people.People.make_uids()Make the four fundamental arrays: UIDs for tracking agents, slots for tracking random numbers, AUIDs for tracking alive agents, and agents’ parents
They are made on first use rather than in __init__(), so that a People not yet in a sim (such as the copy a sim keeps of itself before it is initialized, for sim.new()) holds no arrays per agent. The slots read the UIDs, and the parents are all none, until one is set (see shared in ss.Arr), so they take no memory unless something (e.g. ss.Pregnancy, with fetuses as agents) sets them.
phase
people.People.phase(uids=None)Each agent’s phase, in [0, 1), by UID: a time between steps, t, is taken at step floor(t + phase), so it is rounded up or down at random in proportion (2.3 steps is 2 for 70% of agents and 3 for the rest), keeping its mean, with no draws: both a time due in a disease written as code (ss.TimeArr.due()) and a dwell time of the agent engine. Spread evenly over the slots (so an agent has the same phase in every scenario), and shifted by the sim’s seed
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | uids | the agents to give the phases of (worked out for them alone); by default, every agent’s, kept until agents are made | None |
plot
people.People.plot(
key=None,
alive=True,
bins=50,
hist_kw=None,
max_plots=20,
figsize=(16, 12),
**kwargs,
)Plot the distribution of each of the people’s states: a histogram for numbers, and a bar chart for true/false
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| key | str / list |
the state(s) to plot (default: all, up to max_plots) |
None |
| alive | bool |
if False, include every agent ever made, not only those active | True |
| bins | int |
the number of bins to use in plt.hist() |
50 |
| hist_kw | dict |
passed to plt.hist() |
None |
| max_plots | int |
the maximum number of plots to create | 20 |
| figsize | tuple |
passed to plt.figure(); by default large since the plots are crowded |
(16, 12) |
| **kwargs | dict |
passed to sc.getrowscols(), including fig_kw |
plot_ages
people.People.plot_ages(bins=None, absolute=False, fig_kw=None)Plot the age pyramid: the number or fraction of females and males in each age group
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| bins | list / int |
the edges of the age groups, or the width of each (default: 5-year groups up to the oldest age) | None |
| absolute | bool |
whether to show absolute numbers or percentage of the population | False |
| fig_kw | dict |
passed to plt.subplots() |
None |
Examples
sim = ss.demo(plot=False)
sim.people.plot_ages()record_events
people.People.record_events()Record the people who died or left in the step, at its index; they are taken out at the end of it (see finish_step()), whenever in the step it was requested
record_states
people.People.record_states()Record the population counts at the start of the step (each ss.BoolState, e.g. alive, female), at its index (see ss.loop.Loop)
remove_dead
people.People.remove_dead(aging=False)Remove agents who are exiting the population (death, migration, etc)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| aging | bool |
also age the agents who remain (as update_post() does), in the same pass over them |
False |
request_death
people.People.request_death(uids, cause=None, hazard=None)Request that agents die at this step; how a module kills agents
Use this rather than setting People.ti_dead directly, so that modules don’t overwrite each other’s deaths (e.g. a death set for the future overwriting one due now from another module). A module with a future time of death (e.g. after the course of a disease) keeps it itself, and calls this when the time comes, with itself as the cause.
The deaths happen after every module has stepped, but before the analyzers, so that analyzers can see and record the agents who died in this step.
Each person dies once, of one cause: if several modules request the death of the same person in the same step, one of them is chosen at random (whatever the order of the modules, with common random numbers): in proportion to the hazards of the claims if each gave one (hazard=), and otherwise each equally likely. The cause is kept in people.cause_of_death (see people.causes). A module counts its deaths after they are resolved, with people.died_of(module), so a death is counted once.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | array |
the agents who die | required |
| cause | Module / str |
what they die of: a module (by its name) or a name (default: none) | None |
| hazard | float / array |
the hazard of this death, one number or one per agent (in any unit, the same for every module, e.g. per year), to weight the choice of cause among the claims on the same person in the step (e.g. dis.death.hazard(uids) for a declared transition) |
None |
request_removal
people.People.request_removal(uids)Request that agents leave the sim at this step for a reason other than death (e.g. emigration), so they are counted as having left (results.emigrated) rather than as having died
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | array |
the agents to remove | required |
resolve_causes
people.People.resolve_causes(death_uids)Give each agent who dies in this step one cause, from the deaths requested (see request_death()); called by step_die()
scale_flows
people.People.scale_flows(inds)The number of people some agents stand for, by their weights (people.scale), e.g. for a result that counts people rather than agents
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| inds | array |
the agents to count | required |
slot_raw
people.People.slot_raw()The slot of every agent, by UID, for a kernel; or None while the slots are the UIDs (see make_uids()), which have no array of their own
step_die
people.People.step_die()Carry out the deaths and removals of this step (those requested in it, or set for it before), and return who died or left
taken_names
people.People.taken_names()The names of the attributes every People has (e.g. “age”, “version”), which a module can’t take from its variable
to_df
people.People.to_df()Export to dataframe; 2D states have one column per value, e.g. sus_imm_0 (or sus_imm_wild if the columns are named)
unborn_uids
people.People.unborn_uids()The active agents not yet born, with a negative age (e.g. made at conception by ss.Pregnancy), who are not counted among the people (e.g. in the result alive)
update_post
people.People.update_post()Updates at the end of each step: agents get older (see finish_step())
Person
people.Person()All the properties of one agent (see People.get_person()), as a dict that can be turned into a dataframe
Examples
sim = ss.Sim(diseases='sir', networks='random', n_agents=100).run()
print(sim.people.get_person(5)) # The agent with UID 5Methods
| Name | Description |
|---|---|
| to_df | The properties as a dataframe, one row each |
to_df
people.Person.to_df()The properties as a dataframe, one row each
Functions
| Name | Description |
|---|---|
| population | A named population: the size, age structure, and sex ratio of a country or a US state |
population
people.population(place, year=None, n_agents=None, revision=None)A named population: the size, age structure, and sex ratio of a country or a US state
The data are the UN World Population Prospects, bundled with starsim_data (for a US state, the US Census for 2023, downloaded on first use: see ss.data.population()). The sim takes its population size (total_pop) from them, unless it is given one, and the split of an age stratification that is not given one, so a compartmental model can use a named population as well as agents can. Where the population came from is shown by sim.describe().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| place | str |
a country, by name or code (‘Kenya’, ‘KE’, ‘KEN’), or a US state (‘United States:Massachusetts’) | required |
| year | int |
the year (default: the year the sim starts, if it has dates; otherwise the year of the WPP revision, e.g. 2024); years between the decadal values are interpolated | None |
| n_agents | int |
the number of agents (default: the sim’s); giving it makes the model agent-based, as ss.People(n_agents) does |
None |
| revision | str |
the WPP revision (default: the latest bundled) | None |
Returns
| Name | Type | Description |
|---|---|---|
An ss.People, with ppl.named saying where it came from |
Examples
sim = ss.Sim(sir, people=ss.population('Kenya', year=2024), networks=ss.contacts('Kenya'))
ss.population('NG').named.total # The population of Nigeria in 2024