feat: structural reform hooks in Python wrapper#23
Merged
nikhilwoodruff merged 2 commits intomainfrom Apr 8, 2026
Merged
Conversation
Adds StructuralReform(pre=..., post=...) with the signature:
hook(year, persons, benunits, households) -> (persons, benunits, households)
The year argument enables multi-year reforms. Pre-hooks mutate input
DataFrames before the Rust binary runs; post-hooks mutate microdata output
after the binary produces results, with aggregation then done in Python.
Also extracts a standalone aggregate_microdata() function used both by
post-hook runs and (replacing the old _aggregate_persons_only) for
persons-only datasets.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
StructuralReform(pre=..., post=...)to the Python wrapper, enabling reforms that can't be expressed as parameter overlays.Both hooks share the same signature so multi-year reforms work cleanly:
Pre-hook — runs before the Rust binary sees the data. Mutate input columns: add a new income source, change household composition, set benefit eligibility flags, etc.
Post-hook — runs after the binary produces microdata output. All
baseline_*/reform_*columns are populated at this point. Mutate result columns to apply a new tax, offset a benefit, impose a cap, etc. Aggregation intoSimulationResultthen happens in Python via the newaggregate_microdata()function.Both hooks work for DataFrame-based (single household) and file-based (FRS/dataset) sources. For file-based sources with a pre-hook, the CSVs are loaded into DataFrames, the hook is applied, and the result is piped to the binary via stdin.