Skip to content

Commit ff25551

Browse files
committed
More docs
1 parent 3c78ee5 commit ff25551

File tree

18 files changed

+475
-213
lines changed

18 files changed

+475
-213
lines changed

.github/workflows/black.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Code style (Black)
2+
3+
on:
4+
push:
5+
paths: ["**/*.py", "pyproject.toml"]
6+
pull_request:
7+
8+
jobs:
9+
black:
10+
runs-on: ubuntu-latest
11+
timeout-minutes: 10
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-python@v5
15+
with:
16+
python-version: "3.11"
17+
cache: pip
18+
- name: Install Black
19+
run: |
20+
python -m pip install --upgrade pip
21+
pip install black==24.8.0
22+
- name: Check formatting
23+
run: black --check --diff .

configuration/demos_config.toml

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,27 @@ base_year = 2010
44
forecast_year = 2019
55
calibrated_models_dir = "../data/calibrated_configs/custom/06197001"
66
inconsistent_persons_table_behavior = "fix"
7+
modules = [
8+
"aging",
9+
"employment",
10+
"household_reorg",
11+
"kids_moving",
12+
"fatality",
13+
"birth",
14+
"education",
15+
"household_rebalancing",
16+
"update_income"
17+
]
718
output_tables = [
819
"persons",
920
"households",
10-
"graveyard",
11-
"run_times",
12-
"marital_rebalanced",
13-
"marital_status_output"
21+
"run_times"
1422
]
1523
initialize_empty_tables = [
1624
"graveyard",
1725
"rebalanced_persons",
1826
"rebalanced_households"
1927
]
20-
modules = [
21-
"aging",
22-
"employment",
23-
"household_reorg",
24-
"kids_moving",
25-
"mortality_model",
26-
"birth_model",
27-
"education_model",
28-
"household_rebalancing",
29-
"update_income"
30-
]
3128

3229
# Data sources
3330
## Synthetic population data
@@ -55,14 +52,14 @@ file_type = "csv"
5552
table_name = "income_rates"
5653
filepath = "../data/income_rates_06197001.csv"
5754
index_col = "year"
58-
custom_dtype_casting = {"lcm_county_id"= "object", "year"= "int", "rate"= "float"}
55+
custom_dtype_casting = {"lcm_county_id" = "object", "year" = "int", "rate" = "float"}
5956

6057
[[tables]]
6158
file_type = "csv"
6259
table_name = "hsize_ct"
6360
filepath = "../data/hsize_ct_06197001.csv"
6461
index_col = "year"
65-
custom_dtype_casting = {"lcm_county_id"="object", "year"="int", "hh_size"="object", "total_number_of_households"="int"}
62+
custom_dtype_casting = {"lcm_county_id" = "object", "year" = "int", "hh_size" = "object", "total_number_of_households" = "int"}
6663

6764
## Simultaneous Calibration data
6865
### For simultaneous calibration, tables need
@@ -81,25 +78,9 @@ index_col = "year"
8178

8279

8380
# Modules configuration
84-
85-
## Employment
86-
# [employment_module_config.enter_model_calibration_procedure]
87-
# procedure_type = "rmse_error"
88-
# observed_values_table = "observed_entering_workforce"
89-
# tolerance_type = "relative"
90-
# tolerance = 0.01
91-
# max_iter = 1000
92-
93-
# [employment_module_config.exit_model_calibration_procedure]
94-
# procedure_type = "rmse_error"
95-
# observed_values_table = "observed_exiting_workforce"
96-
# tolerance_type = "relative"
97-
# tolerance = 0.01
98-
# max_iter = 1000
99-
10081
[employment_module_config.simultaneous_calibration_config]
10182
tolerance = 100
102-
max_iter = 2 # 100
83+
max_iter = 2
10384
learning_rate = 2
10485
momentum_weight = 0.3
10586

demos/models/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from .data_fix import *
2-
from .aging import REQUIRED_COLUMNS as AGING_COLUMNS
2+
from .aging import *
33
from .employment import *
44
from .household_reorg import *
55
from .marriage import *
66
from .kids_moving import *
7-
from .mortality import *
7+
from .fatality import *
88
from .birth import *
99
from .education import *
1010
from .rebalancing import *

