Skip to main content

A package to conduct policy analysis using PolicyEngine tax-benefit models.

Project description

PolicyEngine.py

A Python package for tax-benefit microsimulation analysis. Run policy simulations, analyse distributional impacts, and visualise results across the UK and US.

Results are estimates. PolicyEngine simulates a large, evolving body of tax-benefit law (the US model alone encodes more than 95,000 parameters across 5,500+ variables) over survey microdata calibrated to administrative targets. Treat outputs as estimates, and validate them against the policies relevant to your analysis, the scope of the rules engine, and external or back-of-the-envelope calculations. You can inspect the certified US dataset's calibration at https://calibration-diagnostics.vercel.app/populace.

Quick start

Household calculator

import policyengine as pe

# UK: single adult earning £50,000
uk = pe.uk.calculate_household(
    people=[{"age": 35, "employment_income": 50_000}],
    year=2026,
)
print(uk.person[0].income_tax)                   # income tax
print(uk.household.hbai_household_net_income)    # net income

# US: single filer in California, with a reform
us = pe.us.calculate_household(
    people=[{"age": 35, "employment_income": 60_000}],
    tax_unit={"filing_status": "SINGLE"},
    household={"state_code": "CA"},
    year=2026,
    reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000},
)
print(us.tax_unit.income_tax, us.household.household_net_income)

Population analysis

import policyengine as pe
from policyengine.core import Simulation
from policyengine.outputs.aggregate import Aggregate, AggregateType

datasets = pe.uk.ensure_datasets(
    datasets=["enhanced_frs_2023_24"],
    years=[2026],
    data_folder="./data",
)
dataset = datasets["enhanced_frs_2023_24_2026"]

simulation = Simulation(dataset=dataset, tax_benefit_model_version=pe.uk.model)
simulation.run()

agg = Aggregate(
    simulation=simulation,
    variable="universal_credit",
    aggregate_type=AggregateType.SUM,
    entity="benunit",
)
agg.run()
print(f"Total UC spending: £{agg.result / 1e9:.1f}bn")

For baseline-vs-reform comparisons, see pe.uk.economic_impact_analysis and its US counterpart.

UK population data is stored in a private Hugging Face model repository. Set HUGGING_FACE_TOKEN to a token from an account with access before running UK population examples. To download the raw .h5 file directly, see Microsimulation.

Documentation

Core concepts:

Examples:

  • examples/income_distribution_us.py: Analyse benefit distribution by decile
  • examples/employment_income_variation_uk.py: Model employment income phase-outs
  • examples/policy_change_uk.py: Analyse policy reform impacts
  • examples/paper_repro_uk.py: Reproduce the UK reform analysis used in the JOSS paper draft

Installation

As a library

pip install policyengine

This installs both UK and US country models. To install only one:

pip install policyengine[uk]    # UK model only
pip install policyengine[us]    # US model only

For a certified package-plus-dataset bundle, use the bundle installer as the single setup command:

uvx --from policyengine policyengine bundle install

This installs the bundled package scaffold with pip, downloads the certified US and UK datasets into ./data, and writes a local receipt that can be checked with policyengine bundle status. When run from uvx or pipx, the installer creates or reuses ./.venv; inside an existing virtualenv or conda environment, it installs into the active environment.

For development

git clone https://github.com/PolicyEngine/policyengine.py.git
cd policyengine.py
uv pip install -e .[dev]        # install with dev dependencies (pytest, ruff, mypy, etc.)

Development

Running configurations

Configuration Install Use case
Library user pip install policyengine Using the package in your own code
UK only pip install policyengine[uk] Only need UK simulations
US only pip install policyengine[us] Only need US simulations
Certified bundle uvx --from policyengine policyengine bundle install Reproducible model-plus-data setup
Developer uv pip install -e .[dev] Contributing to the package

Common commands

make format           # ruff format
make test             # pytest with coverage
make docs             # build static Quarto HTML docs
make docs-serve       # preview the docs locally
make clean            # remove caches, build artifacts, .h5 files

Testing

Tests require a HUGGING_FACE_TOKEN environment variable for downloading datasets:

export HUGGING_FACE_TOKEN=hf_...
make test

To run a specific test:

pytest tests/test_models.py -v
pytest tests/test_parametric_reforms.py -k "test_uk" -v

Linting and type checking

ruff format .                    # format code
ruff check .                     # lint
mypy src/policyengine            # type check (informational — not yet enforced in CI)

CI pipeline

PRs trigger the following checks:

Check Status Command
Lint + format Required ruff check . + ruff format --check .
Tests (Python 3.13) Required make test
Tests (Python 3.14) Required make test
Mypy Informational mypy src/policyengine
Docs build Required Jupyter Book build

Versioning and releases

This project uses towncrier for changelog management. When making a PR, add a changelog fragment:

