Skip to content

Commit fa0a4fc

Browse files
committed
more docs
1 parent 925a7bf commit fa0a4fc

18 files changed

+531
-93
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Docker imposes a global limit on how much RAM containers can allocate. DEMOS eas
2727
docker build -t demos:0.0.1 --platform=linux/amd64 -f Dockerfile .
2828
```
2929

30-
30+
### From Source
3131

3232
1. Clone this repository
3333
```

configuration/demos_config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ modules = [
2222
"employment",
2323
"household_reorg",
2424
"kids_moving",
25-
# "fatality_model",
25+
"mortality",
2626
# "birth_model",
2727
# "education_model",
2828
"household_rebalancing",

demos/models/aging.py

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import orca
22
import time
3-
import numpy as np
43
import pandas as pd
54
from logging_logic import log_execution_time
65
from config import DEMOSConfig, AgingModuleConfig, get_config
@@ -13,14 +12,20 @@
1312
@orca.step(STEP_NAME)
1413
def aging(persons):
1514
"""
16-
Increases the age of every person in the persons table by 1
15+
Increment the age of every person by one year.
1716
18-
**Required tables:**
19-
- persons
20-
21-
**Modifies State Variables:**
22-
- persons.age
17+
This step updates the `age` column in the `persons` table, simulating the passage of one year
18+
for all agents in the population.
2319
20+
Parameters
21+
----------
22+
persons : orca.Table
23+
The persons table containing the `age` column.
24+
25+
Notes
26+
-----
27+
- Modifies `persons.age` in place.
28+
- Should be run once per simulation year.
2429
"""
2530
start_time = time.time()
2631
persons["age"] += 1
@@ -30,17 +35,42 @@ def aging(persons):
3035
@orca.column(table_name="persons")
3136
def child(data="persons.relate"):
3237
"""
33-
This column returns a binary value equal to 1 if the person has a `relate` value
34-
of `2`, `3`, `4` or `14`.
38+
Identify children in the persons table.
39+
40+
Returns a binary indicator (1 or 0) for each person, where 1 indicates the person is a child
41+
based on the `relate` code (values 2, 3, 4, or 14).
42+
43+
Parameters
44+
----------
45+
data : pandas.Series
46+
The `relate` column from the persons table.
47+
48+
Returns
49+
-------
50+
pandas.Series
51+
Binary indicator for child status.
3552
"""
3653
return data.isin([2, 3, 4, 14]).astype(int)
3754

3855

3956
@orca.column(table_name="persons")
4057
def senior(data="persons.age"):
4158
"""
42-
Returns a binary value for each row in persons table set to 1 if the person
43-
is above `aging_module_config.senior_age` (defaults to `65`).
59+
Identify seniors in the persons table.
60+
61+
Returns a binary indicator (1 or 0) for each person, where 1 indicates the person is a senior,
62+
defined as having an age greater than or equal to the threshold specified in the aging module config
63+
(default is 65).
64+
65+
Parameters
66+
----------
67+
data : pandas.Series
68+
The `age` column from the persons table.
69+
70+
Returns
71+
-------
72+
pandas.Series
73+
Binary indicator for senior status.
4474
"""
4575
# Load calibration config
4676
demos_config: DEMOSConfig = get_config()
@@ -50,6 +80,21 @@ def senior(data="persons.age"):
5080

5181
@orca.column(table_name="persons")
5282
def age_group(data="persons.age"):
83+
"""
84+
Assign each person to an age group.
85+
86+
Categorizes persons into predefined age intervals for use in modeling and reporting.
87+
88+
Parameters
89+
----------
90+
data : pandas.Series
91+
The `age` column from the persons table.
92+
93+
Returns
94+
-------
95+
pandas.Series
96+
Categorical age group labels as strings.
97+
"""
5398
age_intervals = [0, 20, 30, 40, 50, 65, 900]
5499
age_labels = ['lte19', '20-29', '30-39', '40-49', '50-64', 'gte65']
55100
return pd.cut(data, bins=age_intervals, labels=age_labels, include_lowest=True).astype(str)

demos/models/employment.py

Lines changed: 127 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,28 @@
1919
@orca.step(STEP_NAME)
2020
def employment(persons):
2121
"""
22-
Executes the `enter_` and `exit_laborforce` estimated models to determine which unemployed
23-
persons change to employed and which employed change to unemployed. Elegible persons are those
24-
18 years or older.
22+
Simulate labor force transitions for eligible persons.
2523
26-
The `earning` column stores the annual income of each individual. For those unemployed, `earning` is set
27-
to 0, for the rest, the `new_earning` lazily-computed orca column is used to determine the new income.
24+
This step applies estimated models to determine which unemployed persons become employed
25+
and which employed persons exit the workforce. Only persons aged 18 or older are considered.
26+
The function updates the `worker` and `earning` columns in the `persons` table.
2827
29-
**Required tables:**
30-
- persons
28+
Parameters
29+
----------
30+
persons : orca.Table
31+
The persons table containing individual-level attributes.
3132
32-
**Modifies State Variables:**
33-
- persons.worker
34-
- persons.earning
33+
Notes
34+
-----
35+
- Requires the `persons` table with columns: `age`, `worker`, `earning`.
36+
- Modifies `persons.worker` and `persons.earning` in place.
37+
- Uses module configuration from the TOML config file.
38+
- Triggers caching of the `income_dist` table for income assignment.
39+
40+
Example
41+
-------
42+
This step is executed as part of the annual simulation loop:
43+
`orca.run(['employment'], iter_vars=[...])`
3544
"""
3645
start_time = time.time()
3746

@@ -63,14 +72,38 @@ def employment(persons):
6372

6473
def sample_income(mean, std):
6574
"""
66-
Auxiliary function to sample from a lognormal distribution
75+
Draw samples from a lognormal distribution.
76+
77+
Parameters
78+
----------
79+
mean : float or array-like
80+
The mean(s) of the underlying normal distribution.
81+
std : float or array-like
82+
The standard deviation(s) of the underlying normal distribution.
83+
84+
Returns
85+
-------
86+
float or np.ndarray
87+
Sample(s) from the lognormal distribution.
6788
"""
6889
return np.random.lognormal(mean, std)
6990

7091

7192
def run_and_calibrate_in_workforce_model(persons: pd.DataFrame, calibration_procedure: CalibrationConfig) -> pd.Series:
7293
"""
73-
Execute `enter_labor_force` estimated model on elegible people according to the calibration procedure.
94+
Run the 'enter_labor_force' estimated model for eligible persons.
95+
96+
Parameters
97+
----------
98+
persons : pandas.DataFrame
99+
DataFrame of persons with required model variables.
100+
calibration_procedure : CalibrationConfig or None
101+
Calibration procedure to apply, if any.
102+
103+
Returns
104+
-------
105+
pandas.Series
106+
Model predictions for each eligible person (0 = remain unemployed, 1 = become employed).
74107
"""
75108
# Get model data
76109
model = mm.get_step("enter_labor_force")
@@ -86,7 +119,19 @@ def run_and_calibrate_in_workforce_model(persons: pd.DataFrame, calibration_proc
86119

87120
def run_and_calibrate_out_workforce_model(persons: pd.DataFrame, calibration_procedure: CalibrationConfig) -> pd.Series:
88121
"""
89-
Execute `exit_labor_force` estimated model on elegible people according to the calibration procedure.
122+
Run the 'exit_labor_force' estimated model for eligible persons.
123+
124+
Parameters
125+
----------
126+
persons : pandas.DataFrame
127+
DataFrame of persons with required model variables.
128+
calibration_procedure : CalibrationConfig or None
129+
Calibration procedure to apply, if any.
130+
131+
Returns
132+
-------
133+
pandas.Series
134+
Model predictions for each eligible person (0 = remain employed, 1 = become unemployed).
90135
"""
91136
# Get model data
92137
model = mm.get_step("exit_labor_force")
@@ -102,7 +147,19 @@ def run_and_calibrate_out_workforce_model(persons: pd.DataFrame, calibration_pro
102147

103148
def run_simultaenous_calibration(persons: pd.DataFrame, simultaneous_calibration_config: SimultaneousCalibrationConfig) -> pd.Series:
104149
"""
105-
Execute both `enter_labor_force` and `exit_labor_force` estimated models on elegible people according to the calibration procedure.
150+
Run simultaneous calibration for both 'enter' and 'exit' labor force models.
151+
152+
Parameters
153+
----------
154+
persons : pandas.DataFrame
155+
DataFrame of persons with required model variables.
156+
simultaneous_calibration_config : SimultaneousCalibrationConfig
157+
Configuration for simultaneous calibration.
158+
159+
Returns
160+
-------
161+
tuple of pandas.Series
162+
Predictions for entering and exiting the workforce.
106163
"""
107164
# Get enter model data
108165
enter_model = mm.get_step("enter_labor_force")
@@ -156,30 +213,68 @@ def run_simultaenous_calibration(persons: pd.DataFrame, simultaneous_calibration
156213
@orca.column(table_name="persons")
157214
def new_earning(persons, income_dist):
158215
"""
159-
Sample income for new workers according to income distribution (`income_dist`) computed from the original synthetic population.
160-
The income distribution is computed once and cached to be used in `new_earning` calls.
216+
Compute new earnings for persons entering the workforce.
217+
218+
For each eligible person, samples income from a lognormal distribution
219+
parameterized by age and education group, using the cached `income_dist` table.
220+
221+
Parameters
222+
----------
223+
persons : orca.Table
224+
The persons table.
225+
income_dist : orca.Table
226+
Table with income distribution parameters.
161227
162-
Click `[source]` (top right) for more details
228+
Returns
229+
-------
230+
pandas.Series
231+
Sampled earnings for each person.
163232
"""
164233
persons_df = persons.to_frame(["age_group", "education_group"])
165234
merged_df = persons_df.merge(income_dist.local, on=['age_group', 'education_group'], how='left')
166235
return pd.Series(sample_income(merged_df["mu"], merged_df["sigma"]), index=persons_df.index)
167236

237+
168238
@orca.column(table_name="households")
169239
def hh_workers(persons):
170240
"""
171-
Number of workers per household
241+
Compute the number of workers per household.
242+
243+
Parameters
244+
----------
245+
persons : orca.Table
246+
The persons table.
247+
248+
Returns
249+
-------
250+
pandas.Series
251+
Categorical summary: "none", "one", or "two or more" workers per household.
172252
"""
173253
return persons.to_frame(["household_id", "worker"]) \
174254
.groupby("household_id") \
175255
.sum()["worker"] \
176256
.apply(lambda r: "none" if r == 0 else
177257
("one" if r == 1 else "two or more"))
178258

259+
179260
@orca.column(table_name="households")
180261
def income(persons):
181262
"""
182-
Household-level income from person-level income. NOTE: This is for HOUSEHOLDS table
263+
Aggregate household income from person-level earnings.
264+
265+
Parameters
266+
----------
267+
persons : orca.Table
268+
The persons table.
269+
270+
Returns
271+
-------
272+
pandas.Series
273+
Total income per household.
274+
275+
Notes
276+
-----
277+
This is for `HOUSEHOLDS` table
183278
"""
184279
return persons.to_frame(["household_id", "earning"]) \
185280
.groupby("household_id") \
@@ -188,9 +283,19 @@ def income(persons):
188283
@orca.table(cache=True, cache_scope='forever')
189284
def income_dist(persons):
190285
"""
191-
This table is computed the first time it is called, and stores the average and standard deviation of the income
192-
of workers at the beginning of the simulation by age and education group. These values are used in the employment
193-
model to assign an income for people that get into the workforce.
286+
Compute and cache income distribution parameters by age and education group.
287+
288+
This table is used to sample earnings for new workers in the employment model.
289+
290+
Parameters
291+
----------
292+
persons : orca.Table
293+
The persons table.
294+
295+
Returns
296+
-------
297+
pandas.DataFrame
298+
DataFrame with columns: age_group, education_group, data_mean, data_std, mu, sigma.
194299
"""
195300
income_dist = persons.to_frame(["worker", "age_group", "education_group", "earning"])[persons["worker"]==1]\
196301
.groupby(['age_group', 'education_group']).agg(

0 commit comments

Comments
 (0)