demos/models/birth.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,25 @@
77
from logging_logic import log_execution_time
88
from config import DEMOSConfig, get_config
99

10+
STEP_NAME = "birth"
11+
REQUIRED_COLUMNS = []
12+
1013

1114
@orca.injectable(autocall=False)
1215
def get_new_person_id(n):
16+
"""
17+
Generate new unique person IDs for newborns.
18+
19+
Parameters
20+
----------
21+
n : int
22+
Number of new person IDs to generate.
23+
24+
Returns
25+
-------
26+
np.ndarray
27+
Array of new unique person IDs.
28+
"""
1329
persons = orca.get_table("persons")
1430
graveyard = orca.get_table("graveyard")
1531
rebalanced_persons = orca.get_table("rebalanced_persons")
@@ -28,21 +44,33 @@ def get_new_person_id(n):
2844
)
2945

3046

31-
@orca.step("birth_model")
32-
def birth_model(persons, households, observed_births_data, get_new_person_id, year):
47+
@orca.step(STEP_NAME)
48+
def birth(persons, households, get_new_person_id):
3349
"""
34-
Function to run the birth model at the household level.
35-
The function updates the persons table.
36-
37-
Modifies State Variables:
38-
- (Adds rows to `persons` table)
39-
40-
Args:
41-
persons (DataFrameWrapper): DataFrameWrapper of the persons table
42-
households (DataFrameWrapper): DataFrameWrapper of the households table
43-
44-
Returns:
45-
None
50+
Simulate household-level births and add new persons to the population.
51+
52+
This step applies the birth model to eligible households, determines which have a birth event,
53+
and adds new babies to the persons table with default and inferred attributes.
54+
55+
Parameters
56+
----------
57+
persons : orca.Table
58+
The persons table containing individual-level attributes.
59+
households : orca.Table
60+
The households table containing household-level attributes.
61+
get_new_person_id : callable
62+
Function to generate new unique person IDs as needed.
63+
64+
Returns
65+
-------
66+
None
67+
68+
Notes
69+
-----
70+
- Adds new rows to the persons table for each birth event.
71+
- Babies are assigned default values for most attributes.
72+
- Race is assigned based on household head if all members share the same race; otherwise, "other".
73+
- Some attributes may be duplicated or missing if not set in input data.
4674
"""
4775
start_time = time.time()
4876
birth_list = run_and_calibrate_birth_model(persons, households)

demos/models/education.py

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,44 @@
66
from logging_logic import log_execution_time
77
from templates.utils.models import columns_in_formula
88

