13 KiB
name, description
| name | description |
|---|---|
| policyengine-us | PolicyEngine-US tax and benefit microsimulation patterns, situation creation, and common workflows |
PolicyEngine-US
PolicyEngine-US models the US federal and state tax and benefit system.
For Users 👥
What is PolicyEngine-US?
PolicyEngine-US is the "calculator" for US taxes and benefits. When you use policyengine.org/us, PolicyEngine-US runs behind the scenes.
What it models:
Federal taxes:
- Income tax (with standard/itemized deductions)
- Payroll tax (Social Security, Medicare)
- Capital gains tax
Federal benefits:
- Earned Income Tax Credit (EITC)
- Child Tax Credit (CTC)
- SNAP (food stamps)
- WIC, ACA premium tax credits
- Social Security, SSI, TANF
State programs (varies by state):
- State income tax (all 50 states + DC)
- State EITC, CTC
- State-specific benefits
See full list: https://policyengine.org/us/parameters
Understanding Variables
When you see results in PolicyEngine, these are variables:
Income variables:
employment_income- W-2 wagesself_employment_income- 1099 incomequalified_dividend_income- Dividendscapital_gains- Capital gains
Tax variables:
income_tax- Federal income taxstate_income_tax- State income taxpayroll_tax- FICA taxes
Benefit variables:
eitc- Earned Income Tax Creditctc- Child Tax Creditsnap- SNAP benefits
Summary variables:
household_net_income- Income after taxes and benefitshousehold_tax- Total taxeshousehold_benefits- Total benefits
For Analysts 📊
Installation and Setup
# Install PolicyEngine-US
pip install policyengine-us
# Or with uv (recommended)
uv pip install policyengine-us
Quick Start
from policyengine_us import Simulation
# Create a household
situation = {
"people": {
"you": {
"age": {2024: 30},
"employment_income": {2024: 50000}
}
},
"families": {"family": {"members": ["you"]}},
"marital_units": {"marital_unit": {"members": ["you"]}},
"tax_units": {"tax_unit": {"members": ["you"]}},
"spm_units": {"spm_unit": {"members": ["you"]}},
"households": {
"household": {
"members": ["you"],
"state_name": {2024: "CA"}
}
}
}
# Calculate taxes and benefits
sim = Simulation(situation=situation)
income_tax = sim.calculate("income_tax", 2024)[0]
eitc = sim.calculate("eitc", 2024)[0]
print(f"Income tax: ${income_tax:,.0f}")
print(f"EITC: ${eitc:,.0f}")
Web App to Python
Web app URL:
policyengine.org/us/household?household=12345
Equivalent Python (conceptually): The household ID represents a situation dictionary. To replicate in Python, you'd create a similar situation.
When to Use This Skill
- Creating household situations for tax/benefit calculations
- Running microsimulations with PolicyEngine-US
- Analyzing policy reforms and their impacts
- Building tools that use PolicyEngine-US (calculators, analysis notebooks)
- Debugging PolicyEngine-US calculations
For Contributors 💻
Repository
Location: PolicyEngine/policyengine-us
To see current implementation:
git clone https://github.com/PolicyEngine/policyengine-us
cd policyengine-us
# Explore structure
tree policyengine_us/
Key directories:
ls policyengine_us/
# - variables/ - Tax and benefit calculations
# - parameters/ - Policy rules (YAML)
# - reforms/ - Pre-defined reforms
# - tests/ - Test cases
Core Concepts
1. Situation Dictionary Structure
PolicyEngine requires a nested dictionary defining household composition and characteristics:
situation = {
"people": {
"person_id": {
"age": {2024: 35},
"employment_income": {2024: 50000},
# ... other person attributes
}
},
"families": {
"family_id": {"members": ["person_id", ...]}
},
"marital_units": {
"marital_unit_id": {"members": ["person_id", ...]}
},
"tax_units": {
"tax_unit_id": {"members": ["person_id", ...]}
},
"spm_units": {
"spm_unit_id": {"members": ["person_id", ...]}
},
"households": {
"household_id": {
"members": ["person_id", ...],
"state_name": {2024: "CA"}
}
}
}
Key Rules:
- All entities must have consistent member lists
- Use year keys for all values:
{2024: value} - State must be two-letter code (e.g., "CA", "NY", "TX")
- All monetary values in dollars (not cents)
2. Creating Simulations
from policyengine_us import Simulation
# Create simulation from situation
simulation = Simulation(situation=situation)
# Calculate variables
income_tax = simulation.calculate("income_tax", 2024)
eitc = simulation.calculate("eitc", 2024)
household_net_income = simulation.calculate("household_net_income", 2024)
Common Variables:
Income:
employment_income- W-2 wagesself_employment_income- 1099/business incomequalified_dividend_income- Qualified dividendscapital_gains- Capital gainsinterest_income- Interest incomesocial_security- Social Security benefitspension_income- Pension/retirement income
Deductions:
charitable_cash_donations- Cash charitable givingreal_estate_taxes- State and local property taxesmortgage_interest- Mortgage interest deductionmedical_expense- Medical and dental expensescasualty_loss- Casualty and theft losses
Tax Outputs:
income_tax- Total federal income taxpayroll_tax- FICA taxesstate_income_tax- State income taxhousehold_tax- Total taxes (federal + state + local)
Benefits:
eitc- Earned Income Tax Creditctc- Child Tax Creditsnap- SNAP benefitshousehold_benefits- Total benefits
Summary:
household_net_income- Income minus taxes plus benefits
3. Using Axes for Parameter Sweeps
To vary a parameter across multiple values:
situation = {
# ... normal situation setup ...
"axes": [[{
"name": "employment_income",
"count": 1001,
"min": 0,
"max": 200000,
"period": 2024
}]]
}
simulation = Simulation(situation=situation)
# Now calculate() returns arrays of 1001 values
incomes = simulation.calculate("employment_income", 2024) # Array of 1001 values
taxes = simulation.calculate("income_tax", 2024) # Array of 1001 values
Important: Remove axes before creating single-point simulations:
situation_single = situation.copy()
situation_single.pop("axes", None)
simulation = Simulation(situation=situation_single)
4. Policy Reforms
from policyengine_us import Simulation
# Define a reform (modifies parameters)
reform = {
"gov.irs.credits.ctc.amount.base_amount": {
"2024-01-01.2100-12-31": 5000 # Increase CTC to $5000
}
}
# Create simulation with reform
simulation = Simulation(situation=situation, reform=reform)
Common Patterns
Pattern 1: Single Household Calculation
from policyengine_us import Simulation
situation = {
"people": {
"parent": {
"age": {2024: 35},
"employment_income": {2024: 60000}
},
"child": {
"age": {2024: 5}
}
},
"families": {"family": {"members": ["parent", "child"]}},
"marital_units": {"marital_unit": {"members": ["parent"]}},
"tax_units": {"tax_unit": {"members": ["parent", "child"]}},
"spm_units": {"spm_unit": {"members": ["parent", "child"]}},
"households": {
"household": {
"members": ["parent", "child"],
"state_name": {2024: "NY"}
}
}
}
sim = Simulation(situation=situation)
income_tax = sim.calculate("income_tax", 2024)[0]
ctc = sim.calculate("ctc", 2024)[0]
Pattern 2: Marginal Tax Rate Analysis
# Create baseline with axes varying income
situation_with_axes = {
# ... situation setup ...
"axes": [[{
"name": "employment_income",
"count": 1001,
"min": 0,
"max": 200000,
"period": 2024
}]]
}
sim = Simulation(situation=situation_with_axes)
incomes = sim.calculate("employment_income", 2024)
taxes = sim.calculate("income_tax", 2024)
# Calculate marginal tax rate
import numpy as np
mtr = np.gradient(taxes) / np.gradient(incomes)
Pattern 3: Charitable Donation Impact
# Baseline (no donation)
situation_baseline = create_situation(income=100000, donation=0)
sim_baseline = Simulation(situation=situation_baseline)
tax_baseline = sim_baseline.calculate("income_tax", 2024)[0]
# With donation
situation_donation = create_situation(income=100000, donation=5000)
sim_donation = Simulation(situation=situation_donation)
tax_donation = sim_donation.calculate("income_tax", 2024)[0]
# Tax savings from donation
tax_savings = tax_baseline - tax_donation
effective_discount = tax_savings / 5000 # e.g., 0.24 = 24% discount
Pattern 4: State Comparison
states = ["CA", "NY", "TX", "FL"]
results = {}
for state in states:
situation = create_situation(state=state, income=75000)
sim = Simulation(situation=situation)
results[state] = {
"state_income_tax": sim.calculate("state_income_tax", 2024)[0],
"total_tax": sim.calculate("household_tax", 2024)[0]
}
Helper Scripts
This skill includes helper scripts in the scripts/ directory:
from policyengine_skills.situation_helpers import (
create_single_filer,
create_married_couple,
create_family_with_children,
add_itemized_deductions
)
# Quick situation creation
situation = create_single_filer(
income=50000,
state="CA",
age=30
)
# Add deductions
situation = add_itemized_deductions(
situation,
charitable_donations=5000,
mortgage_interest=10000,
real_estate_taxes=8000
)
Common Pitfalls and Solutions
Pitfall 1: Member Lists Out of Sync
Problem: Different entities have different members
# WRONG
"tax_units": {"tax_unit": {"members": ["parent"]}},
"households": {"household": {"members": ["parent", "child"]}}
Solution: Keep all entity member lists consistent:
# CORRECT
all_members = ["parent", "child"]
"families": {"family": {"members": all_members}},
"tax_units": {"tax_unit": {"members": all_members}},
"households": {"household": {"members": all_members}}
Pitfall 2: Forgetting Year Keys
Problem: "age": 35 instead of "age": {2024: 35}
Solution: Always use year dictionary:
"age": {2024: 35},
"employment_income": {2024: 50000}
Pitfall 3: Net Taxes vs Gross Taxes
Problem: Forgetting to subtract benefits from taxes
Solution: Use proper calculation:
# Net taxes (what household actually pays)
net_tax = sim.calculate("household_tax", 2024) - \
sim.calculate("household_benefits", 2024)
Pitfall 4: Axes Persistence
Problem: Axes remain in situation when creating single-point simulation
Solution: Remove axes before single-point simulation:
situation_single = situation.copy()
situation_single.pop("axes", None)
Pitfall 5: State-Specific Variables
Problem: Using NYC-specific variables without in_nyc: True
Solution: Set NYC flag for NY residents in NYC:
"households": {
"household": {
"state_name": {2024: "NY"},
"in_nyc": {2024: True} # Required for NYC taxes
}
}
NYC Handling
For New York City residents:
situation = {
# ... people setup ...
"households": {
"household": {
"members": ["person"],
"state_name": {2024: "NY"},
"in_nyc": {2024: True} # Enable NYC tax calculations
}
}
}
Version Compatibility
- Always use
policyengine-us>=1.155.0for 2024 calculations - Check version:
import policyengine_us; print(policyengine_us.__version__) - Different years may require different package versions
Debugging Tips
-
Enable tracing:
simulation.trace = True result = simulation.calculate("variable_name", 2024) -
Check intermediate calculations:
agi = simulation.calculate("adjusted_gross_income", 2024) taxable_income = simulation.calculate("taxable_income", 2024) -
Verify situation structure:
import json print(json.dumps(situation, indent=2)) -
Test with PolicyEngine web app:
- Go to policyengine.org/us/household
- Enter same inputs
- Compare results
Additional Resources
- Documentation: https://policyengine.org/us/docs
- API Reference: https://github.com/PolicyEngine/policyengine-us
- Example Notebooks: https://github.com/PolicyEngine/analysis-notebooks
- Variable Explorer: https://policyengine.org/us/variables
Examples Directory
See examples/ for complete working examples:
single_filer.yaml- Single person householdmarried_couple.yaml- Married filing jointlyfamily_with_children.yaml- Family with dependentsitemized_deductions.yaml- Using itemized deductionsdonation_sweep.yaml- Analyzing donation impacts with axes