Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/bump_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ def get_current_version(pyproject_path: Path) -> str:

def infer_bump(changelog_dir: Path) -> str:
fragments = [
f
for f in changelog_dir.iterdir()
if f.is_file() and f.name != ".gitkeep"
f for f in changelog_dir.iterdir() if f.is_file() and f.name != ".gitkeep"
]
if not fragments:
print("No changelog fragments found", file=sys.stderr)
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/code_changes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
- name: Install ruff
run: pip install ruff

- name: Run ruff format check
run: ruff format --check .

- name: Run ruff check
run: ruff check .
Test:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/pr_code_changes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
- name: Install ruff
run: pip install ruff

- name: Run ruff format check
run: ruff format --check .

- name: Run ruff check
run: ruff check .
Test:
Expand Down
1 change: 1 addition & 0 deletions changelog.d/switch-to-ruff.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Switched code formatter from black to ruff format.
25 changes: 6 additions & 19 deletions examples/employment_income_variation_uk.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,12 @@ def create_dataset_with_varied_employment_income(
"region": ["LONDON"] * n_households, # Required by policyengine-uk
"council_tax": [0.0] * n_households, # Simplified - no council tax
"rent": [median_annual_rent] * n_households, # Median UK rent
"tenure_type": ["RENT_PRIVATELY"]
* n_households, # Required for uprating
"tenure_type": ["RENT_PRIVATELY"] * n_households, # Required for uprating
}

# Create MicroDataFrames
person_df = MicroDataFrame(
pd.DataFrame(person_data), weights="person_weight"
)
benunit_df = MicroDataFrame(
pd.DataFrame(benunit_data), weights="benunit_weight"
)
person_df = MicroDataFrame(pd.DataFrame(person_data), weights="person_weight")
benunit_df = MicroDataFrame(pd.DataFrame(benunit_data), weights="benunit_weight")
household_df = MicroDataFrame(
pd.DataFrame(household_data), weights="household_weight"
)
Expand Down Expand Up @@ -273,9 +268,7 @@ def visualise_results(results: dict) -> None:
# Calculate net employment income (employment income minus tax)
net_employment = [
emp - tax
for emp, tax in zip(
results["employment_income_hh"], results["household_tax"]
)
for emp, tax in zip(results["employment_income_hh"], results["household_tax"])
]

# Stack benefits and income components using PolicyEngine colors
Expand Down Expand Up @@ -339,17 +332,11 @@ def main():
simulation = run_simulation(dataset)

print("Extracting results using aggregate filters...")
results = extract_results_by_employment_income(
simulation, employment_incomes
)
results = extract_results_by_employment_income(simulation, employment_incomes)