9-
10-
@orca.step("education_model")
11-
def education_model(
9+
STEP_NAME = "education"
10+
REQUIRED_COLUMNS = [
11+
"persons.edu",
12+
"persons.student",
13+
]
14+
15+
@orca.step(STEP_NAME)
16+
def education(
1217
persons, edu_highschool_proportion, edu_highschool_grads_proportion, year
1318
):
1419
"""
15-
Run the education model and update the persons table
16-
17-
Modifies State Variables:
18-
- persons.edu
19-
- persons.student
20-
21-
Args:
22-
persons (DataFrameWrapper): DataFrameWrapper of the persons table
23-
24-
Returns:
25-
None
20+
Simulate educational attainment and student status transitions.
21+
22+
This step applies the education model to eligible persons (age > 15 and currently students)
23+
to determine who drops out. It advances students through grades and degrees, maintains
24+
proportions of high school and GED graduates, and updates the persons table in place.
25+
26+
Parameters
27+
----------
28+
persons : orca.Table
29+
The persons table containing individual-level attributes.
30+
edu_highschool_proportion : pandas.Series
31+
Proportion of students in 11th and 12th grade.
32+
edu_highschool_grads_proportion : pandas.Series
33+
Proportion of students with GED or high school diploma.
34+
year : int
35+
The current simulation year.
36+
37+
Returns
38+
-------
39+
None
40+
41+
Notes
42+
-----
43+
- Modifies `persons.edu` and `persons.student` in place.
44+
- Only persons older than 15 and currently students are considered for dropout modeling.
45+
- Proportions for transitions (e.g., GED vs. diploma) are maintained using observed data.
46+
- Some transitions use random assignment based on empirical proportions.
2647
"""
2748
start_time = time.time()
2849

@@ -88,16 +109,57 @@ def education_model(
88109

89110
@orca.injectable(name="edu_highschool_proportion")
90111
def edu_highschool_proportion(data="persons.edu"):
112+
"""
113+
Calculate the proportion of students in 11th and 12th grade.
114+
115+
Parameters
116+
----------
117+
data : pandas.Series
118+
The `edu` column from the persons table.
119+
120+
Returns
121+
-------
122+
pandas.Series
123+
Proportion of students in 11th (15) and 12th (16) grade.
124+
"""
91125
return data[data.isin([15, 16])].value_counts(normalize=True)
92126

93127

94128
@orca.injectable(name="edu_highschool_grads_proportion")
95129
def edu_highschool_grads_proportion(data="persons.edu"):
130+
"""
131+
Calculate the proportion of students with GED or high school diploma.
132+
133+
Parameters
134+
----------
135+
data : pandas.Series
136+
The `edu` column from the persons table.
137+
138+
Returns
139+
-------
140+
pandas.Series
141+
Proportion of students with GED (16) or high school diploma (17).
142+
"""
96143
return data[data.isin([16, 17])].value_counts(normalize=True)
97144

98145

99146
@orca.column(table_name="persons")
100147
def education_group(data="persons.edu"):
148+
"""
149+
Assign each person to an education group.
150+
151+
Categorizes persons into predefined education intervals for use in modeling and reporting.
152+
153+
Parameters
154+
----------
155+
data : pandas.Series
156+
The `edu` column from the persons table.
157+
158+
Returns
159+
-------
160+
pandas.Series
161+
Categorical education group labels as strings.
162+
"""
101163
education_intervals = [0, 18, 22, 200]
102164
education_labels = ["lte17", "18-21", "gte22"]
103165
return pd.cut(
Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,46 @@
77
from config import DEMOSConfig, get_config
88
from templates.utils.models import columns_in_formula
99

10-
STEP_NAME = "mortality_model"
10+
STEP_NAME = "fatality"
1111
REQUIRED_COLUMNS = [
1212
"persons.MAR",
1313
"persons.relate",
1414
]
1515

1616

1717
@orca.step(STEP_NAME)
18-
def mortality(persons, households, relational_adjustment_mapping, graveyard):
19-
"""Executes the `mortality` estimated model and updates the households and persons
20-
table accordingly. Importantly, this module updates the `relate` column according to the
21-
`relational_adjustment_mapping` table.
22-
23-
**Required tables:**
24-
- persons
25-
- households
26-
- relational_adjustment_mapping
27-
28-
**Modifies State Variables:**
29-
- persons.MAR
30-
- persons.relate
31-
- (Adds rows from `graveyard` table)
32-
- (Removes rows from `persons` table)
33-
- (Removes rows from `households` table)
18+
def fatality(persons, households, relational_adjustment_mapping, graveyard):
19+
"""
20+
Simulate mortality events and update the persons and households tables.
21+
22+
This step applies the `mortality` estimated model to determine which persons die in the current year.
23+
It removes deceased persons from the persons table, updates marital status and household relationships,
24+
and moves the deceased to the graveyard table. The `relate` column is updated using the relational
25+
adjustment mapping to ensure consistency.
26+
27+
Parameters
28+
----------
29+
persons : orca.Table
30+
The persons table containing individual-level attributes.
31+
households : orca.Table
32+
The households table containing household-level attributes.
33+
relational_adjustment_mapping : orca.Table
34+
Table mapping old relationship codes to new ones after a head of household dies.
35+
graveyard : orca.Table
36+
Table for storing records of deceased individuals.
37+
38+
Returns
39+
-------
40+
None
41+
42+
Notes
43+
-----
44+
- Modifies `persons.MAR`, `persons.relate`, and removes rows from `persons` and `households` tables.
45+
- Adds rows to the `graveyard` table for deceased individuals.
46+
- Updates marital status: surviving spouses/partners become widowed.
47+
- If a household head dies, a partner or the oldest remaining member becomes the new head.
48+
- The `relate` column for all household members is updated using the mapping table.
49+
- Some errors (e.g., multiple spouses per household) are handled silently.
3450
"""
3551
start_time = time.time()
3652

0 commit comments

Comments
 (0)