# Fragment types: breaking, added, changed, fixed, removed
echo "Description of change" > changelog.d/my-change.added

On merge, the versioning workflow bumps the version, builds the changelog, and creates a GitHub Release.

Paper reproduction

Use the pinned interpreter and the UK extra to run the checked-in paper repro:

uv run --python 3.14 --extra uk python examples/paper_repro_uk.py

On first run this will create ./data/enhanced_frs_2023_24_year_2026.h5.

Features

  • Multi-country support: UK and US tax-benefit systems
  • Representative microdata: Load FRS, CPS, or create custom scenarios
  • Policy reforms: Parametric reforms with date-bound parameter values
  • Distributional analysis: Aggregate statistics by income decile, demographics
  • Entity mapping: Automatic mapping between person, household, tax unit levels
  • Visualisation: PolicyEngine-branded charts with Plotly

Key concepts

Datasets

Datasets contain microdata at entity level (person, household, tax unit). Load representative data or create custom scenarios:

from policyengine.tax_benefit_models.uk import PolicyEngineUKDataset

dataset = PolicyEngineUKDataset(
    name="Representative data",
    filepath="./data/frs_2023_24_year_2026.h5",
    year=2026,
)
dataset.load()

Simulations

Simulations apply tax-benefit models to datasets:

import policyengine as pe
from policyengine.core import Simulation

simulation = Simulation(
    dataset=dataset,
    tax_benefit_model_version=pe.uk.model,
)
simulation.run()

# Access calculated variables
output = simulation.output_dataset.data
print(output.household[["household_net_income", "household_benefits"]])

Outputs

Extract insights with aggregate statistics:

from policyengine.outputs.aggregate import Aggregate, AggregateType

# Mean income in top decile
agg = Aggregate(
    simulation=simulation,
    variable="household_net_income",
    aggregate_type=AggregateType.MEAN,
    filter_variable="household_net_income",
    quantile=10,
    quantile_eq=10,
)
agg.run()
print(f"Top decile mean income: £{agg.result:,.0f}")

Policy reforms

Apply parametric reforms:

from policyengine.core import Policy, Parameter, ParameterValue
import datetime

parameter = Parameter(
    name="gov.hmrc.income_tax.allowances.personal_allowance.amount",
    tax_benefit_model_version=pe.uk.model,
    data_type=float,
)

policy = Policy(
    name="Increase personal allowance",
    parameter_values=[
        ParameterValue(
            parameter=parameter,
            start_date=datetime.date(2026, 1, 1),
            end_date=datetime.date(2026, 12, 31),
            value=15000,
        )
    ],
)

# Run reform simulation
reform_sim = Simulation(
    dataset=dataset,
    tax_benefit_model_version=pe.uk.model,
    policy=policy,
)
reform_sim.run()

Country models

UK

Three entity levels:

  • Person: Individual with income and demographics
  • Benunit: Benefit unit (single person or couple with children)
  • Household: Residence unit

Key benefits: Universal Credit, Child Benefit, Pension Credit Key taxes: Income tax, National Insurance

US

Six entity levels:

  • Person: Individual
  • Tax unit: Federal tax filing unit
  • SPM unit: Supplemental Poverty Measure unit
  • Family: Census family definition
  • Marital unit: Married couple or single person
  • Household: Residence unit

Key benefits: SNAP, TANF, EITC, CTC, SSI, Social Security Key taxes: Federal income tax, payroll tax

Contributing

See CONTRIBUTING.md for development setup and guidelines.

License

AGPL-3.0

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

policyengine-4.18.9.tar.gz (699.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

policyengine-4.18.9-py3-none-any.whl (210.1 kB view details)

Uploaded Python 3

File details

Details for the file policyengine-4.18.9.tar.gz.

File metadata

  • Download URL: policyengine-4.18.9.tar.gz
  • Upload date:
  • Size: 699.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for policyengine-4.18.9.tar.gz
Algorithm Hash digest
SHA256 4f7a58d9e88256076b474debda02c61019011215196c7f8b8265560486aa4ec7
MD5 069afc367704aea7eb8715cf1eeeac20
BLAKE2b-256 5cf0452c855cdb162e19dfdeb2f04939b763ab98e4cf07ce2caf7a4c619ba34f

See more details on using hashes here.

File details

Details for the file policyengine-4.18.9-py3-none-any.whl.

File metadata

  • Download URL: policyengine-4.18.9-py3-none-any.whl
  • Upload date:
  • Size: 210.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for policyengine-4.18.9-py3-none-any.whl
Algorithm Hash digest
SHA256 fda8d3069151fcfe3e5bb2ac6fc2402b1d0de9600f296086c9a254406bb16f05
MD5 8662fba86860a9b11f7388d6d87ebcaf
BLAKE2b-256 d415908db27608da1981b4b45992eb58ee616eeefd70abe5dfd62a0511edec6d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page