print("\nSample results:")
for emp_inc in [0, 25000, 50000, 100000]:
idx = (
employment_incomes.index(emp_inc)
if emp_inc in employment_incomes
else -1
)
idx = employment_incomes.index(emp_inc) if emp_inc in employment_incomes else -1
if idx >= 0:
print(
f" Employment income £{emp_inc:,}: HBAI net income £{results['hbai_household_net_income'][idx]:,.0f}"
Expand Down
30 changes: 7 additions & 23 deletions examples/employment_income_variation_us.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,16 @@ def create_dataset_with_varied_employment_income(
}

# Create MicroDataFrames
person_df = MicroDataFrame(
pd.DataFrame(person_data), weights="person_weight"
)
person_df = MicroDataFrame(pd.DataFrame(person_data), weights="person_weight")
household_df = MicroDataFrame(
pd.DataFrame(household_data), weights="household_weight"
)
marital_unit_df = MicroDataFrame(
pd.DataFrame(marital_unit_data), weights="marital_unit_weight"
)
family_df = MicroDataFrame(
pd.DataFrame(family_data), weights="family_weight"
)
spm_unit_df = MicroDataFrame(
pd.DataFrame(spm_unit_data), weights="spm_unit_weight"
)
tax_unit_df = MicroDataFrame(
pd.DataFrame(tax_unit_data), weights="tax_unit_weight"
)
family_df = MicroDataFrame(pd.DataFrame(family_data), weights="family_weight")
spm_unit_df = MicroDataFrame(pd.DataFrame(spm_unit_data), weights="spm_unit_weight")
tax_unit_df = MicroDataFrame(pd.DataFrame(tax_unit_data), weights="tax_unit_weight")

# Create temporary file
tmpdir = tempfile.mkdtemp()
Expand Down Expand Up @@ -227,9 +219,7 @@ def visualise_results(results: dict) -> None:
# Calculate net employment income (employment income minus tax)
net_employment = [
emp - tax
for emp, tax in zip(
results["employment_income_hh"], results["household_tax"]
)
for emp, tax in zip(results["employment_income_hh"], results["household_tax"])
]

# Stack benefits and income components using PolicyEngine colors
Expand Down Expand Up @@ -287,17 +277,11 @@ def main():
simulation = run_simulation(dataset)

print("Extracting results using aggregate filters...")
results = extract_results_by_employment_income(
simulation, employment_incomes
)
results = extract_results_by_employment_income(simulation, employment_incomes)

print("\nSample results:")
for emp_inc in [0, 50000, 100000, 200000]:
idx = (
employment_incomes.index(emp_inc)
if emp_inc in employment_incomes
else -1
)
idx = employment_incomes.index(emp_inc) if emp_inc in employment_incomes else -1
if idx >= 0:
print(
f" Employment income ${emp_inc:,}: household net income ${results['household_net_income'][idx]:,.0f}"
Expand Down
16 changes: 4 additions & 12 deletions examples/household_impact_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,9 @@ def uk_example():
result = calculate_uk_impact(household)

print("\nSingle adult, £50k income:")
print(
f" Net income: £{result.household['hbai_household_net_income']:,.0f}"
)
print(f" Net income: £{result.household['hbai_household_net_income']:,.0f}")
print(f" Income tax: £{result.person[0]['income_tax']:,.0f}")
print(
f" National Insurance: £{result.person[0]['national_insurance']:,.0f}"
)
print(f" National Insurance: £{result.person[0]['national_insurance']:,.0f}")
print(f" Total tax: £{result.household['household_tax']:,.0f}")

# Family with two children, £30k income, renting
Expand All @@ -64,9 +60,7 @@ def uk_example():
result = calculate_uk_impact(household)

print("\nFamily (2 adults, 2 children), £30k income, renting:")
print(
f" Net income: £{result.household['hbai_household_net_income']:,.0f}"
)
print(f" Net income: £{result.household['hbai_household_net_income']:,.0f}")
print(f" Income tax: £{result.person[0]['income_tax']:,.0f}")
print(f" Child benefit: £{result.benunit[0]['child_benefit']:,.0f}")
print(f" Universal credit: £{result.benunit[0]['universal_credit']:,.0f}")
Expand All @@ -81,9 +75,7 @@ def us_example():

# Single adult earning $50,000
household = USHouseholdInput(
people=[
{"age": 35, "employment_income": 50_000, "is_tax_unit_head": True}
],
people=[{"age": 35, "employment_income": 50_000, "is_tax_unit_head": True}],
tax_unit={"filing_status": "SINGLE"},
household={"state_code_str": "CA"},
year=2024,
Expand Down
20 changes: 5 additions & 15 deletions examples/income_distribution_us.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@

def load_representative_data(year: int = 2024) -> PolicyEngineUSDataset:
"""Load representative household microdata for a given year."""
dataset_path = (
Path(__file__).parent / "data" / f"enhanced_cps_2024_year_{year}.h5"
)
dataset_path = Path(__file__).parent / "data" / f"enhanced_cps_2024_year_{year}.h5"

if not dataset_path.exists():
raise FileNotFoundError(
Expand Down Expand Up @@ -83,15 +81,11 @@ def calculate_income_decile_statistics(simulation: Simulation) -> dict:
quantile_eq=decile_num,
)
if decile_num == 1:
print(
f" First Aggregate created ({time.time() - pre_create:.2f}s)"
)
print(f" First Aggregate created ({time.time() - pre_create:.2f}s)")
pre_run = time.time()
agg.run()
if decile_num == 1:
print(
f" First Aggregate.run() complete ({time.time() - pre_run:.2f}s)"
)
print(f" First Aggregate.run() complete ({time.time() - pre_run:.2f}s)")
market_incomes.append(agg.result / 1e9)

agg = Aggregate(
Expand Down Expand Up @@ -234,9 +228,7 @@ def calculate_income_decile_statistics(simulation: Simulation) -> dict:
print(f" {prog} complete ({time.time() - prog_start:.2f}s)")

print(f"Tax benefits complete ({time.time() - tax_benefits_start:.2f}s)")
print(
f"\nTotal statistics calculation time: {time.time() - start_time:.2f}s"
)
print(f"\nTotal statistics calculation time: {time.time() - start_time:.2f}s")

return {
"deciles": deciles,
Expand Down Expand Up @@ -392,9 +384,7 @@ def main():
print(f"Total benefits: ${total_benefits:.1f}bn")
print(f"Total net income: ${total_net_income:.1f}bn")
print(f"Total households: {total_households:.1f}m")
print(
f"Average effective tax rate: {total_tax / total_market_income * 100:.1f}%"
)
print(f"Average effective tax rate: {total_tax / total_market_income * 100:.1f}%")

print("\nBenefit programs by decile:")
benefit_programs = [
Expand Down
16 changes: 4 additions & 12 deletions examples/policy_change_uk.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ def run_baseline_simulation(dataset: PolicyEngineUKDataset) -> Simulation:
return simulation


def run_reform_simulation(
dataset: PolicyEngineUKDataset, policy: Policy
) -> Simulation:
def run_reform_simulation(dataset: PolicyEngineUKDataset, policy: Policy) -> Simulation:
"""Run reform microsimulation with policy changes."""
simulation = Simulation(
dataset=dataset,
Expand All @@ -95,9 +93,7 @@ def run_reform_simulation(
return simulation


def analyse_overall_impact(
baseline_sim: Simulation, reform_sim: Simulation
) -> dict:
def analyse_overall_impact(baseline_sim: Simulation, reform_sim: Simulation) -> dict:
"""Analyse overall winners, losers, and financial impact."""
winners = ChangeAggregate(
baseline_simulation=baseline_sim,
Expand Down Expand Up @@ -198,9 +194,7 @@ def analyse_impact_by_income_decile(
}


def visualise_results(
overall: dict, by_decile: dict, reform_name: str
) -> None:
def visualise_results(overall: dict, by_decile: dict, reform_name: str) -> None:
"""Create visualisations of policy change impacts."""
fig = make_subplots(
rows=1,
Expand Down Expand Up @@ -270,9 +264,7 @@ def print_summary(overall: dict, decile: dict, reform_name: str) -> None:
print(f" Losers: {overall['losers']:.2f}m households")
print(f" No change: {overall['no_change']:.2f}m households")
print("\nFinancial impact:")
print(
f" Net income change: £{overall['total_change']:.2f}bn (negative = loss)"
)
print(f" Net income change: £{overall['total_change']:.2f}bn (negative = loss)")
print(f" Tax revenue change: £{overall['tax_revenue_change']:.2f}bn")
print("\nImpact by income decile:")
for i, label in enumerate(decile["labels"]):
Expand Down
Loading