From ddd354f2687af4cb29bc4250c94a45802708f886 Mon Sep 17 00:00:00 2001 From: Anthony Volk <14987227+anth-volk@users.noreply.github.com> Date: Thu, 15 Oct 2026 01:02:50 +0400 Subject: [PATCH] Remove the unused economy comparison module policyengine_api/services/economy_comparison.py has had no caller in the package since #2569 removed the Public API's own society-wide simulations; only its tests and snapshot data used it. Remove the module, its unit and snapshot tests, and the route-boundary test that checked it stayed out of the HTTP layer. api.routes.no-domain-logic-in-http now covers the calculation route alone, and a docstring that pointed at the module now points at the codebase's other Pydantic models. Nothing in the package imports microdf any more, but microdf_python stays declared; removing an unused dependency is a separate cleanup. Co-Authored-By: Claude Opus 5.5 --- .../remove-economy-comparison.removed.md | 1 + docs/generated/rules.md | 2 +- .../data/congressional_districts.py | 2 +- .../services/economy_comparison.py | 915 -- rules/registry.toml | 3 +- tests/data/economy_comparison_snapshots.json | 8085 ----------------- .../routes/test_flask_route_boundaries.py | 9 - .../unit/services/test_economy_comparison.py | 1003 -- .../test_economy_comparison_snapshots.py | 567 -- 9 files changed, 4 insertions(+), 10583 deletions(-) create mode 100644 changelog.d/remove-economy-comparison.removed.md delete mode 100644 policyengine_api/services/economy_comparison.py delete mode 100644 tests/data/economy_comparison_snapshots.json delete mode 100644 tests/unit/services/test_economy_comparison.py delete mode 100644 tests/unit/services/test_economy_comparison_snapshots.py diff --git a/changelog.d/remove-economy-comparison.removed.md b/changelog.d/remove-economy-comparison.removed.md new file mode 100644 index 000000000..591c392a9 --- /dev/null +++ b/changelog.d/remove-economy-comparison.removed.md @@ -0,0 +1 @@ +Removed the unused economy comparison module (`policyengine_api/services/economy_comparison.py`) with its tests and snapshot data. Nothing has called it since the Public API stopped falling back to its own society-wide simulations (#2569); the route-boundary rule now covers the calculation route alone. diff --git a/docs/generated/rules.md b/docs/generated/rules.md index d6609417d..227baa816 100644 --- a/docs/generated/rules.md +++ b/docs/generated/rules.md @@ -123,7 +123,7 @@ flowchart LR | `api.data.runtime-sql-confined` | Raw SQL is confined to the data-access layer; runtime modules and report orchestration use sessions and mapped models. | mechanical | enforced | yes | pytest `tests/unit/data/test_runtime_sql_boundaries.py` | `pr.yml:test` | **unproven** | | `api.legacy.removed-layers-stay-removed` | Removed legacy persistence layers, the temporary DAO module and the legacy endpoints package stay removed. | mechanical | enforced | yes | pytest `tests/unit/data/test_runtime_sql_boundaries.py`, `tests/unit/routes/test_flask_route_boundaries.py`, `tests/unit/services/test_orm_service_boundaries.py` | `pr.yml:test` | **unproven** | | `api.routes.flask-blueprints-own-urls` | Legacy Flask URLs are owned by blueprints, and the Flask app assembles blueprints without legacy endpoint wiring. | mechanical | enforced | yes | pytest `tests/unit/routes/test_flask_route_boundaries.py` | `pr.yml:test` | **unproven** | -| `api.routes.no-domain-logic-in-http` | Flask routes delegate domain processing (economy comparison, calculation) to services instead of doing it in the HTTP layer. | mechanical | enforced | yes | pytest `tests/unit/routes/test_flask_route_boundaries.py` | `pr.yml:test` | **unproven** | +| `api.routes.no-domain-logic-in-http` | Flask routes delegate domain processing, such as calculation, to services instead of doing it in the HTTP layer. | mechanical | enforced | yes | pytest `tests/unit/routes/test_flask_route_boundaries.py` | `pr.yml:test` | **unproven** | | `api.routes.no-inline-json-errors` | Flask routes never build JSON error responses inline; they use the shared error handling. | mechanical | enforced | yes | pytest `tests/unit/routes/test_flask_route_boundaries.py` | `pr.yml:test` | **unproven** | | `api.services.no-direct-queries` | Services never issue database queries or open database connections directly; they delegate persistence to the data-access layer. | mechanical | enforced | yes | pytest `tests/unit/services/test_local_data_service_boundaries.py`, `tests/unit/services/test_orchestration_service_database_boundaries.py`, `tests/unit/services/test_orm_service_boundaries.py`, `tests/unit/services/test_run_and_spec_service_database_boundaries.py` | `pr.yml:test` | **unproven** | | `api.services.session-ownership` | Migrated services use sessions and mapped models; their public methods never accept persistence objects, and routes and the economy service never manage sessions. | mechanical | enforced | yes | pytest `tests/unit/services/test_local_data_service_boundaries.py`, `tests/unit/services/test_orm_service_boundaries.py` | `pr.yml:test` | **unproven** | diff --git a/policyengine_api/data/congressional_districts.py b/policyengine_api/data/congressional_districts.py index 76f0da35d..07693cf23 100644 --- a/policyengine_api/data/congressional_districts.py +++ b/policyengine_api/data/congressional_districts.py @@ -72,7 +72,7 @@ class CongressionalDistrictMetadataItem(BaseModel): Uses Pydantic BaseModel for: - Runtime validation of data integrity - Automatic serialization/deserialization - - Consistency with existing codebase patterns (see services/economy_comparison.py) + - Consistency with the codebase's other Pydantic models - Self-documenting schema with type hints """ diff --git a/policyengine_api/services/economy_comparison.py b/policyengine_api/services/economy_comparison.py deleted file mode 100644 index c3012c783..000000000 --- a/policyengine_api/services/economy_comparison.py +++ /dev/null @@ -1,915 +0,0 @@ -"""Reusable economy comparison calculations and response models.""" - -import logging -from collections.abc import Callable -from typing import Any, NamedTuple - -import h5py -import numpy as np -import pandas as pd -from microdf import MicroSeries -from policyengine_core.tools.hugging_face import download_huggingface_dataset -from pydantic import BaseModel - -logger = logging.getLogger(__name__) - -# Poverty is broken down into children (under 18), working-age adults and -# seniors (65 and over). -ADULT_MIN_AGE = 18 -SENIOR_MIN_AGE = 65 - -# A relative income change above 5% (or below -5%) is large, and one within -# 0.1% of zero counts as no change. -LARGE_INCOME_CHANGE = 0.05 -NO_INCOME_CHANGE_TOLERANCE = 1e-3 - -# Each bucket's label and the relative change an area must exceed to land in -# it, checked in order; an area that exceeds none loses more than 5%. -INCOME_CHANGE_BUCKET_THRESHOLDS = ( - ("Gain more than 5%", LARGE_INCOME_CHANGE), - ("Gain less than 5%", NO_INCOME_CHANGE_TOLERANCE), - ("No change", -NO_INCOME_CHANGE_TOLERANCE), - ("Lose less than 5%", -LARGE_INCOME_CHANGE), -) -LARGEST_LOSS_BUCKET = "Lose more than 5%" -INCOME_CHANGE_BUCKETS = ( - *(label for label, _threshold in INCOME_CHANGE_BUCKET_THRESHOLDS), - LARGEST_LOSS_BUCKET, -) - -# Constituency and local-authority codes start with a letter for their nation. -UK_NATION_CODE_PREFIXES = { - "england": "E", - "scotland": "S", - "wales": "W", - "northern_ireland": "N", -} -UK_OUTCOME_REGIONS = ("uk", *UK_NATION_CODE_PREFIXES) - -# These UK breakdown artifacts are postprocessor inputs, not the certified -# primary simulation dataset resolved through the PolicyEngine bundle. -UK_AREA_NAMES_REPO = "policyengine/policyengine-uk-data-public" - - -def budgetary_impact(baseline: dict, reform: dict) -> dict: - """Return the reform's change in tax revenue, benefit spending and net budget. - - A positive ``budgetary_impact`` means the reform raises net revenue (tax - revenue change minus benefit spending change). - """ - tax_revenue_impact = reform["total_tax"] - baseline["total_tax"] - state_tax_revenue_impact = reform["total_state_tax"] - baseline["total_state_tax"] - benefit_spending_impact = reform["total_benefits"] - baseline["total_benefits"] - budgetary_impact = tax_revenue_impact - benefit_spending_impact - return { - "budgetary_impact": budgetary_impact, - "tax_revenue_impact": tax_revenue_impact, - "state_tax_revenue_impact": state_tax_revenue_impact, - "benefit_spending_impact": benefit_spending_impact, - "households": sum(baseline["household_weight"]), - "baseline_net_income": baseline["total_net_income"], - } - - -def labor_supply_response(baseline: dict, reform: dict) -> dict: - """Return the reform's labour supply responses relative to the baseline. - - This covers the income and substitution effects in total, as shares of - original earnings, by income decile, and in weekly hours. - """ - substitution_lsr = reform["substitution_lsr"] - baseline["substitution_lsr"] - income_lsr = reform["income_lsr"] - baseline["income_lsr"] - total_change = substitution_lsr + income_lsr - revenue_change = reform["budgetary_impact_lsr"] - baseline["budgetary_impact_lsr"] - - substitution_lsr_hh = np.array(reform["substitution_lsr_hh"]) - np.array( - baseline["substitution_lsr_hh"] - ) - income_lsr_hh = np.array(reform["income_lsr_hh"]) - np.array( - baseline["income_lsr_hh"] - ) - decile = np.array(baseline["household_income_decile"]) - household_weight = baseline["household_weight"] - - total_lsr_hh = substitution_lsr_hh + income_lsr_hh - - emp_income = MicroSeries(baseline["employment_income_hh"], weights=household_weight) - self_emp_income = MicroSeries( - baseline["self_employment_income_hh"], weights=household_weight - ) - earnings = emp_income + self_emp_income - original_earnings = earnings - total_lsr_hh - substitution_lsr_hh = MicroSeries(substitution_lsr_hh, weights=household_weight) - income_lsr_hh = MicroSeries(income_lsr_hh, weights=household_weight) - - decile_avg = { - "income": income_lsr_hh.groupby(decile).mean().to_dict(), - "substitution": substitution_lsr_hh.groupby(decile).mean().to_dict(), - } - decile_rel = { - "income": ( - income_lsr_hh.groupby(decile).sum() - / original_earnings.groupby(decile).sum() - ).to_dict(), - "substitution": ( - substitution_lsr_hh.groupby(decile).sum() - / original_earnings.groupby(decile).sum() - ).to_dict(), - } - - relative_lsr = { - "income": (income_lsr_hh.sum() / original_earnings.sum()), - "substitution": (substitution_lsr_hh.sum() / original_earnings.sum()), - } - - decile_rel["income"] = {int(k): v for k, v in decile_rel["income"].items() if k > 0} - decile_rel["substitution"] = { - int(k): v for k, v in decile_rel["substitution"].items() if k > 0 - } - - hours = { - "baseline": baseline["weekly_hours"], - "reform": reform["weekly_hours"], - "change": reform["weekly_hours"] - baseline["weekly_hours"], - "income_effect": reform["weekly_hours_income_effect"] - - baseline["weekly_hours_income_effect"], - "substitution_effect": reform["weekly_hours_substitution_effect"] - - baseline["weekly_hours_substitution_effect"], - } - - return { - "substitution_lsr": substitution_lsr, - "income_lsr": income_lsr, - "relative_lsr": relative_lsr, - "total_change": total_change, - "revenue_change": revenue_change, - "decile": { - "average": decile_avg, - "relative": decile_rel, - }, - "hours": hours, - } - - -def detailed_budgetary_impact( - baseline: dict, reform: dict, country_id: str | None -) -> dict: - """Return baseline, reform and difference for each UK programme's budget. - - Only the UK reports programme-level totals; other countries get an empty dict. - """ - result = {} - if country_id == "uk": - for program in baseline["programs"]: - # baseline[programs][program] = total budgetary impact of program - result[program] = { - "baseline": baseline["programs"][program], - "reform": reform["programs"][program], - "difference": reform["programs"][program] - - baseline["programs"][program], - } - return result - - -def decile_impact(baseline: dict, reform: dict) -> dict: - """Compare the impact of a reform on the deciles of the population. - - Args: - baseline (dict): The baseline economy. - reform (dict): The reform economy. - - Returns: - dict: The impact of the reform on the deciles of the population. - - """ - baseline_income = MicroSeries( - baseline["household_net_income"], weights=baseline["household_weight"] - ) - reform_income = MicroSeries( - reform["household_net_income"], weights=baseline_income.weights - ) - - # Filter out negative decile values - decile = MicroSeries(baseline["household_income_decile"]) - baseline_income_filtered = baseline_income[decile >= 0] - reform_income_filtered = reform_income[decile >= 0] - - income_change = reform_income_filtered - baseline_income_filtered - rel_income_change_by_decile = ( - income_change.groupby(decile).sum() - / baseline_income_filtered.groupby(decile).sum() - ) - - avg_income_change_by_decile = ( - income_change.groupby(decile).sum() - / baseline_income_filtered.groupby(decile).count() - ) - rel_decile_dict = rel_income_change_by_decile.to_dict() - avg_decile_dict = avg_income_change_by_decile.to_dict() - return { - "relative": {int(k): v for k, v in rel_decile_dict.items()}, - "average": {int(k): v for k, v in avg_decile_dict.items()}, - } - - -def wealth_decile_impact(baseline: dict, reform: dict) -> dict: - """Compare the impact of a reform on the deciles of the population. - - Args: - baseline (dict): The baseline economy. - reform (dict): The reform economy. - - Returns: - dict: The impact of the reform on the deciles of the population. - - """ - baseline_income = MicroSeries( - baseline["household_net_income"], weights=baseline["household_weight"] - ) - reform_income = MicroSeries( - reform["household_net_income"], weights=baseline_income.weights - ) - - # Filter out negative decile values - decile = MicroSeries(baseline["household_wealth_decile"]) - baseline_income_filtered = baseline_income[decile >= 0] - reform_income_filtered = reform_income[decile >= 0] - - income_change = reform_income_filtered - baseline_income_filtered - rel_income_change_by_decile = ( - income_change.groupby(decile).sum() - / baseline_income_filtered.groupby(decile).sum() - ) - avg_income_change_by_decile = ( - income_change.groupby(decile).sum() - / baseline_income_filtered.groupby(decile).count() - ) - rel_decile_dict = rel_income_change_by_decile.to_dict() - avg_decile_dict = avg_income_change_by_decile.to_dict() - return { - "relative": {int(k): v for k, v in rel_decile_dict.items()}, - "average": {int(k): v for k, v in avg_decile_dict.items()}, - } - - -def inequality_impact(baseline: dict, reform: dict) -> dict: - """Compare the impact of a reform on inequality. - - Args: - baseline (dict): The baseline economy. - reform (dict): The reform economy. - - Returns: - dict: The impact of the reform on inequality. - - """ - return { - "gini": { - "baseline": baseline["gini"], - "reform": reform["gini"], - }, - "top_10_pct_share": { - "baseline": baseline["top_10_percent_share"], - "reform": reform["top_10_percent_share"], - }, - "top_1_pct_share": { - "baseline": baseline["top_1_percent_share"], - "reform": reform["top_1_percent_share"], - }, - } - - -def poverty_impact(baseline: dict, reform: dict) -> dict: - """Compare the impact of a reform on poverty. - - Args: - baseline (dict): The baseline economy. - reform (dict): The reform economy. - - Returns: - dict: The impact of the reform on poverty. - - """ - baseline_poverty = MicroSeries( - baseline["person_in_poverty"], weights=baseline["person_weight"] - ) - baseline_deep_poverty = MicroSeries( - baseline["person_in_deep_poverty"], weights=baseline["person_weight"] - ) - reform_poverty = MicroSeries( - reform["person_in_poverty"], weights=baseline_poverty.weights - ) - reform_deep_poverty = MicroSeries( - reform["person_in_deep_poverty"], weights=baseline_poverty.weights - ) - age = MicroSeries(baseline["age"]) - - is_child = age < ADULT_MIN_AGE - is_adult = (age >= ADULT_MIN_AGE) & (age < SENIOR_MIN_AGE) - is_senior = age >= SENIOR_MIN_AGE - - poverty = { - "child": { - "baseline": float(baseline_poverty[is_child].mean()), - "reform": float(reform_poverty[is_child].mean()), - }, - "adult": { - "baseline": float(baseline_poverty[is_adult].mean()), - "reform": float(reform_poverty[is_adult].mean()), - }, - "senior": { - "baseline": float(baseline_poverty[is_senior].mean()), - "reform": float(reform_poverty[is_senior].mean()), - }, - "all": { - "baseline": float(baseline_poverty.mean()), - "reform": float(reform_poverty.mean()), - }, - } - - deep_poverty = { - "child": { - "baseline": float(baseline_deep_poverty[is_child].mean()), - "reform": float(reform_deep_poverty[is_child].mean()), - }, - "adult": { - "baseline": float(baseline_deep_poverty[is_adult].mean()), - "reform": float(reform_deep_poverty[is_adult].mean()), - }, - "senior": { - "baseline": float(baseline_deep_poverty[is_senior].mean()), - "reform": float(reform_deep_poverty[is_senior].mean()), - }, - "all": { - "baseline": float(baseline_deep_poverty.mean()), - "reform": float(reform_deep_poverty.mean()), - }, - } - - return { - "poverty": poverty, - "deep_poverty": deep_poverty, - } - - -def compute_income_change( - baseline_values: np.ndarray, reform_values: np.ndarray -) -> np.ndarray: - """Return each household's income change as a fraction of baseline income. - - The baseline is floored at 1 so zero or negative incomes do not divide by zero. - """ - absolute_change = reform_values - baseline_values - capped_baseline = np.maximum(baseline_values, 1) - return absolute_change / capped_baseline - - -def intra_decile_impact(baseline: dict, reform: dict) -> dict: - """Return the share of people in each income decile by size of income change. - - People fall into five buckets (gain or lose more or less than 5%, or no change - within 0.1%). ``all`` is the unweighted mean of the ten decile shares. - """ - baseline_income = MicroSeries( - baseline["household_net_income"], weights=baseline["household_weight"] - ) - reform_income = MicroSeries( - reform["household_net_income"], weights=baseline_income.weights - ) - people = MicroSeries( - baseline["household_count_people"], weights=baseline_income.weights - ) - decile = MicroSeries(baseline["household_income_decile"]).values # noqa: PD011 # MicroSeries overrides .values, and .to_numpy() emits a different warning - income_change = compute_income_change(baseline_income.values, reform_income.values) - - # Within each decile, calculate the percentage of people who: - # 1. Gained more than 5% of their income - # 2. Gained between 0% and 5% of their income - # 3. Had no change in income - # 3. Lost between 0% and 5% of their income - # 4. Lost more than 5% of their income - - outcome_groups = {} - all_outcomes = {} - income_change_bounds = [-np.inf, -0.05, -1e-3, 1e-3, 0.05, np.inf] - outcome_labels = [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%", - ] - for lower, upper, label in zip( - income_change_bounds[:-1], income_change_bounds[1:], outcome_labels, strict=True - ): - outcome_groups[label] = [] - for i in range(1, 11): - in_decile: np.ndarray = decile == i - in_group: np.ndarray = (income_change > lower) & (income_change <= upper) - in_both: np.ndarray = in_decile & in_group - - people_in_both: np.float64 = people[in_both].sum() - people_in_decile: np.float64 = people[in_decile].sum() - - # np.float64 does not raise ZeroDivisionError, instead returns NaN - if people_in_decile == 0 and people_in_both == 0: - people_in_proportion: float = 0.0 - else: - people_in_proportion: float = float(people_in_both / people_in_decile) - - outcome_groups[label].append(people_in_proportion) - - all_outcomes[label] = sum(outcome_groups[label]) / 10 - return {"deciles": outcome_groups, "all": all_outcomes} - - -def intra_wealth_decile_impact(baseline: dict, reform: dict) -> dict: - """Return the share of people in each wealth decile by size of income change. - - This uses the same buckets as ``intra_decile_impact``, grouped by household - wealth decile instead of income decile. - """ - baseline_income = MicroSeries( - baseline["household_net_income"], weights=baseline["household_weight"] - ) - reform_income = MicroSeries( - reform["household_net_income"], weights=baseline_income.weights - ) - people = MicroSeries( - baseline["household_count_people"], weights=baseline_income.weights - ) - decile = MicroSeries(baseline["household_wealth_decile"]).values # noqa: PD011 # MicroSeries overrides .values, and .to_numpy() emits a different warning - income_change = compute_income_change(baseline_income.values, reform_income.values) - - # Within each decile, calculate the percentage of people who: - # 1. Gained more than 5% of their income - # 2. Gained between 0% and 5% of their income - # 3. Had no change in income - # 3. Lost between 0% and 5% of their income - # 4. Lost more than 5% of their income - - outcome_groups = {} - all_outcomes = {} - income_change_bounds = [-np.inf, -0.05, -1e-3, 1e-3, 0.05, np.inf] - outcome_labels = [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%", - ] - for lower, upper, label in zip( - income_change_bounds[:-1], income_change_bounds[1:], outcome_labels, strict=True - ): - outcome_groups[label] = [] - for i in range(1, 11): - in_decile: np.ndarray = decile == i - in_group: np.ndarray = (income_change > lower) & (income_change <= upper) - in_both: np.ndarray = in_decile & in_group - - people_in_both: np.float64 = people[in_both].sum() - people_in_decile: np.float64 = people[in_decile].sum() - - # np.float64 does not raise ZeroDivisionError, instead returns NaN - if people_in_decile == 0 and people_in_both == 0: - people_in_proportion = 0 - else: - people_in_proportion: float = float(people_in_both / people_in_decile) - - outcome_groups[label].append(people_in_proportion) - - all_outcomes[label] = sum(outcome_groups[label]) / 10 - return {"deciles": outcome_groups, "all": all_outcomes} - - -def poverty_gender_breakdown(baseline: dict, reform: dict) -> dict: - """Compare the impact of a reform on poverty. - - Args: - baseline (dict): The baseline economy. - reform (dict): The reform economy. - - Returns: - dict: The impact of the reform on poverty. - - """ - if baseline["is_male"] is None: - return {} - baseline_poverty = MicroSeries( - baseline["person_in_poverty"], weights=baseline["person_weight"] - ) - baseline_deep_poverty = MicroSeries( - baseline["person_in_deep_poverty"], weights=baseline["person_weight"] - ) - reform_poverty = MicroSeries( - reform["person_in_poverty"], weights=baseline_poverty.weights - ) - reform_deep_poverty = MicroSeries( - reform["person_in_deep_poverty"], weights=baseline_poverty.weights - ) - is_male = MicroSeries(baseline["is_male"]) - - poverty = { - "male": { - "baseline": float(baseline_poverty[is_male].mean()), - "reform": float(reform_poverty[is_male].mean()), - }, - "female": { - "baseline": float(baseline_poverty[~is_male].mean()), - "reform": float(reform_poverty[~is_male].mean()), - }, - } - - deep_poverty = { - "male": { - "baseline": float(baseline_deep_poverty[is_male].mean()), - "reform": float(reform_deep_poverty[is_male].mean()), - }, - "female": { - "baseline": float(baseline_deep_poverty[~is_male].mean()), - "reform": float(reform_deep_poverty[~is_male].mean()), - }, - } - - return { - "poverty": poverty, - "deep_poverty": deep_poverty, - } - - -def poverty_racial_breakdown(baseline: dict, reform: dict) -> dict: - """Compare the impact of a reform on poverty. - - Args: - baseline (dict): The baseline economy. - reform (dict): The reform economy. - - Returns: - dict: The impact of the reform on poverty. - - """ - if baseline["race"] is None: - return {} - baseline_poverty = MicroSeries( - baseline["person_in_poverty"], weights=baseline["person_weight"] - ) - reform_poverty = MicroSeries( - reform["person_in_poverty"], weights=baseline_poverty.weights - ) - race = MicroSeries(baseline["race"]) # Can be WHITE, BLACK, HISPANIC, or OTHER. - - poverty = { - "white": { - "baseline": float(baseline_poverty[race == "WHITE"].mean()), - "reform": float(reform_poverty[race == "WHITE"].mean()), - }, - "black": { - "baseline": float(baseline_poverty[race == "BLACK"].mean()), - "reform": float(reform_poverty[race == "BLACK"].mean()), - }, - "hispanic": { - "baseline": float(baseline_poverty[race == "HISPANIC"].mean()), - "reform": float(reform_poverty[race == "HISPANIC"].mean()), - }, - "other": { - "baseline": float(baseline_poverty[race == "OTHER"].mean()), - "reform": float(reform_poverty[race == "OTHER"].mean()), - }, - } - - return { - "poverty": poverty, - } - - -class UKConstituencyBreakdownByConstituency(BaseModel): - """One constituency's household income change and its map position (x, y).""" - - average_household_income_change: float - relative_household_income_change: float - x: int - y: int - - -class UKConstituencyBreakdown(BaseModel): - """UK reform results by constituency. - - ``outcomes_by_region`` counts how many constituencies in the UK and each nation - fall into each income-change bucket. - """ - - by_constituency: dict[str, UKConstituencyBreakdownByConstituency] - outcomes_by_region: dict[str, dict[str, int]] - - -class UKLocalAuthorityBreakdownByLA(BaseModel): - """One local authority's household income change and its map position (x, y).""" - - average_household_income_change: float - relative_household_income_change: float - x: int - y: int - - -class UKLocalAuthorityBreakdown(BaseModel): - """UK reform results by local authority. - - ``outcomes_by_region`` counts how many local authorities in the UK and each - nation fall into each income-change bucket. - """ - - by_local_authority: dict[str, UKLocalAuthorityBreakdownByLA] - outcomes_by_region: dict[str, dict[str, int]] - - -class _UKAreaData(NamedTuple): - """One UK area family's household weights and its area table. - - ``weights[i, j]`` is the weight of household record ``j`` in the area on row - ``i`` of ``names``, whose columns are code, name and map position (x, y). - """ - - weights: np.ndarray - names: pd.DataFrame - - -class _UKAreaFilter(NamedTuple): - """The single area, or the nation's code prefix, a region limits output to.""" - - selected_area: str | None - nation_code_prefix: str | None - - def includes(self, name: str, code: str) -> bool: - """Return whether the area with this name and code passes the filter.""" - if self.selected_area is not None and self.selected_area not in ( - name, - code, - ): - return False - # Match the leading nation letter with startswith, so a letter anywhere - # else in the code cannot place the area in the wrong nation. - return self.nation_code_prefix is None or code.startswith( - self.nation_code_prefix - ) - - -def _uk_area_filter( - region: str | None, - *, - area_region_prefix: str, - normalize_nation: Callable[[str], str], -) -> _UKAreaFilter: - """Read the area or nation a region selects. - - ``normalize_nation`` gives the case in which the region's nation name is - compared with the known nations. - """ - selected_area = None - if region is not None and region.startswith(area_region_prefix): - selected_area = region.split("/", 1)[1] - - nation_code_prefix = None - if region is not None and region.startswith("country/"): - selected_nation = normalize_nation(region.split("/", 1)[1]) - nation_code_prefix = { - normalize_nation(nation): prefix - for nation, prefix in UK_NATION_CODE_PREFIXES.items() - }.get(selected_nation) - return _UKAreaFilter(selected_area, nation_code_prefix) - - -def _load_uk_area_data( - *, weights_repo: str, weights_filename: str, names_filename: str -) -> _UKAreaData: - """Download one area family's 2025 household weights and its area table.""" - weights_path = download_huggingface_dataset( - repo=weights_repo, - repo_filename=weights_filename, - ) - with h5py.File(weights_path, "r") as weights_file: - weights = weights_file["2025"][...] - - names_path = download_huggingface_dataset( - repo=UK_AREA_NAMES_REPO, - repo_filename=names_filename, - ) - return _UKAreaData(weights=weights, names=pd.read_csv(names_path)) - - -def _income_change_bucket(relative_change: float) -> str: - """Return the income-change bucket a relative household income change is in.""" - for label, threshold in INCOME_CHANGE_BUCKET_THRESHOLDS: - if relative_change > threshold: - return label - return LARGEST_LOSS_BUCKET - - -def _outcome_regions(code: str) -> list[str]: - """Return the UK and, when its code names one, the nation an area counts in.""" - regions = ["uk"] - for nation, prefix in UK_NATION_CODE_PREFIXES.items(): - if code.startswith(prefix): - regions.append(nation) - break - return regions - - -def _uk_area_breakdown( - baseline_net_income: object, - reform_net_income: object, - area_data: _UKAreaData, - area_filter: _UKAreaFilter, -) -> tuple[dict[str, Any], dict[str, dict[str, int]]]: - """Return each selected area's income change and the bucket counts by region. - - The first mapping is keyed by area name in table order; the second counts - the selected areas in the UK and each nation by income-change bucket. - """ - by_area: dict[str, Any] = {} - outcomes_by_region = { - region_name: dict.fromkeys(INCOME_CHANGE_BUCKETS, 0) - for region_name in UK_OUTCOME_REGIONS - } - for index in range(len(area_data.names)): - name: str = area_data.names.iloc[index]["name"] - code: str = area_data.names.iloc[index]["code"] - if not area_filter.includes(name, code): - continue - - weight: np.ndarray = area_data.weights[index] - baseline_income = MicroSeries(baseline_net_income, weights=weight) - reform_income = MicroSeries(reform_net_income, weights=weight) - average_household_income_change: float = ( - reform_income.sum() - baseline_income.sum() - ) / baseline_income.count() - percent_household_income_change: float = ( - reform_income.sum() / baseline_income.sum() - 1 - ) - by_area[name] = { - "average_household_income_change": average_household_income_change, - "relative_household_income_change": percent_household_income_change, - "x": int(area_data.names.iloc[index]["x"]), # Geographic positions - "y": int(area_data.names.iloc[index]["y"]), - } - - bucket = _income_change_bucket(percent_household_income_change) - for region_name in _outcome_regions(code): - outcomes_by_region[region_name][bucket] += 1 - return by_area, outcomes_by_region - - -def uk_constituency_breakdown( - baseline: dict, reform: dict, country_id: str | None, region: str | None = None -) -> UKConstituencyBreakdown | None: - """Return each UK constituency's average and relative household income change. - - Return None outside the UK or for a local-authority region. A constituency or - country region limits the output to that area. Constituency weights and - names come from PolicyEngine's Hugging Face datasets. - """ - if country_id != "uk": - return None - - # If simulating a local authority, constituency breakdown is not applicable - if region is not None and region.startswith("local_authority/"): - return None - - area_filter = _uk_area_filter( - region, - area_region_prefix="constituency/", - normalize_nation=str.upper, - ) - baseline_net_income = baseline["household_net_income"] - reform_net_income = reform["household_net_income"] - constituencies = _load_uk_area_data( - weights_repo="policyengine/policyengine-uk-data", - weights_filename="parliamentary_constituency_weights.h5", - names_filename="constituencies_2024.csv", - ) - by_constituency, outcomes_by_region = _uk_area_breakdown( - baseline_net_income, reform_net_income, constituencies, area_filter - ) - return UKConstituencyBreakdown( - by_constituency=by_constituency, - outcomes_by_region=outcomes_by_region, - ) - - -def uk_local_authority_breakdown( - baseline: dict, reform: dict, country_id: str | None, region: str | None = None -) -> UKLocalAuthorityBreakdown | None: - """Return each UK local authority's average and relative household income change. - - Return None outside the UK or for a constituency region. A local-authority or - country region limits the output to that area. Local-authority weights and - names come from PolicyEngine's Hugging Face datasets. - """ - if country_id != "uk": - return None - - # If simulating a constituency, local authority breakdown is not applicable - if region is not None and region.startswith("constituency/"): - return None - - area_filter = _uk_area_filter( - region, - area_region_prefix="local_authority/", - normalize_nation=str.lower, - ) - baseline_net_income = baseline["household_net_income"] - reform_net_income = reform["household_net_income"] - local_authorities = _load_uk_area_data( - weights_repo="policyengine/policyengine-uk-data-private", - weights_filename="local_authority_weights.h5", - names_filename="local_authorities_2021.csv", - ) - by_local_authority, outcomes_by_region = _uk_area_breakdown( - baseline_net_income, reform_net_income, local_authorities, area_filter - ) - return UKLocalAuthorityBreakdown( - by_local_authority=by_local_authority, - outcomes_by_region=outcomes_by_region, - ) - - -def compare_economic_outputs( - baseline: dict, - reform: dict, - country_id: str | None = None, - region: str | None = None, -) -> dict: - """Compare the economic outputs of two economies. - - Args: - baseline (dict): The baseline economy. - reform (dict): The reform economy. - country_id (str): The country identifier (e.g., "uk", "us"). - region (str | None): The region filter (e.g., "uk", "local_authority/Leicester", - "constituency/Aldershot", "country/scotland"). Used to filter breakdown - results. - - Returns: - dict: The comparison of the two economies. - - """ - if baseline.get("type") == "general": - budgetary_impact_data = budgetary_impact(baseline, reform) - detailed_budgetary_impact_data = detailed_budgetary_impact( - baseline, reform, country_id - ) - decile_impact_data = decile_impact(baseline, reform) - inequality_impact_data = inequality_impact(baseline, reform) - poverty_impact_data = poverty_impact(baseline, reform) - poverty_by_gender_data = poverty_gender_breakdown(baseline, reform) - poverty_by_race_data = poverty_racial_breakdown(baseline, reform) - intra_decile_impact_data = intra_decile_impact(baseline, reform) - labor_supply_response_data = labor_supply_response(baseline, reform) - constituency_impact_data: UKConstituencyBreakdown | None = ( - uk_constituency_breakdown(baseline, reform, country_id, region) - ) - if constituency_impact_data is not None: - constituency_impact_data = constituency_impact_data.model_dump() - local_authority_impact_data: UKLocalAuthorityBreakdown | None = ( - uk_local_authority_breakdown(baseline, reform, country_id, region) - ) - if local_authority_impact_data is not None: - local_authority_impact_data = local_authority_impact_data.model_dump() - try: - wealth_decile_impact_data = wealth_decile_impact(baseline, reform) - intra_wealth_decile_impact_data = intra_wealth_decile_impact( - baseline, reform - ) - except Exception: - logger.exception( - "Wealth decile impact computation failed; returning empty breakdowns." - ) - wealth_decile_impact_data = {} - intra_wealth_decile_impact_data = {} - - return { - "budget": budgetary_impact_data, - "detailed_budget": detailed_budgetary_impact_data, - "decile": decile_impact_data, - "inequality": inequality_impact_data, - "poverty": poverty_impact_data, - "poverty_by_gender": poverty_by_gender_data, - "poverty_by_race": poverty_by_race_data, - "intra_decile": intra_decile_impact_data, - "wealth_decile": wealth_decile_impact_data, - "intra_wealth_decile": intra_wealth_decile_impact_data, - "labor_supply_response": labor_supply_response_data, - "constituency_impact": constituency_impact_data, - "local_authority_impact": local_authority_impact_data, - } - if baseline.get("type") == "cliff": - return { - "baseline": { - "cliff_gap": baseline["cliff_gap"], - "cliff_share": baseline["cliff_share"], - }, - "reform": { - "cliff_gap": reform["cliff_gap"], - "cliff_share": reform["cliff_share"], - }, - } - return None diff --git a/rules/registry.toml b/rules/registry.toml index 60936e92e..129099ca8 100644 --- a/rules/registry.toml +++ b/rules/registry.toml @@ -313,7 +313,7 @@ invocation = "make test" [[rule]] id = "api.routes.no-domain-logic-in-http" category = "runtime-layers" -statement = "Flask routes delegate domain processing (economy comparison, calculation) to services instead of doing it in the HTTP layer." +statement = "Flask routes delegate domain processing, such as calculation, to services instead of doing it in the HTTP layer." rationale = "docs/engineering/skills/testing.md#migration-test-layout" scope = ["policyengine_api/routes/**/*.py"] tier = "mechanical" @@ -322,7 +322,6 @@ autofix = false [rule.enforcer] kind = "pytest" nodes = [ - "tests/unit/routes/test_flask_route_boundaries.py::test_economy_comparison_logic_is_not_in_the_http_layer", "tests/unit/routes/test_flask_route_boundaries.py::test_calculation_route_delegates_domain_processing_to_its_service", ] [[rule.ci]] diff --git a/tests/data/economy_comparison_snapshots.json b/tests/data/economy_comparison_snapshots.json deleted file mode 100644 index 821691033..000000000 --- a/tests/data/economy_comparison_snapshots.json +++ /dev/null @@ -1,8085 +0,0 @@ -{ - "cases": { - "area_breakdowns_numpy_incomes": "6b76bd61fccf0bbb", - "constituency_breakdown_country_None": "3d7edf7b8d836943", - "constituency_breakdown_country_UK": "3d7edf7b8d836943", - "constituency_breakdown_country_us": "3d7edf7b8d836943", - "constituency_breakdown_region_None": "58627eb566b5096c", - "constituency_breakdown_region_constituency/Constituency 4": "bca9e3f160f4017e", - "constituency_breakdown_region_constituency/Missing": "d86811df62ca768c", - "constituency_breakdown_region_constituency/S14000002": "b92910f1c70bc7be", - "constituency_breakdown_region_country/": "58627eb566b5096c", - "constituency_breakdown_region_country/ENGLAND": "a22d74f249e80a14", - "constituency_breakdown_region_country/Northern_Ireland": "7499530840afae5a", - "constituency_breakdown_region_country/england": "a22d74f249e80a14", - "constituency_breakdown_region_country/northern_ireland": "7499530840afae5a", - "constituency_breakdown_region_country/scotland": "be770bdebb4bc44e", - "constituency_breakdown_region_country/unknown": "58627eb566b5096c", - "constituency_breakdown_region_country/wales": "ea0cb06718c2a3ee", - "constituency_breakdown_region_local_authority/Local authority 4": "3d7edf7b8d836943", - "constituency_breakdown_region_local_authority/Missing": "3d7edf7b8d836943", - "constituency_breakdown_region_local_authority/N06000002": "3d7edf7b8d836943", - "constituency_breakdown_region_state/ca": "58627eb566b5096c", - "constituency_breakdown_region_uk": "58627eb566b5096c", - "local_authority_breakdown_country_None": "3d7edf7b8d836943", - "local_authority_breakdown_country_UK": "3d7edf7b8d836943", - "local_authority_breakdown_country_us": "3d7edf7b8d836943", - "local_authority_breakdown_region_None": "a0f54bfeeaeb5f01", - "local_authority_breakdown_region_constituency/Constituency 4": "3d7edf7b8d836943", - "local_authority_breakdown_region_constituency/Missing": "3d7edf7b8d836943", - "local_authority_breakdown_region_constituency/S14000002": "3d7edf7b8d836943", - "local_authority_breakdown_region_country/": "a0f54bfeeaeb5f01", - "local_authority_breakdown_region_country/ENGLAND": "df9703bc15aed219", - "local_authority_breakdown_region_country/Northern_Ireland": "8f9eacaf36435ed2", - "local_authority_breakdown_region_country/england": "df9703bc15aed219", - "local_authority_breakdown_region_country/northern_ireland": "8f9eacaf36435ed2", - "local_authority_breakdown_region_country/scotland": "5845de9e78b609cd", - "local_authority_breakdown_region_country/unknown": "a0f54bfeeaeb5f01", - "local_authority_breakdown_region_country/wales": "405183757f196d9d", - "local_authority_breakdown_region_local_authority/Local authority 4": "0a623f658d39403c", - "local_authority_breakdown_region_local_authority/Missing": "2b41b426c8388a6e", - "local_authority_breakdown_region_local_authority/N06000002": "b1c4dc99ea923286", - "local_authority_breakdown_region_state/ca": "a0f54bfeeaeb5f01", - "local_authority_breakdown_region_uk": "a0f54bfeeaeb5f01", - "missing_type": "3d7edf7b8d836943", - "uk_cliff": "d51953975fb01911", - "uk_general_all_regions": "93fdec1dfdb6bc3c", - "uk_general_constituency_by_name": "a07d11f0be32b997", - "uk_general_country_scotland": "7440b7b4b4a295d0", - "uk_general_numpy_inputs": "9e199e7f1ac1bad4", - "unknown_type": "3d7edf7b8d836943", - "us_cliff": "d51953975fb01911", - "us_general_national_without_wealth_deciles": "46762aba1acca0b8", - "us_general_state_with_wealth_deciles": "31fd7a13a472c7b8" - }, - "outputs": { - "0a623f658d39403c": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [ - "Local authority 4" - ], - "/by_local_authority/Local authority 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": { - "Local authority 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "2b41b426c8388a6e": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": {}, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "31fd7a13a472c7b8": { - "dataset_calls": [], - "key_order": { - "": [ - "budget", - "detailed_budget", - "decile", - "inequality", - "poverty", - "poverty_by_gender", - "poverty_by_race", - "intra_decile", - "wealth_decile", - "intra_wealth_decile", - "labor_supply_response", - "constituency_impact", - "local_authority_impact" - ], - "/budget": [ - "budgetary_impact", - "tax_revenue_impact", - "state_tax_revenue_impact", - "benefit_spending_impact", - "households", - "baseline_net_income" - ], - "/decile": [ - "relative", - "average" - ], - "/decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/detailed_budget": [], - "/inequality": [ - "gini", - "top_10_pct_share", - "top_1_pct_share" - ], - "/inequality/gini": [ - "baseline", - "reform" - ], - "/inequality/top_10_pct_share": [ - "baseline", - "reform" - ], - "/inequality/top_1_pct_share": [ - "baseline", - "reform" - ], - "/intra_decile": [ - "deciles", - "all" - ], - "/intra_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile": [ - "deciles", - "all" - ], - "/intra_wealth_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/labor_supply_response": [ - "substitution_lsr", - "income_lsr", - "relative_lsr", - "total_change", - "revenue_change", - "decile", - "hours" - ], - "/labor_supply_response/decile": [ - "average", - "relative" - ], - "/labor_supply_response/decile/average": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/average/income": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/average/substitution": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/relative": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/relative/income": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/decile/relative/substitution": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/hours": [ - "baseline", - "reform", - "change", - "income_effect", - "substitution_effect" - ], - "/labor_supply_response/relative_lsr": [ - "income", - "substitution" - ], - "/poverty": [ - "poverty", - "deep_poverty" - ], - "/poverty/deep_poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/deep_poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/all": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/child": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/senior": [ - "baseline", - "reform" - ], - "/poverty/poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/poverty/all": [ - "baseline", - "reform" - ], - "/poverty/poverty/child": [ - "baseline", - "reform" - ], - "/poverty/poverty/senior": [ - "baseline", - "reform" - ], - "/poverty_by_gender": [ - "poverty", - "deep_poverty" - ], - "/poverty_by_gender/deep_poverty": [ - "male", - "female" - ], - "/poverty_by_gender/deep_poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/deep_poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty": [ - "male", - "female" - ], - "/poverty_by_gender/poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_race": [ - "poverty" - ], - "/poverty_by_race/poverty": [ - "white", - "black", - "hispanic", - "other" - ], - "/poverty_by_race/poverty/black": [ - "baseline", - "reform" - ], - "/poverty_by_race/poverty/hispanic": [ - "baseline", - "reform" - ], - "/poverty_by_race/poverty/other": [ - "baseline", - "reform" - ], - "/poverty_by_race/poverty/white": [ - "baseline", - "reform" - ], - "/wealth_decile": [ - "relative", - "average" - ], - "/wealth_decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/wealth_decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ] - }, - "logs": [], - "result": { - "budget": { - "baseline_net_income": "float:1600000000000.0", - "benefit_spending_impact": "float:1750000000.0", - "budgetary_impact": "float:1500000000.0", - "households": "float:28.0", - "state_tax_revenue_impact": "float:-110000000.0", - "tax_revenue_impact": "float:3250000000.0" - }, - "constituency_impact": null, - "decile": { - "average": { - "int:1": "float:26.09166666666715", - "int:10": "float:9.999999999998181", - "int:2": "float:380.37573281250025", - "int:3": "float:433.33333333333394", - "int:4": "float:53.96875", - "int:5": "float:-162.08333333333334", - "int:6": "float:0.0", - "int:7": "float:-2.7999999999992724", - "int:8": "float:-12.5", - "int:9": "float:-436.25" - }, - "relative": { - "int:1": "float:0.0034444444444445086", - "int:10": "float:0.000999999999999818", - "int:2": "float:0.04738961825968467", - "int:3": "float:0.07046070460704616", - "int:4": "float:0.007010351126446113", - "int:5": "float:-0.014171220400728598", - "int:6": "float:nan", - "int:7": "float:-0.00039999999999989604", - "int:8": "float:0.05", - "int:9": "float:-0.05" - } - }, - "detailed_budget": {}, - "inequality": { - "gini": { - "baseline": "float:0.4217", - "reform": "float:0.4186" - }, - "top_10_pct_share": { - "baseline": "float:0.3125", - "reform": "float:0.3083" - }, - "top_1_pct_share": { - "baseline": "float:0.1033", - "reform": "float:0.1022" - } - }, - "intra_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.07692307692307693", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.5571428571428572" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:1.0", - "float:1.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:1.0" - ] - } - } - }, - "intra_wealth_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.15384615384615385", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.4802197802197802" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.7692307692307693", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.0", - "float:0.0", - "float:1.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.23076923076923078", - "float:1.0" - ] - } - } - }, - "labor_supply_response": { - "decile": { - "average": { - "income": { - "float:-1.0": "float:3.625", - "float:1.0": "float:1.2083333333333333", - "float:10.0": "float:3.625", - "float:2.0": "float:-2.265625", - "float:3.0": "float:-1.2083333333333333", - "float:4.0": "float:2.265625", - "float:5.0": "float:-2.4166666666666665", - "float:6.0": "float:-3.625", - "float:7.0": "float:3.625", - "float:8.0": "float:0.0", - "float:9.0": "float:-3.625" - }, - "substitution": { - "float:-1.0": "float:-12.5", - "float:1.0": "float:-12.5", - "float:10.0": "float:12.5", - "float:2.0": "float:-6.25", - "float:3.0": "float:0.0", - "float:4.0": "float:6.25", - "float:5.0": "float:12.5", - "float:6.0": "float:-12.5", - "float:7.0": "float:-6.25", - "float:8.0": "float:0.0", - "float:9.0": "float:6.25" - } - }, - "relative": { - "income": { - "int:1": "float:3.598590841462219e-05", - "int:10": "float:0.00010826106596085429", - "int:2": "float:-7.336033330887989e-05", - "int:3": "float:-3.829348390155141e-05", - "int:4": "float:7.522873426357554e-05", - "int:5": "float:-6.1032201852432556e-05", - "int:6": "float:-0.00013174093372522475", - "int:7": "float:0.00011132394885240364", - "int:8": "float:0.0", - "int:9": "float:-0.0001018905976059223" - }, - "substitution": { - "int:1": "float:-0.00037226801808229855", - "int:10": "float:0.00037331402055466997", - "int:2": "float:-0.00020237333326587556", - "int:3": "float:0.0", - "int:4": "float:0.00020752754279607045", - "int:5": "float:0.000315683802684996", - "int:6": "float:-0.00045427908181111987", - "int:7": "float:-0.00019193784284897178", - "int:8": "float:0.0", - "int:9": "float:0.0001756734441481419" - } - } - }, - "hours": { - "baseline": "float:1180000000.0", - "change": "float:2300000.0", - "income_effect": "float:1000000.0", - "reform": "float:1182300000.0", - "substitution_effect": "float:-3000000.0" - }, - "income_lsr": "float:-75000000.0", - "relative_lsr": { - "income": "float64:5.8285236289352835e-06", - "substitution": "float64:-4.01967146823123e-05" - }, - "revenue_change": "float:400000000.0", - "substitution_lsr": "float:125000000.0", - "total_change": "float:50000000.0" - }, - "local_authority_impact": null, - "poverty": { - "deep_poverty": { - "adult": { - "baseline": "float:0.1346153846153846", - "reform": "float:0.1346153846153846" - }, - "all": { - "baseline": "float:0.1523809523809524", - "reform": "float:0.06666666666666667" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.0" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - }, - "poverty": { - "adult": { - "baseline": "float:0.40384615384615385", - "reform": "float:0.40384615384615385" - }, - "all": { - "baseline": "float:0.2857142857142857", - "reform": "float:0.37142857142857144" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.5625" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - } - }, - "poverty_by_gender": { - "deep_poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "male": { - "baseline": "float:0.3018867924528302", - "reform": "float:0.1320754716981132" - } - }, - "poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.09615384615384616" - }, - "male": { - "baseline": "float:0.5660377358490566", - "reform": "float:0.6415094339622641" - } - } - }, - "poverty_by_race": { - "poverty": { - "black": { - "baseline": "float:0.0", - "reform": "float:0.16666666666666666" - }, - "hispanic": { - "baseline": "float:0.0", - "reform": "float:0.17391304347826086" - }, - "other": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "white": { - "baseline": "float:1.0", - "reform": "float:1.0" - } - } - }, - "wealth_decile": { - "average": { - "int:1": "float:-12.5", - "int:10": "float:-2.7999999999992724", - "int:2": "float:-436.25", - "int:3": "float:9.999999999998181", - "int:4": "float:26.09166666666715", - "int:5": "float:380.37573281250025", - "int:6": "float:433.33333333333394", - "int:7": "float:53.96875", - "int:8": "float:-162.08333333333334", - "int:9": "float:-200.0" - }, - "relative": { - "int:1": "float:0.05", - "int:10": "float:-0.00039999999999989604", - "int:2": "float:-0.05", - "int:3": "float:0.000999999999999818", - "int:4": "float:0.0034444444444445086", - "int:5": "float:0.04738961825968467", - "int:6": "float:0.07046070460704616", - "int:7": "float:0.007010351126446113", - "int:8": "float:-0.014171220400728598", - "int:9": "float:-0.02" - } - } - }, - "result_type": "dict" - }, - "3d7edf7b8d836943": { - "dataset_calls": [], - "key_order": {}, - "logs": [], - "result": null, - "result_type": "NoneType" - }, - "405183757f196d9d": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [ - "Local authority 2" - ], - "/by_local_authority/Local authority 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": { - "Local authority 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "46762aba1acca0b8": { - "dataset_calls": [], - "key_order": { - "": [ - "budget", - "detailed_budget", - "decile", - "inequality", - "poverty", - "poverty_by_gender", - "poverty_by_race", - "intra_decile", - "wealth_decile", - "intra_wealth_decile", - "labor_supply_response", - "constituency_impact", - "local_authority_impact" - ], - "/budget": [ - "budgetary_impact", - "tax_revenue_impact", - "state_tax_revenue_impact", - "benefit_spending_impact", - "households", - "baseline_net_income" - ], - "/decile": [ - "relative", - "average" - ], - "/decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/detailed_budget": [], - "/inequality": [ - "gini", - "top_10_pct_share", - "top_1_pct_share" - ], - "/inequality/gini": [ - "baseline", - "reform" - ], - "/inequality/top_10_pct_share": [ - "baseline", - "reform" - ], - "/inequality/top_1_pct_share": [ - "baseline", - "reform" - ], - "/intra_decile": [ - "deciles", - "all" - ], - "/intra_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile": [], - "/labor_supply_response": [ - "substitution_lsr", - "income_lsr", - "relative_lsr", - "total_change", - "revenue_change", - "decile", - "hours" - ], - "/labor_supply_response/decile": [ - "average", - "relative" - ], - "/labor_supply_response/decile/average": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/average/income": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/average/substitution": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/relative": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/relative/income": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/decile/relative/substitution": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/hours": [ - "baseline", - "reform", - "change", - "income_effect", - "substitution_effect" - ], - "/labor_supply_response/relative_lsr": [ - "income", - "substitution" - ], - "/poverty": [ - "poverty", - "deep_poverty" - ], - "/poverty/deep_poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/deep_poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/all": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/child": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/senior": [ - "baseline", - "reform" - ], - "/poverty/poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/poverty/all": [ - "baseline", - "reform" - ], - "/poverty/poverty/child": [ - "baseline", - "reform" - ], - "/poverty/poverty/senior": [ - "baseline", - "reform" - ], - "/poverty_by_gender": [ - "poverty", - "deep_poverty" - ], - "/poverty_by_gender/deep_poverty": [ - "male", - "female" - ], - "/poverty_by_gender/deep_poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/deep_poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty": [ - "male", - "female" - ], - "/poverty_by_gender/poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_race": [ - "poverty" - ], - "/poverty_by_race/poverty": [ - "white", - "black", - "hispanic", - "other" - ], - "/poverty_by_race/poverty/black": [ - "baseline", - "reform" - ], - "/poverty_by_race/poverty/hispanic": [ - "baseline", - "reform" - ], - "/poverty_by_race/poverty/other": [ - "baseline", - "reform" - ], - "/poverty_by_race/poverty/white": [ - "baseline", - "reform" - ], - "/wealth_decile": [] - }, - "logs": [ - [ - "policyengine_api.services.economy_comparison", - "ERROR", - "Wealth decile impact computation failed; returning empty breakdowns.", - "KeyError" - ] - ], - "result": { - "budget": { - "baseline_net_income": "float:1600000000000.0", - "benefit_spending_impact": "float:1750000000.0", - "budgetary_impact": "float:1500000000.0", - "households": "float:28.0", - "state_tax_revenue_impact": "float:-110000000.0", - "tax_revenue_impact": "float:3250000000.0" - }, - "constituency_impact": null, - "decile": { - "average": { - "int:1": "float:26.09166666666715", - "int:10": "float:9.999999999998181", - "int:2": "float:380.37573281250025", - "int:3": "float:433.33333333333394", - "int:4": "float:53.96875", - "int:5": "float:-162.08333333333334", - "int:6": "float:0.0", - "int:7": "float:-2.7999999999992724", - "int:8": "float:-12.5", - "int:9": "float:-436.25" - }, - "relative": { - "int:1": "float:0.0034444444444445086", - "int:10": "float:0.000999999999999818", - "int:2": "float:0.04738961825968467", - "int:3": "float:0.07046070460704616", - "int:4": "float:0.007010351126446113", - "int:5": "float:-0.014171220400728598", - "int:6": "float:nan", - "int:7": "float:-0.00039999999999989604", - "int:8": "float:0.05", - "int:9": "float:-0.05" - } - }, - "detailed_budget": {}, - "inequality": { - "gini": { - "baseline": "float:0.4217", - "reform": "float:0.4186" - }, - "top_10_pct_share": { - "baseline": "float:0.3125", - "reform": "float:0.3083" - }, - "top_1_pct_share": { - "baseline": "float:0.1033", - "reform": "float:0.1022" - } - }, - "intra_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.07692307692307693", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.5571428571428572" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:1.0", - "float:1.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:1.0" - ] - } - } - }, - "intra_wealth_decile": {}, - "labor_supply_response": { - "decile": { - "average": { - "income": { - "float:-1.0": "float:3.625", - "float:1.0": "float:1.2083333333333333", - "float:10.0": "float:3.625", - "float:2.0": "float:-2.265625", - "float:3.0": "float:-1.2083333333333333", - "float:4.0": "float:2.265625", - "float:5.0": "float:-2.4166666666666665", - "float:6.0": "float:-3.625", - "float:7.0": "float:3.625", - "float:8.0": "float:0.0", - "float:9.0": "float:-3.625" - }, - "substitution": { - "float:-1.0": "float:-12.5", - "float:1.0": "float:-12.5", - "float:10.0": "float:12.5", - "float:2.0": "float:-6.25", - "float:3.0": "float:0.0", - "float:4.0": "float:6.25", - "float:5.0": "float:12.5", - "float:6.0": "float:-12.5", - "float:7.0": "float:-6.25", - "float:8.0": "float:0.0", - "float:9.0": "float:6.25" - } - }, - "relative": { - "income": { - "int:1": "float:3.598590841462219e-05", - "int:10": "float:0.00010826106596085429", - "int:2": "float:-7.336033330887989e-05", - "int:3": "float:-3.829348390155141e-05", - "int:4": "float:7.522873426357554e-05", - "int:5": "float:-6.1032201852432556e-05", - "int:6": "float:-0.00013174093372522475", - "int:7": "float:0.00011132394885240364", - "int:8": "float:0.0", - "int:9": "float:-0.0001018905976059223" - }, - "substitution": { - "int:1": "float:-0.00037226801808229855", - "int:10": "float:0.00037331402055466997", - "int:2": "float:-0.00020237333326587556", - "int:3": "float:0.0", - "int:4": "float:0.00020752754279607045", - "int:5": "float:0.000315683802684996", - "int:6": "float:-0.00045427908181111987", - "int:7": "float:-0.00019193784284897178", - "int:8": "float:0.0", - "int:9": "float:0.0001756734441481419" - } - } - }, - "hours": { - "baseline": "float:1180000000.0", - "change": "float:2300000.0", - "income_effect": "float:1000000.0", - "reform": "float:1182300000.0", - "substitution_effect": "float:-3000000.0" - }, - "income_lsr": "float:-75000000.0", - "relative_lsr": { - "income": "float64:5.8285236289352835e-06", - "substitution": "float64:-4.01967146823123e-05" - }, - "revenue_change": "float:400000000.0", - "substitution_lsr": "float:125000000.0", - "total_change": "float:50000000.0" - }, - "local_authority_impact": null, - "poverty": { - "deep_poverty": { - "adult": { - "baseline": "float:0.1346153846153846", - "reform": "float:0.1346153846153846" - }, - "all": { - "baseline": "float:0.1523809523809524", - "reform": "float:0.06666666666666667" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.0" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - }, - "poverty": { - "adult": { - "baseline": "float:0.40384615384615385", - "reform": "float:0.40384615384615385" - }, - "all": { - "baseline": "float:0.2857142857142857", - "reform": "float:0.37142857142857144" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.5625" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - } - }, - "poverty_by_gender": { - "deep_poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "male": { - "baseline": "float:0.3018867924528302", - "reform": "float:0.1320754716981132" - } - }, - "poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.09615384615384616" - }, - "male": { - "baseline": "float:0.5660377358490566", - "reform": "float:0.6415094339622641" - } - } - }, - "poverty_by_race": { - "poverty": { - "black": { - "baseline": "float:0.0", - "reform": "float:0.16666666666666666" - }, - "hispanic": { - "baseline": "float:0.0", - "reform": "float:0.17391304347826086" - }, - "other": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "white": { - "baseline": "float:1.0", - "reform": "float:1.0" - } - } - }, - "wealth_decile": {} - }, - "result_type": "dict" - }, - "5845de9e78b609cd": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [ - "Local authority 1", - "Local authority 6" - ], - "/by_local_authority/Local authority 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": { - "Local authority 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Local authority 6": { - "average_household_income_change": "float:189.81651617187526", - "relative_household_income_change": "float:0.029066292702476515", - "x": "int:4", - "y": "int:7" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "58627eb566b5096c": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [ - "Constituency 0", - "Constituency 1", - "Constituency 2", - "Constituency 3", - "Constituency 4", - "Constituency 5", - "Constituency 6", - "Constituency 7" - ], - "/by_constituency/Constituency 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 5": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": { - "Constituency 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Constituency 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Constituency 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - }, - "Constituency 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Constituency 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - }, - "Constituency 5": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:nan", - "x": "int:1", - "y": "int:5" - }, - "Constituency 6": { - "average_household_income_change": "float:19.123372416666502", - "relative_household_income_change": "float:0.002298021920688953", - "x": "int:4", - "y": "int:7" - }, - "Constituency 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:2", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:2", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - }, - "6b76bd61fccf0bbb": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ], - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "constituency", - "local_authority" - ], - "/constituency": [ - "by_constituency", - "outcomes_by_region" - ], - "/constituency/by_constituency": [ - "Constituency 0", - "Constituency 1", - "Constituency 2", - "Constituency 3", - "Constituency 4", - "Constituency 5", - "Constituency 6", - "Constituency 7" - ], - "/constituency/by_constituency/Constituency 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/by_constituency/Constituency 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/by_constituency/Constituency 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/by_constituency/Constituency 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/by_constituency/Constituency 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/by_constituency/Constituency 5": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/by_constituency/Constituency 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/by_constituency/Constituency 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/constituency/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority": [ - "by_local_authority", - "outcomes_by_region" - ], - "/local_authority/by_local_authority": [ - "Local authority 0", - "Local authority 1", - "Local authority 2", - "Local authority 3", - "Local authority 4", - "Local authority 5", - "Local authority 6", - "Local authority 7" - ], - "/local_authority/by_local_authority/Local authority 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/by_local_authority/Local authority 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/by_local_authority/Local authority 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/by_local_authority/Local authority 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/by_local_authority/Local authority 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/by_local_authority/Local authority 5": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/by_local_authority/Local authority 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/by_local_authority/Local authority 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/local_authority/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "constituency": { - "fields": { - "by_constituency": { - "Constituency 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Constituency 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Constituency 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - }, - "Constituency 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Constituency 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - }, - "Constituency 5": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:nan", - "x": "int:1", - "y": "int:5" - }, - "Constituency 6": { - "average_household_income_change": "float:19.123372416666502", - "relative_household_income_change": "float:0.002298021920688953", - "x": "int:4", - "y": "int:7" - }, - "Constituency 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:2", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:2", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "local_authority": { - "fields": { - "by_local_authority": { - "Local authority 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Local authority 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Local authority 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - }, - "Local authority 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Local authority 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - }, - "Local authority 5": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:nan", - "x": "int:1", - "y": "int:5" - }, - "Local authority 6": { - "average_household_income_change": "float:189.81651617187526", - "relative_household_income_change": "float:0.029066292702476515", - "x": "int:4", - "y": "int:7" - }, - "Local authority 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:2", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:2", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - } - }, - "result_type": "dict" - }, - "7440b7b4b4a295d0": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ], - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "budget", - "detailed_budget", - "decile", - "inequality", - "poverty", - "poverty_by_gender", - "poverty_by_race", - "intra_decile", - "wealth_decile", - "intra_wealth_decile", - "labor_supply_response", - "constituency_impact", - "local_authority_impact" - ], - "/budget": [ - "budgetary_impact", - "tax_revenue_impact", - "state_tax_revenue_impact", - "benefit_spending_impact", - "households", - "baseline_net_income" - ], - "/constituency_impact": [ - "by_constituency", - "outcomes_by_region" - ], - "/constituency_impact/by_constituency": [ - "Constituency 1", - "Constituency 6" - ], - "/constituency_impact/by_constituency/Constituency 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/constituency_impact/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/decile": [ - "relative", - "average" - ], - "/decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/detailed_budget": [ - "income_tax", - "national_insurance", - "universal_credit" - ], - "/detailed_budget/income_tax": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/national_insurance": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/universal_credit": [ - "baseline", - "reform", - "difference" - ], - "/inequality": [ - "gini", - "top_10_pct_share", - "top_1_pct_share" - ], - "/inequality/gini": [ - "baseline", - "reform" - ], - "/inequality/top_10_pct_share": [ - "baseline", - "reform" - ], - "/inequality/top_1_pct_share": [ - "baseline", - "reform" - ], - "/intra_decile": [ - "deciles", - "all" - ], - "/intra_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile": [ - "deciles", - "all" - ], - "/intra_wealth_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/labor_supply_response": [ - "substitution_lsr", - "income_lsr", - "relative_lsr", - "total_change", - "revenue_change", - "decile", - "hours" - ], - "/labor_supply_response/decile": [ - "average", - "relative" - ], - "/labor_supply_response/decile/average": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/average/income": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/average/substitution": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/relative": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/relative/income": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/decile/relative/substitution": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/hours": [ - "baseline", - "reform", - "change", - "income_effect", - "substitution_effect" - ], - "/labor_supply_response/relative_lsr": [ - "income", - "substitution" - ], - "/local_authority_impact": [ - "by_local_authority", - "outcomes_by_region" - ], - "/local_authority_impact/by_local_authority": [ - "Local authority 1", - "Local authority 6" - ], - "/local_authority_impact/by_local_authority/Local authority 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/local_authority_impact/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/poverty": [ - "poverty", - "deep_poverty" - ], - "/poverty/deep_poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/deep_poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/all": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/child": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/senior": [ - "baseline", - "reform" - ], - "/poverty/poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/poverty/all": [ - "baseline", - "reform" - ], - "/poverty/poverty/child": [ - "baseline", - "reform" - ], - "/poverty/poverty/senior": [ - "baseline", - "reform" - ], - "/poverty_by_gender": [ - "poverty", - "deep_poverty" - ], - "/poverty_by_gender/deep_poverty": [ - "male", - "female" - ], - "/poverty_by_gender/deep_poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/deep_poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty": [ - "male", - "female" - ], - "/poverty_by_gender/poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_race": [], - "/wealth_decile": [ - "relative", - "average" - ], - "/wealth_decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/wealth_decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ] - }, - "logs": [], - "result": { - "budget": { - "baseline_net_income": "float:1600000000000.0", - "benefit_spending_impact": "float:1750000000.0", - "budgetary_impact": "float:1500000000.0", - "households": "float:28.0", - "state_tax_revenue_impact": "float:-110000000.0", - "tax_revenue_impact": "float:3250000000.0" - }, - "constituency_impact": { - "by_constituency": { - "Constituency 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Constituency 6": { - "average_household_income_change": "float:19.123372416666502", - "relative_household_income_change": "float:0.002298021920688953", - "x": "int:4", - "y": "int:7" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "decile": { - "average": { - "int:1": "float:26.09166666666715", - "int:10": "float:9.999999999998181", - "int:2": "float:380.37573281250025", - "int:3": "float:433.33333333333394", - "int:4": "float:53.96875", - "int:5": "float:-162.08333333333334", - "int:6": "float:0.0", - "int:7": "float:-2.7999999999992724", - "int:8": "float:-12.5", - "int:9": "float:-436.25" - }, - "relative": { - "int:1": "float:0.0034444444444445086", - "int:10": "float:0.000999999999999818", - "int:2": "float:0.04738961825968467", - "int:3": "float:0.07046070460704616", - "int:4": "float:0.007010351126446113", - "int:5": "float:-0.014171220400728598", - "int:6": "float:nan", - "int:7": "float:-0.00039999999999989604", - "int:8": "float:0.05", - "int:9": "float:-0.05" - } - }, - "detailed_budget": { - "income_tax": { - "baseline": "float:240000000000.0", - "difference": "float:1500000000.0", - "reform": "float:241500000000.0" - }, - "national_insurance": { - "baseline": "float:160000000000.0", - "difference": "float:-200000000.0", - "reform": "float:159800000000.0" - }, - "universal_credit": { - "baseline": "float:61000000000.0", - "difference": "float:750000000.0", - "reform": "float:61750000000.0" - } - }, - "inequality": { - "gini": { - "baseline": "float:0.4217", - "reform": "float:0.4186" - }, - "top_10_pct_share": { - "baseline": "float:0.3125", - "reform": "float:0.3083" - }, - "top_1_pct_share": { - "baseline": "float:0.1033", - "reform": "float:0.1022" - } - }, - "intra_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.07692307692307693", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.5571428571428572" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:1.0", - "float:1.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:1.0" - ] - } - } - }, - "intra_wealth_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.15384615384615385", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.4802197802197802" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.7692307692307693", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.0", - "float:0.0", - "float:1.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.23076923076923078", - "float:1.0" - ] - } - } - }, - "labor_supply_response": { - "decile": { - "average": { - "income": { - "float:-1.0": "float:3.625", - "float:1.0": "float:1.2083333333333333", - "float:10.0": "float:3.625", - "float:2.0": "float:-2.265625", - "float:3.0": "float:-1.2083333333333333", - "float:4.0": "float:2.265625", - "float:5.0": "float:-2.4166666666666665", - "float:6.0": "float:-3.625", - "float:7.0": "float:3.625", - "float:8.0": "float:0.0", - "float:9.0": "float:-3.625" - }, - "substitution": { - "float:-1.0": "float:-12.5", - "float:1.0": "float:-12.5", - "float:10.0": "float:12.5", - "float:2.0": "float:-6.25", - "float:3.0": "float:0.0", - "float:4.0": "float:6.25", - "float:5.0": "float:12.5", - "float:6.0": "float:-12.5", - "float:7.0": "float:-6.25", - "float:8.0": "float:0.0", - "float:9.0": "float:6.25" - } - }, - "relative": { - "income": { - "int:1": "float:3.598590841462219e-05", - "int:10": "float:0.00010826106596085429", - "int:2": "float:-7.336033330887989e-05", - "int:3": "float:-3.829348390155141e-05", - "int:4": "float:7.522873426357554e-05", - "int:5": "float:-6.1032201852432556e-05", - "int:6": "float:-0.00013174093372522475", - "int:7": "float:0.00011132394885240364", - "int:8": "float:0.0", - "int:9": "float:-0.0001018905976059223" - }, - "substitution": { - "int:1": "float:-0.00037226801808229855", - "int:10": "float:0.00037331402055466997", - "int:2": "float:-0.00020237333326587556", - "int:3": "float:0.0", - "int:4": "float:0.00020752754279607045", - "int:5": "float:0.000315683802684996", - "int:6": "float:-0.00045427908181111987", - "int:7": "float:-0.00019193784284897178", - "int:8": "float:0.0", - "int:9": "float:0.0001756734441481419" - } - } - }, - "hours": { - "baseline": "float:1180000000.0", - "change": "float:2300000.0", - "income_effect": "float:1000000.0", - "reform": "float:1182300000.0", - "substitution_effect": "float:-3000000.0" - }, - "income_lsr": "float:-75000000.0", - "relative_lsr": { - "income": "float64:5.8285236289352835e-06", - "substitution": "float64:-4.01967146823123e-05" - }, - "revenue_change": "float:400000000.0", - "substitution_lsr": "float:125000000.0", - "total_change": "float:50000000.0" - }, - "local_authority_impact": { - "by_local_authority": { - "Local authority 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Local authority 6": { - "average_household_income_change": "float:189.81651617187526", - "relative_household_income_change": "float:0.029066292702476515", - "x": "int:4", - "y": "int:7" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "poverty": { - "deep_poverty": { - "adult": { - "baseline": "float:0.1346153846153846", - "reform": "float:0.1346153846153846" - }, - "all": { - "baseline": "float:0.1523809523809524", - "reform": "float:0.06666666666666667" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.0" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - }, - "poverty": { - "adult": { - "baseline": "float:0.40384615384615385", - "reform": "float:0.40384615384615385" - }, - "all": { - "baseline": "float:0.2857142857142857", - "reform": "float:0.37142857142857144" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.5625" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - } - }, - "poverty_by_gender": { - "deep_poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "male": { - "baseline": "float:0.3018867924528302", - "reform": "float:0.1320754716981132" - } - }, - "poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.09615384615384616" - }, - "male": { - "baseline": "float:0.5660377358490566", - "reform": "float:0.6415094339622641" - } - } - }, - "poverty_by_race": {}, - "wealth_decile": { - "average": { - "int:1": "float:-12.5", - "int:10": "float:-2.7999999999992724", - "int:2": "float:-436.25", - "int:3": "float:9.999999999998181", - "int:4": "float:26.09166666666715", - "int:5": "float:380.37573281250025", - "int:6": "float:433.33333333333394", - "int:7": "float:53.96875", - "int:8": "float:-162.08333333333334", - "int:9": "float:-200.0" - }, - "relative": { - "int:1": "float:0.05", - "int:10": "float:-0.00039999999999989604", - "int:2": "float:-0.05", - "int:3": "float:0.000999999999999818", - "int:4": "float:0.0034444444444445086", - "int:5": "float:0.04738961825968467", - "int:6": "float:0.07046070460704616", - "int:7": "float:0.007010351126446113", - "int:8": "float:-0.014171220400728598", - "int:9": "float:-0.02" - } - } - }, - "result_type": "dict" - }, - "7499530840afae5a": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [ - "Constituency 3", - "Constituency 7" - ], - "/by_constituency/Constituency 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": { - "Constituency 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Constituency 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - }, - "8f9eacaf36435ed2": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [ - "Local authority 3", - "Local authority 7" - ], - "/by_local_authority/Local authority 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": { - "Local authority 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Local authority 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "93fdec1dfdb6bc3c": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ], - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "budget", - "detailed_budget", - "decile", - "inequality", - "poverty", - "poverty_by_gender", - "poverty_by_race", - "intra_decile", - "wealth_decile", - "intra_wealth_decile", - "labor_supply_response", - "constituency_impact", - "local_authority_impact" - ], - "/budget": [ - "budgetary_impact", - "tax_revenue_impact", - "state_tax_revenue_impact", - "benefit_spending_impact", - "households", - "baseline_net_income" - ], - "/constituency_impact": [ - "by_constituency", - "outcomes_by_region" - ], - "/constituency_impact/by_constituency": [ - "Constituency 0", - "Constituency 1", - "Constituency 2", - "Constituency 3", - "Constituency 4", - "Constituency 5", - "Constituency 6", - "Constituency 7" - ], - "/constituency_impact/by_constituency/Constituency 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 5": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/constituency_impact/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/decile": [ - "relative", - "average" - ], - "/decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/detailed_budget": [ - "income_tax", - "national_insurance", - "universal_credit" - ], - "/detailed_budget/income_tax": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/national_insurance": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/universal_credit": [ - "baseline", - "reform", - "difference" - ], - "/inequality": [ - "gini", - "top_10_pct_share", - "top_1_pct_share" - ], - "/inequality/gini": [ - "baseline", - "reform" - ], - "/inequality/top_10_pct_share": [ - "baseline", - "reform" - ], - "/inequality/top_1_pct_share": [ - "baseline", - "reform" - ], - "/intra_decile": [ - "deciles", - "all" - ], - "/intra_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile": [ - "deciles", - "all" - ], - "/intra_wealth_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/labor_supply_response": [ - "substitution_lsr", - "income_lsr", - "relative_lsr", - "total_change", - "revenue_change", - "decile", - "hours" - ], - "/labor_supply_response/decile": [ - "average", - "relative" - ], - "/labor_supply_response/decile/average": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/average/income": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/average/substitution": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/relative": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/relative/income": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/decile/relative/substitution": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/hours": [ - "baseline", - "reform", - "change", - "income_effect", - "substitution_effect" - ], - "/labor_supply_response/relative_lsr": [ - "income", - "substitution" - ], - "/local_authority_impact": [ - "by_local_authority", - "outcomes_by_region" - ], - "/local_authority_impact/by_local_authority": [ - "Local authority 0", - "Local authority 1", - "Local authority 2", - "Local authority 3", - "Local authority 4", - "Local authority 5", - "Local authority 6", - "Local authority 7" - ], - "/local_authority_impact/by_local_authority/Local authority 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 5": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/local_authority_impact/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/poverty": [ - "poverty", - "deep_poverty" - ], - "/poverty/deep_poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/deep_poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/all": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/child": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/senior": [ - "baseline", - "reform" - ], - "/poverty/poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/poverty/all": [ - "baseline", - "reform" - ], - "/poverty/poverty/child": [ - "baseline", - "reform" - ], - "/poverty/poverty/senior": [ - "baseline", - "reform" - ], - "/poverty_by_gender": [ - "poverty", - "deep_poverty" - ], - "/poverty_by_gender/deep_poverty": [ - "male", - "female" - ], - "/poverty_by_gender/deep_poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/deep_poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty": [ - "male", - "female" - ], - "/poverty_by_gender/poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_race": [], - "/wealth_decile": [ - "relative", - "average" - ], - "/wealth_decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/wealth_decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ] - }, - "logs": [], - "result": { - "budget": { - "baseline_net_income": "float:1600000000000.0", - "benefit_spending_impact": "float:1750000000.0", - "budgetary_impact": "float:1500000000.0", - "households": "float:28.0", - "state_tax_revenue_impact": "float:-110000000.0", - "tax_revenue_impact": "float:3250000000.0" - }, - "constituency_impact": { - "by_constituency": { - "Constituency 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Constituency 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Constituency 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - }, - "Constituency 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Constituency 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - }, - "Constituency 5": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:nan", - "x": "int:1", - "y": "int:5" - }, - "Constituency 6": { - "average_household_income_change": "float:19.123372416666502", - "relative_household_income_change": "float:0.002298021920688953", - "x": "int:4", - "y": "int:7" - }, - "Constituency 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:2", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:2", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "decile": { - "average": { - "int:1": "float:26.09166666666715", - "int:10": "float:9.999999999998181", - "int:2": "float:380.37573281250025", - "int:3": "float:433.33333333333394", - "int:4": "float:53.96875", - "int:5": "float:-162.08333333333334", - "int:6": "float:0.0", - "int:7": "float:-2.7999999999992724", - "int:8": "float:-12.5", - "int:9": "float:-436.25" - }, - "relative": { - "int:1": "float:0.0034444444444445086", - "int:10": "float:0.000999999999999818", - "int:2": "float:0.04738961825968467", - "int:3": "float:0.07046070460704616", - "int:4": "float:0.007010351126446113", - "int:5": "float:-0.014171220400728598", - "int:6": "float:nan", - "int:7": "float:-0.00039999999999989604", - "int:8": "float:0.05", - "int:9": "float:-0.05" - } - }, - "detailed_budget": { - "income_tax": { - "baseline": "float:240000000000.0", - "difference": "float:1500000000.0", - "reform": "float:241500000000.0" - }, - "national_insurance": { - "baseline": "float:160000000000.0", - "difference": "float:-200000000.0", - "reform": "float:159800000000.0" - }, - "universal_credit": { - "baseline": "float:61000000000.0", - "difference": "float:750000000.0", - "reform": "float:61750000000.0" - } - }, - "inequality": { - "gini": { - "baseline": "float:0.4217", - "reform": "float:0.4186" - }, - "top_10_pct_share": { - "baseline": "float:0.3125", - "reform": "float:0.3083" - }, - "top_1_pct_share": { - "baseline": "float:0.1033", - "reform": "float:0.1022" - } - }, - "intra_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.07692307692307693", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.5571428571428572" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:1.0", - "float:1.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:1.0" - ] - } - } - }, - "intra_wealth_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.15384615384615385", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.4802197802197802" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.7692307692307693", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.0", - "float:0.0", - "float:1.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.23076923076923078", - "float:1.0" - ] - } - } - }, - "labor_supply_response": { - "decile": { - "average": { - "income": { - "float:-1.0": "float:3.625", - "float:1.0": "float:1.2083333333333333", - "float:10.0": "float:3.625", - "float:2.0": "float:-2.265625", - "float:3.0": "float:-1.2083333333333333", - "float:4.0": "float:2.265625", - "float:5.0": "float:-2.4166666666666665", - "float:6.0": "float:-3.625", - "float:7.0": "float:3.625", - "float:8.0": "float:0.0", - "float:9.0": "float:-3.625" - }, - "substitution": { - "float:-1.0": "float:-12.5", - "float:1.0": "float:-12.5", - "float:10.0": "float:12.5", - "float:2.0": "float:-6.25", - "float:3.0": "float:0.0", - "float:4.0": "float:6.25", - "float:5.0": "float:12.5", - "float:6.0": "float:-12.5", - "float:7.0": "float:-6.25", - "float:8.0": "float:0.0", - "float:9.0": "float:6.25" - } - }, - "relative": { - "income": { - "int:1": "float:3.598590841462219e-05", - "int:10": "float:0.00010826106596085429", - "int:2": "float:-7.336033330887989e-05", - "int:3": "float:-3.829348390155141e-05", - "int:4": "float:7.522873426357554e-05", - "int:5": "float:-6.1032201852432556e-05", - "int:6": "float:-0.00013174093372522475", - "int:7": "float:0.00011132394885240364", - "int:8": "float:0.0", - "int:9": "float:-0.0001018905976059223" - }, - "substitution": { - "int:1": "float:-0.00037226801808229855", - "int:10": "float:0.00037331402055466997", - "int:2": "float:-0.00020237333326587556", - "int:3": "float:0.0", - "int:4": "float:0.00020752754279607045", - "int:5": "float:0.000315683802684996", - "int:6": "float:-0.00045427908181111987", - "int:7": "float:-0.00019193784284897178", - "int:8": "float:0.0", - "int:9": "float:0.0001756734441481419" - } - } - }, - "hours": { - "baseline": "float:1180000000.0", - "change": "float:2300000.0", - "income_effect": "float:1000000.0", - "reform": "float:1182300000.0", - "substitution_effect": "float:-3000000.0" - }, - "income_lsr": "float:-75000000.0", - "relative_lsr": { - "income": "float64:5.8285236289352835e-06", - "substitution": "float64:-4.01967146823123e-05" - }, - "revenue_change": "float:400000000.0", - "substitution_lsr": "float:125000000.0", - "total_change": "float:50000000.0" - }, - "local_authority_impact": { - "by_local_authority": { - "Local authority 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Local authority 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Local authority 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - }, - "Local authority 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Local authority 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - }, - "Local authority 5": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:nan", - "x": "int:1", - "y": "int:5" - }, - "Local authority 6": { - "average_household_income_change": "float:189.81651617187526", - "relative_household_income_change": "float:0.029066292702476515", - "x": "int:4", - "y": "int:7" - }, - "Local authority 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:2", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:2", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "poverty": { - "deep_poverty": { - "adult": { - "baseline": "float:0.1346153846153846", - "reform": "float:0.1346153846153846" - }, - "all": { - "baseline": "float:0.1523809523809524", - "reform": "float:0.06666666666666667" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.0" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - }, - "poverty": { - "adult": { - "baseline": "float:0.40384615384615385", - "reform": "float:0.40384615384615385" - }, - "all": { - "baseline": "float:0.2857142857142857", - "reform": "float:0.37142857142857144" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.5625" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - } - }, - "poverty_by_gender": { - "deep_poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "male": { - "baseline": "float:0.3018867924528302", - "reform": "float:0.1320754716981132" - } - }, - "poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.09615384615384616" - }, - "male": { - "baseline": "float:0.5660377358490566", - "reform": "float:0.6415094339622641" - } - } - }, - "poverty_by_race": {}, - "wealth_decile": { - "average": { - "int:1": "float:-12.5", - "int:10": "float:-2.7999999999992724", - "int:2": "float:-436.25", - "int:3": "float:9.999999999998181", - "int:4": "float:26.09166666666715", - "int:5": "float:380.37573281250025", - "int:6": "float:433.33333333333394", - "int:7": "float:53.96875", - "int:8": "float:-162.08333333333334", - "int:9": "float:-200.0" - }, - "relative": { - "int:1": "float:0.05", - "int:10": "float:-0.00039999999999989604", - "int:2": "float:-0.05", - "int:3": "float:0.000999999999999818", - "int:4": "float:0.0034444444444445086", - "int:5": "float:0.04738961825968467", - "int:6": "float:0.07046070460704616", - "int:7": "float:0.007010351126446113", - "int:8": "float:-0.014171220400728598", - "int:9": "float:-0.02" - } - } - }, - "result_type": "dict" - }, - "9e199e7f1ac1bad4": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ], - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "budget", - "detailed_budget", - "decile", - "inequality", - "poverty", - "poverty_by_gender", - "poverty_by_race", - "intra_decile", - "wealth_decile", - "intra_wealth_decile", - "labor_supply_response", - "constituency_impact", - "local_authority_impact" - ], - "/budget": [ - "budgetary_impact", - "tax_revenue_impact", - "state_tax_revenue_impact", - "benefit_spending_impact", - "households", - "baseline_net_income" - ], - "/constituency_impact": [ - "by_constituency", - "outcomes_by_region" - ], - "/constituency_impact/by_constituency": [ - "Constituency 3", - "Constituency 7" - ], - "/constituency_impact/by_constituency/Constituency 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/by_constituency/Constituency 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/constituency_impact/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/decile": [ - "relative", - "average" - ], - "/decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/detailed_budget": [ - "income_tax", - "national_insurance", - "universal_credit" - ], - "/detailed_budget/income_tax": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/national_insurance": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/universal_credit": [ - "baseline", - "reform", - "difference" - ], - "/inequality": [ - "gini", - "top_10_pct_share", - "top_1_pct_share" - ], - "/inequality/gini": [ - "baseline", - "reform" - ], - "/inequality/top_10_pct_share": [ - "baseline", - "reform" - ], - "/inequality/top_1_pct_share": [ - "baseline", - "reform" - ], - "/intra_decile": [ - "deciles", - "all" - ], - "/intra_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile": [ - "deciles", - "all" - ], - "/intra_wealth_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/labor_supply_response": [ - "substitution_lsr", - "income_lsr", - "relative_lsr", - "total_change", - "revenue_change", - "decile", - "hours" - ], - "/labor_supply_response/decile": [ - "average", - "relative" - ], - "/labor_supply_response/decile/average": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/average/income": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/average/substitution": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/relative": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/relative/income": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/decile/relative/substitution": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/hours": [ - "baseline", - "reform", - "change", - "income_effect", - "substitution_effect" - ], - "/labor_supply_response/relative_lsr": [ - "income", - "substitution" - ], - "/local_authority_impact": [ - "by_local_authority", - "outcomes_by_region" - ], - "/local_authority_impact/by_local_authority": [ - "Local authority 3", - "Local authority 7" - ], - "/local_authority_impact/by_local_authority/Local authority 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/by_local_authority/Local authority 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/local_authority_impact/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/local_authority_impact/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/local_authority_impact/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/poverty": [ - "poverty", - "deep_poverty" - ], - "/poverty/deep_poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/deep_poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/all": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/child": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/senior": [ - "baseline", - "reform" - ], - "/poverty/poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/poverty/all": [ - "baseline", - "reform" - ], - "/poverty/poverty/child": [ - "baseline", - "reform" - ], - "/poverty/poverty/senior": [ - "baseline", - "reform" - ], - "/poverty_by_gender": [ - "poverty", - "deep_poverty" - ], - "/poverty_by_gender/deep_poverty": [ - "male", - "female" - ], - "/poverty_by_gender/deep_poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/deep_poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty": [ - "male", - "female" - ], - "/poverty_by_gender/poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_race": [], - "/wealth_decile": [ - "relative", - "average" - ], - "/wealth_decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/wealth_decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ] - }, - "logs": [], - "result": { - "budget": { - "baseline_net_income": "float:1600000000000.0", - "benefit_spending_impact": "float:1750000000.0", - "budgetary_impact": "float:1500000000.0", - "households": "float64:28.0", - "state_tax_revenue_impact": "float:-110000000.0", - "tax_revenue_impact": "float:3250000000.0" - }, - "constituency_impact": { - "by_constituency": { - "Constituency 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Constituency 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "decile": { - "average": { - "int:1": "float:26.09166666666715", - "int:10": "float:9.999999999998181", - "int:2": "float:380.37573281250025", - "int:3": "float:433.33333333333394", - "int:4": "float:53.96875", - "int:5": "float:-162.08333333333334", - "int:6": "float:0.0", - "int:7": "float:-2.7999999999992724", - "int:8": "float:-12.5", - "int:9": "float:-436.25" - }, - "relative": { - "int:1": "float:0.0034444444444445086", - "int:10": "float:0.000999999999999818", - "int:2": "float:0.04738961825968467", - "int:3": "float:0.07046070460704616", - "int:4": "float:0.007010351126446113", - "int:5": "float:-0.014171220400728598", - "int:6": "float:nan", - "int:7": "float:-0.00039999999999989604", - "int:8": "float:0.05", - "int:9": "float:-0.05" - } - }, - "detailed_budget": { - "income_tax": { - "baseline": "float:240000000000.0", - "difference": "float:1500000000.0", - "reform": "float:241500000000.0" - }, - "national_insurance": { - "baseline": "float:160000000000.0", - "difference": "float:-200000000.0", - "reform": "float:159800000000.0" - }, - "universal_credit": { - "baseline": "float:61000000000.0", - "difference": "float:750000000.0", - "reform": "float:61750000000.0" - } - }, - "inequality": { - "gini": { - "baseline": "float:0.4217", - "reform": "float:0.4186" - }, - "top_10_pct_share": { - "baseline": "float:0.3125", - "reform": "float:0.3083" - }, - "top_1_pct_share": { - "baseline": "float:0.1033", - "reform": "float:0.1022" - } - }, - "intra_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.07692307692307693", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.5571428571428572" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:1.0", - "float:1.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:1.0" - ] - } - } - }, - "intra_wealth_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.15384615384615385", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.4802197802197802" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.7692307692307693", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.0", - "float:0.0", - "float:1.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.23076923076923078", - "float:1.0" - ] - } - } - }, - "labor_supply_response": { - "decile": { - "average": { - "income": { - "float:-1.0": "float:3.625", - "float:1.0": "float:1.2083333333333333", - "float:10.0": "float:3.625", - "float:2.0": "float:-2.265625", - "float:3.0": "float:-1.2083333333333333", - "float:4.0": "float:2.265625", - "float:5.0": "float:-2.4166666666666665", - "float:6.0": "float:-3.625", - "float:7.0": "float:3.625", - "float:8.0": "float:0.0", - "float:9.0": "float:-3.625" - }, - "substitution": { - "float:-1.0": "float:-12.5", - "float:1.0": "float:-12.5", - "float:10.0": "float:12.5", - "float:2.0": "float:-6.25", - "float:3.0": "float:0.0", - "float:4.0": "float:6.25", - "float:5.0": "float:12.5", - "float:6.0": "float:-12.5", - "float:7.0": "float:-6.25", - "float:8.0": "float:0.0", - "float:9.0": "float:6.25" - } - }, - "relative": { - "income": { - "int:1": "float:3.598590841462219e-05", - "int:10": "float:0.00010826106596085429", - "int:2": "float:-7.336033330887989e-05", - "int:3": "float:-3.829348390155141e-05", - "int:4": "float:7.522873426357554e-05", - "int:5": "float:-6.1032201852432556e-05", - "int:6": "float:-0.00013174093372522475", - "int:7": "float:0.00011132394885240364", - "int:8": "float:0.0", - "int:9": "float:-0.0001018905976059223" - }, - "substitution": { - "int:1": "float:-0.00037226801808229855", - "int:10": "float:0.00037331402055466997", - "int:2": "float:-0.00020237333326587556", - "int:3": "float:0.0", - "int:4": "float:0.00020752754279607045", - "int:5": "float:0.000315683802684996", - "int:6": "float:-0.00045427908181111987", - "int:7": "float:-0.00019193784284897178", - "int:8": "float:0.0", - "int:9": "float:0.0001756734441481419" - } - } - }, - "hours": { - "baseline": "float:1180000000.0", - "change": "float:2300000.0", - "income_effect": "float:1000000.0", - "reform": "float:1182300000.0", - "substitution_effect": "float:-3000000.0" - }, - "income_lsr": "float:-75000000.0", - "relative_lsr": { - "income": "float64:5.8285236289352835e-06", - "substitution": "float64:-4.01967146823123e-05" - }, - "revenue_change": "float:400000000.0", - "substitution_lsr": "float:125000000.0", - "total_change": "float:50000000.0" - }, - "local_authority_impact": { - "by_local_authority": { - "Local authority 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Local authority 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "poverty": { - "deep_poverty": { - "adult": { - "baseline": "float:0.1346153846153846", - "reform": "float:0.1346153846153846" - }, - "all": { - "baseline": "float:0.1523809523809524", - "reform": "float:0.06666666666666667" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.0" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - }, - "poverty": { - "adult": { - "baseline": "float:0.40384615384615385", - "reform": "float:0.40384615384615385" - }, - "all": { - "baseline": "float:0.2857142857142857", - "reform": "float:0.37142857142857144" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.5625" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - } - }, - "poverty_by_gender": { - "deep_poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "male": { - "baseline": "float:0.3018867924528302", - "reform": "float:0.1320754716981132" - } - }, - "poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.09615384615384616" - }, - "male": { - "baseline": "float:0.5660377358490566", - "reform": "float:0.6415094339622641" - } - } - }, - "poverty_by_race": {}, - "wealth_decile": { - "average": { - "int:1": "float:-12.5", - "int:10": "float:-2.7999999999992724", - "int:2": "float:-436.25", - "int:3": "float:9.999999999998181", - "int:4": "float:26.09166666666715", - "int:5": "float:380.37573281250025", - "int:6": "float:433.33333333333394", - "int:7": "float:53.96875", - "int:8": "float:-162.08333333333334", - "int:9": "float:-200.0" - }, - "relative": { - "int:1": "float:0.05", - "int:10": "float:-0.00039999999999989604", - "int:2": "float:-0.05", - "int:3": "float:0.000999999999999818", - "int:4": "float:0.0034444444444445086", - "int:5": "float:0.04738961825968467", - "int:6": "float:0.07046070460704616", - "int:7": "float:0.007010351126446113", - "int:8": "float:-0.014171220400728598", - "int:9": "float:-0.02" - } - } - }, - "result_type": "dict" - }, - "a07d11f0be32b997": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "budget", - "detailed_budget", - "decile", - "inequality", - "poverty", - "poverty_by_gender", - "poverty_by_race", - "intra_decile", - "wealth_decile", - "intra_wealth_decile", - "labor_supply_response", - "constituency_impact", - "local_authority_impact" - ], - "/budget": [ - "budgetary_impact", - "tax_revenue_impact", - "state_tax_revenue_impact", - "benefit_spending_impact", - "households", - "baseline_net_income" - ], - "/constituency_impact": [ - "by_constituency", - "outcomes_by_region" - ], - "/constituency_impact/by_constituency": [ - "Constituency 3" - ], - "/constituency_impact/by_constituency/Constituency 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/constituency_impact/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/constituency_impact/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/constituency_impact/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/decile": [ - "relative", - "average" - ], - "/decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/detailed_budget": [ - "income_tax", - "national_insurance", - "universal_credit" - ], - "/detailed_budget/income_tax": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/national_insurance": [ - "baseline", - "reform", - "difference" - ], - "/detailed_budget/universal_credit": [ - "baseline", - "reform", - "difference" - ], - "/inequality": [ - "gini", - "top_10_pct_share", - "top_1_pct_share" - ], - "/inequality/gini": [ - "baseline", - "reform" - ], - "/inequality/top_10_pct_share": [ - "baseline", - "reform" - ], - "/inequality/top_1_pct_share": [ - "baseline", - "reform" - ], - "/intra_decile": [ - "deciles", - "all" - ], - "/intra_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile": [ - "deciles", - "all" - ], - "/intra_wealth_decile/all": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/intra_wealth_decile/deciles": [ - "Lose more than 5%", - "Lose less than 5%", - "No change", - "Gain less than 5%", - "Gain more than 5%" - ], - "/labor_supply_response": [ - "substitution_lsr", - "income_lsr", - "relative_lsr", - "total_change", - "revenue_change", - "decile", - "hours" - ], - "/labor_supply_response/decile": [ - "average", - "relative" - ], - "/labor_supply_response/decile/average": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/average/income": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/average/substitution": [ - "float:-1.0", - "float:1.0", - "float:2.0", - "float:3.0", - "float:4.0", - "float:5.0", - "float:6.0", - "float:7.0", - "float:8.0", - "float:9.0", - "float:10.0" - ], - "/labor_supply_response/decile/relative": [ - "income", - "substitution" - ], - "/labor_supply_response/decile/relative/income": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/decile/relative/substitution": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/labor_supply_response/hours": [ - "baseline", - "reform", - "change", - "income_effect", - "substitution_effect" - ], - "/labor_supply_response/relative_lsr": [ - "income", - "substitution" - ], - "/poverty": [ - "poverty", - "deep_poverty" - ], - "/poverty/deep_poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/deep_poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/all": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/child": [ - "baseline", - "reform" - ], - "/poverty/deep_poverty/senior": [ - "baseline", - "reform" - ], - "/poverty/poverty": [ - "child", - "adult", - "senior", - "all" - ], - "/poverty/poverty/adult": [ - "baseline", - "reform" - ], - "/poverty/poverty/all": [ - "baseline", - "reform" - ], - "/poverty/poverty/child": [ - "baseline", - "reform" - ], - "/poverty/poverty/senior": [ - "baseline", - "reform" - ], - "/poverty_by_gender": [ - "poverty", - "deep_poverty" - ], - "/poverty_by_gender/deep_poverty": [ - "male", - "female" - ], - "/poverty_by_gender/deep_poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/deep_poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty": [ - "male", - "female" - ], - "/poverty_by_gender/poverty/female": [ - "baseline", - "reform" - ], - "/poverty_by_gender/poverty/male": [ - "baseline", - "reform" - ], - "/poverty_by_race": [], - "/wealth_decile": [ - "relative", - "average" - ], - "/wealth_decile/average": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ], - "/wealth_decile/relative": [ - "int:1", - "int:2", - "int:3", - "int:4", - "int:5", - "int:6", - "int:7", - "int:8", - "int:9", - "int:10" - ] - }, - "logs": [], - "result": { - "budget": { - "baseline_net_income": "float:1600000000000.0", - "benefit_spending_impact": "float:1750000000.0", - "budgetary_impact": "float:1500000000.0", - "households": "float:28.0", - "state_tax_revenue_impact": "float:-110000000.0", - "tax_revenue_impact": "float:3250000000.0" - }, - "constituency_impact": { - "by_constituency": { - "Constituency 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "decile": { - "average": { - "int:1": "float:26.09166666666715", - "int:10": "float:9.999999999998181", - "int:2": "float:380.37573281250025", - "int:3": "float:433.33333333333394", - "int:4": "float:53.96875", - "int:5": "float:-162.08333333333334", - "int:6": "float:0.0", - "int:7": "float:-2.7999999999992724", - "int:8": "float:-12.5", - "int:9": "float:-436.25" - }, - "relative": { - "int:1": "float:0.0034444444444445086", - "int:10": "float:0.000999999999999818", - "int:2": "float:0.04738961825968467", - "int:3": "float:0.07046070460704616", - "int:4": "float:0.007010351126446113", - "int:5": "float:-0.014171220400728598", - "int:6": "float:nan", - "int:7": "float:-0.00039999999999989604", - "int:8": "float:0.05", - "int:9": "float:-0.05" - } - }, - "detailed_budget": { - "income_tax": { - "baseline": "float:240000000000.0", - "difference": "float:1500000000.0", - "reform": "float:241500000000.0" - }, - "national_insurance": { - "baseline": "float:160000000000.0", - "difference": "float:-200000000.0", - "reform": "float:159800000000.0" - }, - "universal_credit": { - "baseline": "float:61000000000.0", - "difference": "float:750000000.0", - "reform": "float:61750000000.0" - } - }, - "inequality": { - "gini": { - "baseline": "float:0.4217", - "reform": "float:0.4186" - }, - "top_10_pct_share": { - "baseline": "float:0.3125", - "reform": "float:0.3083" - }, - "top_1_pct_share": { - "baseline": "float:0.1033", - "reform": "float:0.1022" - } - }, - "intra_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.07692307692307693", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.5571428571428572" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:1.0", - "float:1.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:1.0" - ] - } - } - }, - "intra_wealth_decile": { - "all": { - "Gain less than 5%": "float:0.046153846153846156", - "Gain more than 5%": "float:0.1054945054945055", - "Lose less than 5%": "float:0.15384615384615385", - "Lose more than 5%": "float:0.21428571428571427", - "No change": "float:0.4802197802197802" - }, - "deciles": { - "Gain less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.23076923076923078", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Gain more than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.7692307692307693", - "float:0.14285714285714285", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0" - ] - }, - "Lose less than 5%": { - "list": [ - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.7692307692307693", - "float:0.0", - "float:0.7692307692307693", - "float:0.0" - ] - }, - "Lose more than 5%": { - "list": [ - "float:1.0", - "float:1.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.0", - "float:0.14285714285714285", - "float:0.0", - "float:0.0" - ] - }, - "No change": { - "list": [ - "float:0.0", - "float:0.0", - "float:1.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.0", - "float:0.8571428571428571", - "float:0.23076923076923078", - "float:1.0" - ] - } - } - }, - "labor_supply_response": { - "decile": { - "average": { - "income": { - "float:-1.0": "float:3.625", - "float:1.0": "float:1.2083333333333333", - "float:10.0": "float:3.625", - "float:2.0": "float:-2.265625", - "float:3.0": "float:-1.2083333333333333", - "float:4.0": "float:2.265625", - "float:5.0": "float:-2.4166666666666665", - "float:6.0": "float:-3.625", - "float:7.0": "float:3.625", - "float:8.0": "float:0.0", - "float:9.0": "float:-3.625" - }, - "substitution": { - "float:-1.0": "float:-12.5", - "float:1.0": "float:-12.5", - "float:10.0": "float:12.5", - "float:2.0": "float:-6.25", - "float:3.0": "float:0.0", - "float:4.0": "float:6.25", - "float:5.0": "float:12.5", - "float:6.0": "float:-12.5", - "float:7.0": "float:-6.25", - "float:8.0": "float:0.0", - "float:9.0": "float:6.25" - } - }, - "relative": { - "income": { - "int:1": "float:3.598590841462219e-05", - "int:10": "float:0.00010826106596085429", - "int:2": "float:-7.336033330887989e-05", - "int:3": "float:-3.829348390155141e-05", - "int:4": "float:7.522873426357554e-05", - "int:5": "float:-6.1032201852432556e-05", - "int:6": "float:-0.00013174093372522475", - "int:7": "float:0.00011132394885240364", - "int:8": "float:0.0", - "int:9": "float:-0.0001018905976059223" - }, - "substitution": { - "int:1": "float:-0.00037226801808229855", - "int:10": "float:0.00037331402055466997", - "int:2": "float:-0.00020237333326587556", - "int:3": "float:0.0", - "int:4": "float:0.00020752754279607045", - "int:5": "float:0.000315683802684996", - "int:6": "float:-0.00045427908181111987", - "int:7": "float:-0.00019193784284897178", - "int:8": "float:0.0", - "int:9": "float:0.0001756734441481419" - } - } - }, - "hours": { - "baseline": "float:1180000000.0", - "change": "float:2300000.0", - "income_effect": "float:1000000.0", - "reform": "float:1182300000.0", - "substitution_effect": "float:-3000000.0" - }, - "income_lsr": "float:-75000000.0", - "relative_lsr": { - "income": "float64:5.8285236289352835e-06", - "substitution": "float64:-4.01967146823123e-05" - }, - "revenue_change": "float:400000000.0", - "substitution_lsr": "float:125000000.0", - "total_change": "float:50000000.0" - }, - "local_authority_impact": null, - "poverty": { - "deep_poverty": { - "adult": { - "baseline": "float:0.1346153846153846", - "reform": "float:0.1346153846153846" - }, - "all": { - "baseline": "float:0.1523809523809524", - "reform": "float:0.06666666666666667" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.0" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - }, - "poverty": { - "adult": { - "baseline": "float:0.40384615384615385", - "reform": "float:0.40384615384615385" - }, - "all": { - "baseline": "float:0.2857142857142857", - "reform": "float:0.37142857142857144" - }, - "child": { - "baseline": "float:0.28125", - "reform": "float:0.5625" - }, - "senior": { - "baseline": "float:0.0", - "reform": "float:0.0" - } - } - }, - "poverty_by_gender": { - "deep_poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.0" - }, - "male": { - "baseline": "float:0.3018867924528302", - "reform": "float:0.1320754716981132" - } - }, - "poverty": { - "female": { - "baseline": "float:0.0", - "reform": "float:0.09615384615384616" - }, - "male": { - "baseline": "float:0.5660377358490566", - "reform": "float:0.6415094339622641" - } - } - }, - "poverty_by_race": {}, - "wealth_decile": { - "average": { - "int:1": "float:-12.5", - "int:10": "float:-2.7999999999992724", - "int:2": "float:-436.25", - "int:3": "float:9.999999999998181", - "int:4": "float:26.09166666666715", - "int:5": "float:380.37573281250025", - "int:6": "float:433.33333333333394", - "int:7": "float:53.96875", - "int:8": "float:-162.08333333333334", - "int:9": "float:-200.0" - }, - "relative": { - "int:1": "float:0.05", - "int:10": "float:-0.00039999999999989604", - "int:2": "float:-0.05", - "int:3": "float:0.000999999999999818", - "int:4": "float:0.0034444444444445086", - "int:5": "float:0.04738961825968467", - "int:6": "float:0.07046070460704616", - "int:7": "float:0.007010351126446113", - "int:8": "float:-0.014171220400728598", - "int:9": "float:-0.02" - } - } - }, - "result_type": "dict" - }, - "a0f54bfeeaeb5f01": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [ - "Local authority 0", - "Local authority 1", - "Local authority 2", - "Local authority 3", - "Local authority 4", - "Local authority 5", - "Local authority 6", - "Local authority 7" - ], - "/by_local_authority/Local authority 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 3": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 5": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": { - "Local authority 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Local authority 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Local authority 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - }, - "Local authority 3": { - "average_household_income_change": "float:-80.0", - "relative_household_income_change": "float:-0.020000000000000018", - "x": "int:2", - "y": "int:1" - }, - "Local authority 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - }, - "Local authority 5": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:nan", - "x": "int:1", - "y": "int:5" - }, - "Local authority 6": { - "average_household_income_change": "float:189.81651617187526", - "relative_household_income_change": "float:0.029066292702476515", - "x": "int:4", - "y": "int:7" - }, - "Local authority 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:2", - "Lose less than 5%": "int:1", - "Lose more than 5%": "int:2", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "a22d74f249e80a14": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [ - "Constituency 0", - "Constituency 4" - ], - "/by_constituency/Constituency 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": { - "Constituency 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Constituency 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - }, - "b1c4dc99ea923286": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [ - "Local authority 7" - ], - "/by_local_authority/Local authority 7": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": { - "Local authority 7": { - "average_household_income_change": "float:-12.5", - "relative_household_income_change": "float:0.050000000000000044", - "x": "int:0", - "y": "int:9" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "b92910f1c70bc7be": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [ - "Constituency 6" - ], - "/by_constituency/Constituency 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": { - "Constituency 6": { - "average_household_income_change": "float:19.123372416666502", - "relative_household_income_change": "float:0.002298021920688953", - "x": "int:4", - "y": "int:7" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:1", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:1", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - }, - "bca9e3f160f4017e": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [ - "Constituency 4" - ], - "/by_constituency/Constituency 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": { - "Constituency 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - }, - "be770bdebb4bc44e": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [ - "Constituency 1", - "Constituency 6" - ], - "/by_constituency/Constituency 1": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_constituency/Constituency 6": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": { - "Constituency 1": { - "average_household_income_change": "float:37.25", - "relative_household_income_change": "float:0.020000000000000018", - "x": "int:3", - "y": "int:-3" - }, - "Constituency 6": { - "average_household_income_change": "float:19.123372416666502", - "relative_household_income_change": "float:0.002298021920688953", - "x": "int:4", - "y": "int:7" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:2", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - }, - "d51953975fb01911": { - "dataset_calls": [], - "key_order": { - "": [ - "baseline", - "reform" - ], - "/baseline": [ - "cliff_gap", - "cliff_share" - ], - "/reform": [ - "cliff_gap", - "cliff_share" - ] - }, - "logs": [], - "result": { - "baseline": { - "cliff_gap": "float:2062500000.0", - "cliff_share": "float:0.0457" - }, - "reform": { - "cliff_gap": "float:1875000000.0", - "cliff_share": "float:0.0421" - } - }, - "result_type": "dict" - }, - "d86811df62ca768c": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": {}, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - }, - "df9703bc15aed219": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data-private", - "local_authority_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data-private/local_authority_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "local_authorities_2021.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/local_authorities_2021.csv" - ] - ], - "key_order": { - "": [ - "by_local_authority", - "outcomes_by_region" - ], - "/by_local_authority": [ - "Local authority 0", - "Local authority 4" - ], - "/by_local_authority/Local authority 0": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/by_local_authority/Local authority 4": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_local_authority": { - "Local authority 0": { - "average_household_income_change": "float:100.0", - "relative_household_income_change": "float:0.10000000000000009", - "x": "int:0", - "y": "int:-5" - }, - "Local authority 4": { - "average_household_income_change": "float:-486.25", - "relative_household_income_change": "float:-0.09999999999999998", - "x": "int:5", - "y": "int:3" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:1", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:1", - "No change": "int:0" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - } - } - }, - "model": "UKLocalAuthorityBreakdown" - }, - "result_type": "UKLocalAuthorityBreakdown" - }, - "ea0cb06718c2a3ee": { - "dataset_calls": [ - [ - "download", - "policyengine/policyengine-uk-data", - "parliamentary_constituency_weights.h5" - ], - [ - "h5py.File", - "/fake/policyengine/policyengine-uk-data/parliamentary_constituency_weights.h5", - "r" - ], - [ - "download", - "policyengine/policyengine-uk-data-public", - "constituencies_2024.csv" - ], - [ - "read_csv", - "/fake/policyengine/policyengine-uk-data-public/constituencies_2024.csv" - ] - ], - "key_order": { - "": [ - "by_constituency", - "outcomes_by_region" - ], - "/by_constituency": [ - "Constituency 2" - ], - "/by_constituency/Constituency 2": [ - "average_household_income_change", - "relative_household_income_change", - "x", - "y" - ], - "/outcomes_by_region": [ - "uk", - "england", - "scotland", - "wales", - "northern_ireland" - ], - "/outcomes_by_region/england": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/northern_ireland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/scotland": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/uk": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ], - "/outcomes_by_region/wales": [ - "Gain more than 5%", - "Gain less than 5%", - "No change", - "Lose less than 5%", - "Lose more than 5%" - ] - }, - "logs": [], - "result": { - "fields": { - "by_constituency": { - "Constituency 2": { - "average_household_income_change": "float:0.0", - "relative_household_income_change": "float:0.0", - "x": "int:6", - "y": "int:-1" - } - }, - "outcomes_by_region": { - "england": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "northern_ireland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "scotland": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:0" - }, - "uk": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - }, - "wales": { - "Gain less than 5%": "int:0", - "Gain more than 5%": "int:0", - "Lose less than 5%": "int:0", - "Lose more than 5%": "int:0", - "No change": "int:1" - } - } - }, - "model": "UKConstituencyBreakdown" - }, - "result_type": "UKConstituencyBreakdown" - } - } -} diff --git a/tests/unit/routes/test_flask_route_boundaries.py b/tests/unit/routes/test_flask_route_boundaries.py index 2251d46d2..a9b5ccac5 100644 --- a/tests/unit/routes/test_flask_route_boundaries.py +++ b/tests/unit/routes/test_flask_route_boundaries.py @@ -63,15 +63,6 @@ def test_legacy_endpoints_package_is_removed(): assert not any((PACKAGE_ROOT / "endpoints").rglob("*.py")) -def test_economy_comparison_logic_is_not_in_the_http_layer(): - comparison_module = PACKAGE_ROOT / "services/economy_comparison.py" - - assert comparison_module.exists() - source = comparison_module.read_text(encoding="utf-8") - assert "from flask" not in source - assert "import flask" not in source - - def test_calculation_route_delegates_domain_processing_to_its_service(): source = (PACKAGE_ROOT / "routes/household_routes.py").read_text(encoding="utf-8") diff --git a/tests/unit/services/test_economy_comparison.py b/tests/unit/services/test_economy_comparison.py deleted file mode 100644 index ce9bde4b6..000000000 --- a/tests/unit/services/test_economy_comparison.py +++ /dev/null @@ -1,1003 +0,0 @@ -from unittest.mock import MagicMock, patch - -import numpy as np -import pandas as pd -import pytest -from pydantic import ValidationError - -from policyengine_api.services.economy_comparison import ( - UKConstituencyBreakdown, - UKConstituencyBreakdownByConstituency, - UKLocalAuthorityBreakdown, - UKLocalAuthorityBreakdownByLA, - compute_income_change, - intra_decile_impact, - intra_wealth_decile_impact, - uk_constituency_breakdown, - uk_local_authority_breakdown, -) - - -class TestUKLocalAuthorityBreakdownByLA: - """Tests for the UKLocalAuthorityBreakdownByLA Pydantic model.""" - - def test__given_valid_data__creates_instance(self): - breakdown = UKLocalAuthorityBreakdownByLA( - average_household_income_change=100.50, - relative_household_income_change=0.05, - x=10, - y=20, - ) - assert breakdown.average_household_income_change == 100.50 - assert breakdown.relative_household_income_change == 0.05 - assert breakdown.x == 10 - assert breakdown.y == 20 - - def test__given_negative_income_change__creates_instance(self): - breakdown = UKLocalAuthorityBreakdownByLA( - average_household_income_change=-500.0, - relative_household_income_change=-0.03, - x=5, - y=-10, - ) - assert breakdown.average_household_income_change == -500.0 - assert breakdown.relative_household_income_change == -0.03 - - def test__given_zero_values__creates_instance(self): - breakdown = UKLocalAuthorityBreakdownByLA( - average_household_income_change=0.0, - relative_household_income_change=0.0, - x=0, - y=0, - ) - assert breakdown.average_household_income_change == 0.0 - assert breakdown.relative_household_income_change == 0.0 - - def test__given_missing_field__raises_validation_error(self): - with pytest.raises(ValidationError): - UKLocalAuthorityBreakdownByLA( - average_household_income_change=100.0, - # Missing relative_household_income_change - x=10, - y=20, - ) - - -class TestUKLocalAuthorityBreakdown: - """Tests for the UKLocalAuthorityBreakdown Pydantic model.""" - - def test__given_valid_data__creates_instance(self): - breakdown = UKLocalAuthorityBreakdown( - by_local_authority={ - "Hartlepool": UKLocalAuthorityBreakdownByLA( - average_household_income_change=100.0, - relative_household_income_change=0.02, - x=8, - y=19, - ) - }, - outcomes_by_region={ - "uk": {"Gain more than 5%": 1, "No change": 0}, - "england": {"Gain more than 5%": 1, "No change": 0}, - }, - ) - assert "Hartlepool" in breakdown.by_local_authority - assert "uk" in breakdown.outcomes_by_region - - def test__given_empty_by_local_authority__creates_instance(self): - breakdown = UKLocalAuthorityBreakdown( - by_local_authority={}, - outcomes_by_region={ - "uk": {"No change": 0}, - }, - ) - assert len(breakdown.by_local_authority) == 0 - - def test__model_dump_returns_dict(self): - breakdown = UKLocalAuthorityBreakdown( - by_local_authority={ - "Leicester": UKLocalAuthorityBreakdownByLA( - average_household_income_change=50.0, - relative_household_income_change=0.01, - x=8, - y=8, - ) - }, - outcomes_by_region={"uk": {"No change": 1}}, - ) - result = breakdown.model_dump() - assert isinstance(result, dict) - assert "by_local_authority" in result - assert "outcomes_by_region" in result - - -class TestUKLocalAuthorityBreakdownFunction: - """Tests for the uk_local_authority_breakdown function.""" - - def test__given_non_uk_country__returns_none(self): - result = uk_local_authority_breakdown({}, {}, "us") - assert result is None - - def test__given_non_uk_country_canada__returns_none(self): - result = uk_local_authority_breakdown({}, {}, "ca") - assert result is None - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_uk_country__returns_breakdown( - self, mock_read_csv, mock_h5py_file, mock_download - ): - # Setup mocks - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - # Create mock weights - 3 local authorities, 10 households - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - # Create mock local authority names DataFrame - mock_la_df = pd.DataFrame( - { - "code": ["E06000001", "S12000033", "W06000001"], - "name": ["Hartlepool", "Aberdeen City", "Isle of Anglesey"], - "x": [8.0, 5.0, 3.0], - "y": [19.0, 10.0, 15.0], - } - ) - mock_read_csv.return_value = mock_la_df - - # Create baseline and reform data - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_local_authority_breakdown(baseline, reform, "uk") - - assert result is not None - assert isinstance(result, UKLocalAuthorityBreakdown) - assert "Hartlepool" in result.by_local_authority - assert "Aberdeen City" in result.by_local_authority - assert "Isle of Anglesey" in result.by_local_authority - - def test__region_categorization_by_code_prefix(self): - """Test that region categorization identifies UK nations by code prefix.""" - # This is a unit test for the region categorization logic - # We test the logic directly rather than through the full function - - test_cases = [ - ("E06000001", ["uk", "england"]), # English LA - ("S12000033", ["uk", "scotland"]), # Scottish LA - ("W06000001", ["uk", "wales"]), # Welsh LA - ("N09000001", ["uk", "northern_ireland"]), # NI LA - ] - - for code, expected_regions in test_cases: - regions = ["uk"] - if code.startswith("E"): - regions.append("england") - elif code.startswith("S"): - regions.append("scotland") - elif code.startswith("W"): - regions.append("wales") - elif code.startswith("N"): - regions.append("northern_ireland") - - assert regions == expected_regions, f"Failed for code {code}" - - def test__outcome_bucket_categorization_logic(self): - """Test that outcome bucket categorization logic is correct.""" - # Thresholds: > 0.05 (5%), > 0.001 (0.1%), > -0.001, > -0.05 - test_cases = [ - (0.10, "Gain more than 5%"), # 10% gain - (0.06, "Gain more than 5%"), # 6% gain - (0.051, "Gain more than 5%"), # Just over 5% - (0.05, "Gain less than 5%"), # Exactly 5% gain (not > 5%) - (0.03, "Gain less than 5%"), # 3% gain - (0.002, "Gain less than 5%"), # 0.2% gain (> 0.001) - (0.001, "No change"), # Exactly 0.1% - not > 0.001 - (0.0005, "No change"), # 0.05% gain (within tolerance) - (0.0, "No change"), # No change - (-0.0005, "No change"), # 0.05% loss (> -0.001) - (-0.001, "Lose less than 5%"), # Exactly -0.1% (not > -0.001) - (-0.002, "Lose less than 5%"), # 0.2% loss - (-0.03, "Lose less than 5%"), # 3% loss - (-0.049, "Lose less than 5%"), # Just under 5% loss (> -0.05) - (-0.05, "Lose more than 5%"), # Exactly 5% loss (not > -0.05) - (-0.051, "Lose more than 5%"), # Just over 5% loss - (-0.06, "Lose more than 5%"), # 6% loss - (-0.10, "Lose more than 5%"), # 10% loss - ] - - for percent_change, expected_bucket in test_cases: - if percent_change > 0.05: - bucket = "Gain more than 5%" - elif percent_change > 1e-3: - bucket = "Gain less than 5%" - elif percent_change > -1e-3: - bucket = "No change" - elif percent_change > -0.05: - bucket = "Lose less than 5%" - else: - bucket = "Lose more than 5%" - - assert bucket == expected_bucket, ( - f"Failed for {percent_change}: expected {expected_bucket}, got {bucket}" - ) - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__outcome_buckets_are_correct( - self, mock_read_csv, mock_h5py_file, mock_download - ): - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((1, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_la_df = pd.DataFrame( - { - "code": ["E06000001"], - "name": ["Hartlepool"], - "x": [8.0], - "y": [19.0], - } - ) - mock_read_csv.return_value = mock_la_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - # 10% gain - should be "Gain more than 5%" - reform = {"household_net_income": np.array([1100.0] * 10)} - - result = uk_local_authority_breakdown(baseline, reform, "uk") - - assert result.outcomes_by_region["uk"]["Gain more than 5%"] == 1 - assert result.outcomes_by_region["uk"]["Gain less than 5%"] == 0 - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__downloads_from_correct_repos( - self, mock_read_csv, mock_h5py_file, mock_download - ): - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((1, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_la_df = pd.DataFrame( - { - "code": ["E06000001"], - "name": ["Test"], - "x": [0.0], - "y": [0.0], - } - ) - mock_read_csv.return_value = mock_la_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1000.0] * 10)} - - uk_local_authority_breakdown(baseline, reform, "uk") - - # Verify correct repos are used - calls = mock_download.call_args_list - assert calls[0][1]["repo"] == "policyengine/policyengine-uk-data-private" - assert calls[0][1]["repo_filename"] == "local_authority_weights.h5" - assert calls[1][1]["repo"] == "policyengine/policyengine-uk-data-public" - assert calls[1][1]["repo_filename"] == "local_authorities_2021.csv" - - def test__given_constituency_region__returns_none(self): - """Skip the local authority breakdown when simulating a constituency.""" - result = uk_local_authority_breakdown({}, {}, "uk", "constituency/Aldershot") - assert result is None - - def test__given_constituency_region_with_code__returns_none(self): - """Skip the local authority breakdown for a constituency given by code.""" - result = uk_local_authority_breakdown({}, {}, "uk", "constituency/E12345678") - assert result is None - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_specific_la_region__returns_only_that_la( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """Return only that LA when simulating a specific local authority.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_la_df = pd.DataFrame( - { - "code": ["E06000001", "S12000033", "W06000001"], - "name": ["Hartlepool", "Aberdeen City", "Isle of Anglesey"], - "x": [8.0, 5.0, 3.0], - "y": [19.0, 10.0, 15.0], - } - ) - mock_read_csv.return_value = mock_la_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_local_authority_breakdown( - baseline, reform, "uk", "local_authority/Hartlepool" - ) - - assert result is not None - assert len(result.by_local_authority) == 1 - assert "Hartlepool" in result.by_local_authority - assert "Aberdeen City" not in result.by_local_authority - assert "Isle of Anglesey" not in result.by_local_authority - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_country_scotland_region__returns_only_scottish_las( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """Return only Scottish local authorities when simulating country/scotland.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_la_df = pd.DataFrame( - { - "code": ["E06000001", "S12000033", "W06000001"], - "name": ["Hartlepool", "Aberdeen City", "Isle of Anglesey"], - "x": [8.0, 5.0, 3.0], - "y": [19.0, 10.0, 15.0], - } - ) - mock_read_csv.return_value = mock_la_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_local_authority_breakdown( - baseline, reform, "uk", "country/scotland" - ) - - assert result is not None - assert len(result.by_local_authority) == 1 - assert "Aberdeen City" in result.by_local_authority - assert "Hartlepool" not in result.by_local_authority - assert "Isle of Anglesey" not in result.by_local_authority - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_uk_region__returns_all_las( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """When simulating uk-wide, all local authorities should be returned.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_la_df = pd.DataFrame( - { - "code": ["E06000001", "S12000033", "W06000001"], - "name": ["Hartlepool", "Aberdeen City", "Isle of Anglesey"], - "x": [8.0, 5.0, 3.0], - "y": [19.0, 10.0, 15.0], - } - ) - mock_read_csv.return_value = mock_la_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_local_authority_breakdown(baseline, reform, "uk", "uk") - - assert result is not None - assert len(result.by_local_authority) == 3 - assert "Hartlepool" in result.by_local_authority - assert "Aberdeen City" in result.by_local_authority - assert "Isle of Anglesey" in result.by_local_authority - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_no_region__returns_all_las( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """When no region specified (None), all local authorities should be returned.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_la_df = pd.DataFrame( - { - "code": ["E06000001", "S12000033", "W06000001"], - "name": ["Hartlepool", "Aberdeen City", "Isle of Anglesey"], - "x": [8.0, 5.0, 3.0], - "y": [19.0, 10.0, 15.0], - } - ) - mock_read_csv.return_value = mock_la_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_local_authority_breakdown(baseline, reform, "uk", None) - - assert result is not None - assert len(result.by_local_authority) == 3 - - -class TestUKConstituencyBreakdownModels: - """Tests for the existing UK constituency breakdown models (for completeness).""" - - def test__constituency_breakdown_by_constituency_creates_instance(self): - breakdown = UKConstituencyBreakdownByConstituency( - average_household_income_change=200.0, - relative_household_income_change=0.04, - x=56, - y=-40, - ) - assert breakdown.average_household_income_change == 200.0 - assert breakdown.x == 56 - - def test__constituency_breakdown_creates_instance(self): - breakdown = UKConstituencyBreakdown( - by_constituency={ - "Aldershot": UKConstituencyBreakdownByConstituency( - average_household_income_change=150.0, - relative_household_income_change=0.03, - x=56, - y=-40, - ) - }, - outcomes_by_region={"uk": {"No change": 1}}, - ) - assert "Aldershot" in breakdown.by_constituency - - -class TestUKConstituencyBreakdownFunction: - """Tests for the uk_constituency_breakdown function.""" - - def test__given_non_uk_country__returns_none(self): - result = uk_constituency_breakdown({}, {}, "us") - assert result is None - - def test__given_non_uk_country_nigeria__returns_none(self): - result = uk_constituency_breakdown({}, {}, "ng") - assert result is None - - def test__given_local_authority_region__returns_none(self): - """Skip the constituency breakdown when simulating a local authority.""" - result = uk_constituency_breakdown({}, {}, "uk", "local_authority/Leicester") - assert result is None - - def test__given_local_authority_region_with_code__returns_none(self): - """Skip the constituency breakdown for a local authority given by code.""" - result = uk_constituency_breakdown({}, {}, "uk", "local_authority/E06000016") - assert result is None - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_specific_constituency_region__returns_only_that_constituency( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """Return only that constituency when simulating a specific constituency.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - # Create mock weights - 3 constituencies, 10 households - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - # Create mock constituency names DataFrame - mock_const_df = pd.DataFrame( - { - "code": ["E12345678", "S12345678", "W12345678"], - "name": ["Aldershot", "Edinburgh East", "Cardiff South"], - "x": [10.0, 5.0, 3.0], - "y": [20.0, 15.0, 12.0], - } - ) - mock_read_csv.return_value = mock_const_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_constituency_breakdown( - baseline, reform, "uk", "constituency/Aldershot" - ) - - assert result is not None - assert len(result.by_constituency) == 1 - assert "Aldershot" in result.by_constituency - assert "Edinburgh East" not in result.by_constituency - assert "Cardiff South" not in result.by_constituency - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_country_scotland_region__returns_only_scottish_constituencies( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """Return only Scottish constituencies when simulating country/scotland.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_const_df = pd.DataFrame( - { - "code": ["E12345678", "S12345678", "W12345678"], - "name": ["Aldershot", "Edinburgh East", "Cardiff South"], - "x": [10.0, 5.0, 3.0], - "y": [20.0, 15.0, 12.0], - } - ) - mock_read_csv.return_value = mock_const_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_constituency_breakdown(baseline, reform, "uk", "country/scotland") - - assert result is not None - assert len(result.by_constituency) == 1 - assert "Edinburgh East" in result.by_constituency - assert "Aldershot" not in result.by_constituency - assert "Cardiff South" not in result.by_constituency - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_uk_region__returns_all_constituencies( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """When simulating uk-wide, all constituencies should be returned.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_const_df = pd.DataFrame( - { - "code": ["E12345678", "S12345678", "W12345678"], - "name": ["Aldershot", "Edinburgh East", "Cardiff South"], - "x": [10.0, 5.0, 3.0], - "y": [20.0, 15.0, 12.0], - } - ) - mock_read_csv.return_value = mock_const_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_constituency_breakdown(baseline, reform, "uk", "uk") - - assert result is not None - assert len(result.by_constituency) == 3 - assert "Aldershot" in result.by_constituency - assert "Edinburgh East" in result.by_constituency - assert "Cardiff South" in result.by_constituency - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__given_no_region__returns_all_constituencies( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """When no region specified (None), all constituencies should be returned.""" - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - mock_const_df = pd.DataFrame( - { - "code": ["E12345678", "S12345678", "W12345678"], - "name": ["Aldershot", "Edinburgh East", "Cardiff South"], - "x": [10.0, 5.0, 3.0], - "y": [20.0, 15.0, 12.0], - } - ) - mock_read_csv.return_value = mock_const_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_constituency_breakdown(baseline, reform, "uk", None) - - assert result is not None - assert len(result.by_constituency) == 3 - - @patch("policyengine_api.services.economy_comparison.download_huggingface_dataset") - @patch("policyengine_api.services.economy_comparison.h5py.File") - @patch("policyengine_api.services.economy_comparison.pd.read_csv") - def test__country_filter_uses_prefix_not_substring( - self, mock_read_csv, mock_h5py_file, mock_download - ): - """Regression for issue #3453. - - Previously the filter used `"E" not in code`, so a Welsh - code containing any 'E' (e.g. "W12345E") or a Scottish code - containing 'W' would leak into the wrong country bucket. - Use startswith on the leading country-letter instead. - """ - mock_download.side_effect = [ - "/path/to/weights.h5", - "/path/to/names.csv", - ] - - mock_weights = np.ones((3, 10)) - mock_h5py_context = MagicMock() - mock_h5py_context.__enter__ = MagicMock(return_value={"2025": mock_weights}) - mock_h5py_context.__exit__ = MagicMock(return_value=False) - mock_h5py_file.return_value = mock_h5py_context - - # Welsh code "W12345E7" happens to contain 'E' in its tail. - mock_const_df = pd.DataFrame( - { - "code": ["W12345E7", "E12345678", "S12345678"], - "name": ["Cardiff Trap", "Aldershot", "Edinburgh East"], - "x": [10.0, 5.0, 3.0], - "y": [20.0, 15.0, 12.0], - } - ) - mock_read_csv.return_value = mock_const_df - - baseline = {"household_net_income": np.array([1000.0] * 10)} - reform = {"household_net_income": np.array([1050.0] * 10)} - - result = uk_constituency_breakdown(baseline, reform, "uk", "country/england") - - assert result is not None - # Only Aldershot (code starting with 'E') should pass the - # England filter; the Welsh trap code must be excluded. - assert "Aldershot" in result.by_constituency - assert "Cardiff Trap" not in result.by_constituency - assert "Edinburgh East" not in result.by_constituency - - # The Welsh trap code must also not be double-counted in the - # England regional bucket. - england_total = sum(result.outcomes_by_region["england"].values()) - assert england_total == 1 - - -def _make_economy( - incomes, - deciles, - weights=None, - people=None, - decile_key="household_income_decile", -): - """Build a baseline or reform economy dict for the intra-decile tests.""" - n = len(incomes) - return { - "household_net_income": np.array(incomes, dtype=float), - "household_weight": np.array(weights or [1.0] * n), - "household_count_people": np.array(people or [1.0] * n), - decile_key: np.array(deciles, dtype=float), - } - - -class TestComputeIncomeChange: - """Direct unit tests for the income change formula.""" - - def test__income_change_formula_exact(self): - result = compute_income_change(np.array([1000.0]), np.array([1040.0])) - assert result[0] == pytest.approx(0.04) - - -class TestIntraDecileImpact: - """Tests for intra_decile_impact. - - They verify the percentage change calculation and the bucket assignment. - """ - - def test__5pct_gain_classified_below_5pct(self): - """A uniform 5% income gain must NOT land in 'Gain more than 5%'. - - This is the regression test for the double-counting bug where - income_change was 2x the true value, pushing 5% gains into the - >5% bucket. - """ - # 10 households, one per decile, all gain exactly 5% - baseline = _make_economy( - incomes=[1000.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[1050.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - # Every decile should have 0% in "Gain more than 5%" - for pct in result["deciles"]["Gain more than 5%"]: - assert pct == 0.0, f"5% gain incorrectly classified as >5% (got {pct})" - # Every decile should have 100% in "Gain less than 5%" - for pct in result["deciles"]["Gain less than 5%"]: - assert pct == 1.0, f"5% gain not classified as <5% (got {pct})" - - def test__10pct_gain_classified_above_5pct(self): - """A 10% gain should be in 'Gain more than 5%'.""" - baseline = _make_economy( - incomes=[1000.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[1100.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - for pct in result["deciles"]["Gain more than 5%"]: - assert pct == 1.0 - - def test__3pct_loss_classified_below_5pct(self): - """A 3% loss should be in 'Lose less than 5%'.""" - baseline = _make_economy( - incomes=[1000.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[970.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - for pct in result["deciles"]["Lose less than 5%"]: - assert pct == 1.0 - for pct in result["deciles"]["Lose more than 5%"]: - assert pct == 0.0 - - def test__no_change_classified_correctly(self): - """Zero change should land in 'No change'.""" - baseline = _make_economy( - incomes=[1000.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[1000.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - for pct in result["deciles"]["No change"]: - assert pct == 1.0 - - def test__near_zero_baseline_no_division_error(self): - """Avoid division errors for households with zero baseline income. - - The floor of 1 on the denominator handles this. - """ - baseline = _make_economy( - incomes=[0.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[100.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - # Should not raise; all households gained income - total = sum(result["all"][label] for label in result["all"]) - assert abs(total - 1.0) < 1e-9, f"Proportions should sum to 1, got {total}" - - def test__zero_baseline_uses_floor_of_one(self): - """Divide by the floor of 1 when baseline income is 0. - - The max(B, 1) floor makes the effective denominator 1, so a $0 -> $100 - change gives income_change = 100/1 = 100 (10000%), landing in >5%. - """ - baseline = _make_economy( - incomes=[0.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[100.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - # $100 gain on a floored baseline of $1 = 10000% change -> >5% - for pct in result["deciles"]["Gain more than 5%"]: - assert pct == 1.0, f"Zero baseline with $100 gain should be >5% (got {pct})" - # No NaN or Inf in any bucket - for label in result["all"]: - assert not np.isnan(result["all"][label]) - assert not np.isinf(result["all"][label]) - - def test__negative_baseline_handled(self): - """Handle negative baseline income without producing NaN or Inf. - - The max(B, 1) floor applies to negative incomes too. - """ - baseline = _make_economy( - incomes=[-500.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[500.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - for label in result["all"]: - assert not np.isnan(result["all"][label]) - assert not np.isinf(result["all"][label]) - - def test__2pct_gain_is_not_doubled(self): - """Keep a 2% gain in the <5% bucket. - - A 2% gain stays in <5% even if doubled (2*2%=4% < 5%), so this test - alone would not catch the doubling bug. - """ - baseline = _make_economy( - incomes=[50000.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[51000.0] * 10, # +2% - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - for pct in result["deciles"]["Gain more than 5%"]: - assert pct == 0.0, "2% gain incorrectly classified as >5%" - for pct in result["deciles"]["Gain less than 5%"]: - assert pct == 1.0, "2% gain not classified as <5%" - - def test__4pct_gain_not_doubled_into_above_5pct(self): - """Keep a 4% gain in the <5% bucket. - - With the doubling bug, 4% * 2 = 8% would incorrectly land in >5%. This - is the tightest regression test for the doubling bug on the gain side. - """ - baseline = _make_economy( - incomes=[10000.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[10400.0] * 10, # +4% - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - for pct in result["deciles"]["Gain more than 5%"]: - assert pct == 0.0, "4% gain incorrectly classified as >5% (doubling bug)" - for pct in result["deciles"]["Gain less than 5%"]: - assert pct == 1.0, "4% gain not classified as <5%" - - def test__all_field_averages_deciles(self): - """The 'all' field should be the mean of the 10 decile values.""" - baseline = _make_economy( - incomes=[1000.0] * 10, - deciles=list(range(1, 11)), - ) - reform = _make_economy( - incomes=[1050.0] * 10, - deciles=list(range(1, 11)), - ) - result = intra_decile_impact(baseline, reform) - - for label in result["all"]: - expected = sum(result["deciles"][label]) / 10 - assert abs(result["all"][label] - expected) < 1e-9 - - -class TestIntraWealthDecileImpact: - """Tests for intra_wealth_decile_impact. - - It uses the same formula as intra_decile_impact, keyed by wealth decile - instead of income decile. - """ - - def test__5pct_gain_classified_below_5pct(self): - """Regression test: 5% gain must not be doubled into >5% bucket.""" - baseline = _make_economy( - incomes=[1000.0] * 10, - deciles=list(range(1, 11)), - decile_key="household_wealth_decile", - ) - reform = _make_economy( - incomes=[1050.0] * 10, - deciles=list(range(1, 11)), - decile_key="household_wealth_decile", - ) - - result = intra_wealth_decile_impact(baseline, reform) - - for pct in result["deciles"]["Gain more than 5%"]: - assert pct == 0.0, ( - f"5% gain incorrectly classified as >5% in wealth decile (got {pct})" - ) - - def test__2pct_gain_not_doubled(self): - """A 2% gain must stay in the <5% bucket for wealth deciles too.""" - baseline = _make_economy( - incomes=[50000.0] * 10, - deciles=list(range(1, 11)), - decile_key="household_wealth_decile", - ) - reform = _make_economy( - incomes=[51000.0] * 10, - deciles=list(range(1, 11)), - decile_key="household_wealth_decile", - ) - - result = intra_wealth_decile_impact(baseline, reform) - - for pct in result["deciles"]["Gain more than 5%"]: - assert pct == 0.0, "2% gain incorrectly classified as >5%" - for pct in result["deciles"]["Gain less than 5%"]: - assert pct == 1.0, "2% gain not classified as <5%" diff --git a/tests/unit/services/test_economy_comparison_snapshots.py b/tests/unit/services/test_economy_comparison_snapshots.py deleted file mode 100644 index 27a6e6e2b..000000000 --- a/tests/unit/services/test_economy_comparison_snapshots.py +++ /dev/null @@ -1,567 +0,0 @@ -"""Characterization snapshots of the published economy comparison outputs. - -``compare_economic_outputs`` and the UK area breakdowns build the economy impact -results the Public API returns and caches, so these tests pin their complete -output: every key in insertion order, every value with its type, and every float -bit for bit (by ``repr``). The inputs exercise the UK and US paths and each -branch of the comparison: budgets, deciles, poverty, inequality, labour supply, -wealth deciles (including their logged failure), cliffs, and the constituency and -local-authority breakdowns with every region filter and income-change bucket. - -The expected values live in ``tests/data/economy_comparison_snapshots.json``. To -regenerate them after an intended change to the published output, run this module -with ``ECONOMY_COMPARISON_SNAPSHOTS=update`` and review the diff. -""" - -import hashlib -import json -import logging -import math -import os -from collections.abc import Callable, Iterator -from pathlib import Path -from typing import Any -from unittest.mock import patch - -import numpy as np -import pandas as pd -import pytest -from pydantic import BaseModel - -from policyengine_api.services import economy_comparison -from policyengine_api.services.economy_comparison import ( - compare_economic_outputs, - uk_constituency_breakdown, - uk_local_authority_breakdown, -) - -SNAPSHOT_PATH = ( - Path(__file__).resolve().parents[2] / "data" / "economy_comparison_snapshots.json" -) -UPDATE_SNAPSHOTS = os.environ.get("ECONOMY_COMPARISON_SNAPSHOTS") == "update" - -HOUSEHOLD_COUNT = 16 -PERSON_COUNT = 18 - -# Reform-to-baseline income ratios chosen to land households (and areas made of -# one household) in each income-change bucket, on and around its boundaries. -INCOME_CHANGE_FACTORS = [ - 1.1, - 1.02, - 1.0, - 0.98, - 0.9, - 1.0004, - 0.9996, - 1.05, - 0.95, - 1.001, - 0.999, - 1.0500001, -] - -# One area per nation prefix and income-change bucket, one with an unknown prefix -# and a zero-income household (a NaN relative change), and a blended area. -AREA_CODES = [ - "E14000530", - "S14000001", - "W07000041", - "N06000001", - "E14000531", - "K99999999", - "S14000002", - "N06000002", -] - - -def _household_values(build: Callable[[int], Any]) -> list: - return [build(index) for index in range(HOUSEHOLD_COUNT)] - - -def _person_values(build: Callable[[int], Any]) -> list: - return [build(index) for index in range(PERSON_COUNT)] - - -def _baseline_incomes() -> list[float]: - incomes = _household_values( - lambda index: 1000.0 * (index + 1) - (index % 3) * 137.5 - ) - incomes[5] = 0.0 - incomes[7] = -250.0 - return incomes - - -def _reform_incomes() -> list[float]: - return [ - income * INCOME_CHANGE_FACTORS[index % len(INCOME_CHANGE_FACTORS)] - for index, income in enumerate(_baseline_incomes()) - ] - - -def _income_deciles() -> list[float]: - deciles = _household_values(lambda index: float(index % 10 + 1)) - deciles[15] = -1.0 - return deciles - - -def _economy( - *, - country_id: str, - side: str, - include_wealth: bool, -) -> dict[str, Any]: - """Build one side of a general economy with household and person arrays.""" - is_reform = side == "reform" - shift = 1 if is_reform else 0 - economy: dict[str, Any] = { - "type": "general", - "total_tax": 4.2e11 + shift * 3.25e9, - "total_state_tax": 7.5e10 - shift * 1.1e8, - "total_benefits": 2.9e11 + shift * 1.75e9, - "total_net_income": 1.6e12 + shift * 2.5e9, - "gini": 0.4217 - shift * 0.0031, - "top_10_percent_share": 0.3125 - shift * 0.0042, - "top_1_percent_share": 0.1033 - shift * 0.0011, - "substitution_lsr": 1.25e8 * shift - 3.0e7, - "income_lsr": -7.5e7 * shift + 1.0e7, - "budgetary_impact_lsr": 4.0e8 * shift + 2.5e7, - "weekly_hours": 1.18e9 + shift * 2.3e6, - "weekly_hours_income_effect": 1.0e6 * shift - 2.0e5, - "weekly_hours_substitution_effect": -3.0e6 * shift + 4.0e5, - "household_net_income": _reform_incomes() if is_reform else _baseline_incomes(), - "household_weight": _household_values(lambda index: 1.0 + (index % 4) * 0.5), - "household_income_decile": _income_deciles(), - "household_count_people": _household_values(lambda index: float(index % 4 + 1)), - "substitution_lsr_hh": _household_values( - lambda index: (index % 5 - 2) * 12.5 * (1.5 if is_reform else 1.0) - ), - "income_lsr_hh": _household_values( - lambda index: (index % 3 - 1) * 7.25 * (0.5 if is_reform else 1.0) - ), - "employment_income_hh": _household_values( - lambda index: 20000.0 + index * 1500.0 - ), - "self_employment_income_hh": _household_values( - lambda index: 0.0 if index % 2 else 3500.0 + index * 10.0 - ), - "person_in_poverty": _person_values( - lambda index: (index % 4 == 0) or (is_reform and index % 9 == 1) - ), - "person_in_deep_poverty": _person_values( - lambda index: index % 8 == 0 and not (is_reform and index % 16 == 0) - ), - "person_weight": _person_values(lambda index: 1.0 + (index % 5) * 0.25), - "age": _person_values( - lambda index: [3, 17, 18, 30, 64, 65, 80, 12, 45][index % 9] - ), - "is_male": _person_values(lambda index: index % 2 == 0), - "race": ( - _person_values( - lambda index: ["WHITE", "BLACK", "HISPANIC", "OTHER"][index % 4] - ) - if country_id == "us" - else None - ), - } - if country_id == "uk": - economy["programs"] = { - "income_tax": 2.4e11 + shift * 1.5e9, - "national_insurance": 1.6e11 - shift * 2.0e8, - "universal_credit": 6.1e10 + shift * 7.5e8, - } - if include_wealth: - economy["household_wealth_decile"] = _household_values( - lambda index: float((index + 3) % 10 + 1) - ) - return economy - - -def _numpy_economy(economy: dict[str, Any]) -> dict[str, Any]: - """Return the economy with every list replaced by a numpy array.""" - return { - key: np.array(value) if isinstance(value, list) else value - for key, value in economy.items() - } - - -def _cliff_economy(*, side: str) -> dict[str, Any]: - is_reform = side == "reform" - return { - "type": "cliff", - "cliff_gap": 1.875e9 if is_reform else 2.0625e9, - "cliff_share": 0.0421 if is_reform else 0.0457, - } - - -def _area_names(prefix: str) -> pd.DataFrame: - """Return an area table (code, name, x, y) in the shape of the UK datasets.""" - return pd.DataFrame( - { - "code": AREA_CODES, - "name": [f"{prefix} {index}" for index in range(len(AREA_CODES))], - "x": [float(index * 3 % 7) for index in range(len(AREA_CODES))], - "y": [index * 2 - 5 for index in range(len(AREA_CODES))], - } - ) - - -def _area_weights(seed_offset: int) -> np.ndarray: - """Return area-by-household weights: one household per area, then blends.""" - weights = np.zeros((len(AREA_CODES), HOUSEHOLD_COUNT)) - for area_index in range(len(AREA_CODES)): - weights[area_index, area_index] = 1.0 - weights[6] = [ - ((index + seed_offset) % 5) * 0.75 for index in range(HOUSEHOLD_COUNT) - ] - weights[7, 7] = 2.0 - return weights - - -class FakeUKDatasets: - """Stand in for the Hugging Face downloads, the HDF5 weights and the CSVs.""" - - def __init__(self) -> None: - self.calls: list[list[Any]] = [] - self._weights = { - "parliamentary_constituency_weights.h5": _area_weights(0), - "local_authority_weights.h5": _area_weights(2), - } - self._names = { - "constituencies_2024.csv": _area_names("Constituency"), - "local_authorities_2021.csv": _area_names("Local authority"), - } - - def download(self, *, repo: str, repo_filename: str) -> str: - self.calls.append(["download", repo, repo_filename]) - return f"/fake/{repo}/{repo_filename}" - - def open_weights(self, path: str, mode: str) -> "FakeWeightsFile": - self.calls.append(["h5py.File", path, mode]) - return FakeWeightsFile({"2025": self._weights[Path(path).name]}) - - def read_names(self, path: str) -> pd.DataFrame: - self.calls.append(["read_csv", path]) - return self._names[Path(path).name].copy() - - -class FakeWeightsFile: - """A context manager that yields a mapping like an open ``h5py.File``.""" - - def __init__(self, datasets: dict[str, np.ndarray]) -> None: - self._datasets = datasets - - def __enter__(self) -> dict[str, np.ndarray]: - return self._datasets - - def __exit__(self, *_exc_info: object) -> bool: - return False - - -@pytest.fixture -def uk_datasets() -> Iterator[FakeUKDatasets]: - datasets = FakeUKDatasets() - with ( - patch.object( - economy_comparison, "download_huggingface_dataset", datasets.download - ), - patch.object(economy_comparison.h5py, "File", datasets.open_weights), - patch.object(economy_comparison.pd, "read_csv", datasets.read_names), - ): - yield datasets - - -class _RecordingHandler(logging.Handler): - """Keep every record logged to the logger it is attached to.""" - - def __init__(self, records: list[logging.LogRecord]) -> None: - super().__init__(level=logging.DEBUG) - self.records = records - - def emit(self, record: logging.LogRecord) -> None: - self.records.append(record) - - -@pytest.fixture -def comparison_logs() -> Iterator[list[logging.LogRecord]]: - """Record what the comparison module logs, whatever the global logging setup. - - Other tests load logging configuration that disables existing loggers, so - the module logger is enabled and recorded directly for the test's duration. - """ - records: list[logging.LogRecord] = [] - handler = _RecordingHandler(records) - logger = economy_comparison.logger - previous_state = (logger.disabled, logger.level) - logger.disabled = False - logger.setLevel(logging.DEBUG) - logger.addHandler(handler) - try: - yield records - finally: - logger.removeHandler(handler) - logger.disabled, previous_level = previous_state - logger.setLevel(previous_level) - - -def _canonical_key(key: object) -> str: - """Keep string keys as they are and tag any other key with its type.""" - if isinstance(key, str): - return key - return f"{type(key).__name__}:{key!r}" - - -def _canonical(value: object) -> object: - """Return a JSON-safe form of ``value`` that keeps every type and float bit.""" - if isinstance(value, BaseModel): - return { - "model": type(value).__name__, - "fields": _canonical(value.model_dump()), - } - if isinstance(value, dict): - return {_canonical_key(key): _canonical(item) for key, item in value.items()} - if isinstance(value, list | tuple): - return { - type(value).__name__: [_canonical(item) for item in value], - } - if value is None or isinstance(value, str | bool): - return value - if isinstance(value, float | np.floating): - return f"{type(value).__name__}:{float(value)!r}" - if isinstance(value, int | np.integer | np.bool_): - return f"{type(value).__name__}:{value!r}" - raise TypeError(type(value).__name__) - - -def _key_order(value: object, path: str = "") -> dict[str, list[str]]: - """Map each dict's path to its keys in insertion order, so reorders show up.""" - if isinstance(value, BaseModel): - return _key_order(value.model_dump(), path) - orders: dict[str, list[str]] = {} - if isinstance(value, dict): - orders[path] = [_canonical_key(key) for key in value] - for key, item in value.items(): - orders.update(_key_order(item, f"{path}/{_canonical_key(key)}")) - elif isinstance(value, list | tuple): - for index, item in enumerate(value): - orders.update(_key_order(item, f"{path}[{index}]")) - return orders - - -def _observed( - result: object, - datasets: FakeUKDatasets | None, - comparison_logs: list[logging.LogRecord], -) -> dict[str, Any]: - return { - "result_type": type(result).__name__, - "result": _canonical(result), - "key_order": _key_order(result), - "dataset_calls": datasets.calls if datasets is not None else [], - "logs": [ - [ - record.name, - record.levelname, - record.getMessage(), - record.exc_info[0].__name__ if record.exc_info else None, - ] - for record in comparison_logs - if record.name.startswith("policyengine_api") - ], - } - - -def _load_snapshots() -> dict[str, Any]: - if not SNAPSHOT_PATH.exists(): - return {"cases": {}, "outputs": {}} - return json.loads(SNAPSHOT_PATH.read_text(encoding="utf-8")) - - -def _write_snapshot(case_name: str, observed: dict[str, Any], output_id: str) -> None: - """Record a case, sharing one stored output between cases that agree.""" - snapshots = _load_snapshots() - snapshots["cases"][case_name] = output_id - snapshots["outputs"][output_id] = observed - used_outputs = set(snapshots["cases"].values()) - snapshots["outputs"] = { - stored_id: stored - for stored_id, stored in snapshots["outputs"].items() - if stored_id in used_outputs - } - SNAPSHOT_PATH.write_text( - json.dumps(snapshots, sort_keys=True, indent=1) + "\n", - encoding="utf-8", - ) - - -def _assert_matches_snapshot(case_name: str, observed: dict[str, Any]) -> None: - serialized = json.dumps(observed, sort_keys=True) - if UPDATE_SNAPSHOTS: - output_id = hashlib.sha256(serialized.encode("utf-8")).hexdigest()[:16] - _write_snapshot(case_name, json.loads(serialized), output_id) - return - snapshots = _load_snapshots() - assert case_name in snapshots["cases"], f"no snapshot recorded for {case_name}" - expected = snapshots["outputs"][snapshots["cases"][case_name]] - assert json.loads(serialized) == expected - assert serialized == json.dumps(expected, sort_keys=True) - - -GENERAL_CASES = { - "uk_general_all_regions": ("uk", None, True, False), - "uk_general_country_scotland": ("uk", "country/scotland", True, False), - "uk_general_constituency_by_name": ( - "uk", - "constituency/Constituency 3", - True, - False, - ), - "uk_general_numpy_inputs": ("uk", "country/northern_ireland", True, True), - "us_general_national_without_wealth_deciles": ("us", "us", False, False), - "us_general_state_with_wealth_deciles": ("us", "state/ca", True, False), -} - - -@pytest.mark.parametrize("case_name", sorted(GENERAL_CASES)) -def test_compare_economic_outputs_general_matches_snapshot( - case_name, uk_datasets, comparison_logs -): - country_id, region, include_wealth, use_numpy = GENERAL_CASES[case_name] - economy_country = country_id or "us" - baseline = _economy( - country_id=economy_country, side="baseline", include_wealth=include_wealth - ) - reform = _economy( - country_id=economy_country, side="reform", include_wealth=include_wealth - ) - if use_numpy: - baseline, reform = _numpy_economy(baseline), _numpy_economy(reform) - - result = compare_economic_outputs(baseline, reform, country_id, region) - - _assert_matches_snapshot(case_name, _observed(result, uk_datasets, comparison_logs)) - - -@pytest.mark.parametrize( - ("case_name", "baseline_type", "country_id"), - [ - ("uk_cliff", "cliff", "uk"), - ("us_cliff", "cliff", "us"), - ("unknown_type", "household", "us"), - ("missing_type", None, "uk"), - ], -) -def test_compare_economic_outputs_other_types_match_snapshot( - case_name, baseline_type, country_id, comparison_logs -): - baseline = _cliff_economy(side="baseline") - reform = _cliff_economy(side="reform") - if baseline_type is None: - del baseline["type"] - else: - baseline["type"] = baseline_type - - result = compare_economic_outputs(baseline, reform, country_id, None) - - _assert_matches_snapshot(case_name, _observed(result, None, comparison_logs)) - - -BREAKDOWN_REGIONS = [ - None, - "uk", - "country/england", - "country/scotland", - "country/wales", - "country/northern_ireland", - "country/ENGLAND", - "country/Northern_Ireland", - "country/unknown", - "country/", - "constituency/Constituency 4", - f"constituency/{AREA_CODES[6]}", - "constituency/Missing", - "local_authority/Local authority 4", - f"local_authority/{AREA_CODES[7]}", - "local_authority/Missing", - "state/ca", -] - - -def _uk_breakdown_economies() -> tuple[dict[str, Any], dict[str, Any]]: - baseline = {"household_net_income": _baseline_incomes()} - reform = {"household_net_income": _reform_incomes()} - return baseline, reform - - -@pytest.mark.parametrize( - ("breakdown_name", "breakdown"), - [ - ("constituency", uk_constituency_breakdown), - ("local_authority", uk_local_authority_breakdown), - ], -) -@pytest.mark.parametrize("region", BREAKDOWN_REGIONS) -def test_uk_area_breakdowns_match_snapshot( - breakdown_name, breakdown, region, uk_datasets, comparison_logs -): - baseline, reform = _uk_breakdown_economies() - - result = breakdown(baseline, reform, "uk", region) - - _assert_matches_snapshot( - f"{breakdown_name}_breakdown_region_{region}", - _observed(result, uk_datasets, comparison_logs), - ) - - -@pytest.mark.parametrize( - ("breakdown_name", "breakdown"), - [ - ("constituency", uk_constituency_breakdown), - ("local_authority", uk_local_authority_breakdown), - ], -) -@pytest.mark.parametrize("country_id", ["us", None, "UK"]) -def test_uk_area_breakdowns_outside_uk_match_snapshot( - breakdown_name, breakdown, country_id, uk_datasets, comparison_logs -): - baseline, reform = _uk_breakdown_economies() - - result = breakdown(baseline, reform, country_id, None) - - _assert_matches_snapshot( - f"{breakdown_name}_breakdown_country_{country_id}", - _observed(result, uk_datasets, comparison_logs), - ) - - -def test_uk_area_breakdowns_with_numpy_incomes_match_snapshot( - uk_datasets, comparison_logs -): - baseline, reform = _uk_breakdown_economies() - baseline = _numpy_economy(baseline) - reform = _numpy_economy(reform) - - results = { - "constituency": uk_constituency_breakdown(baseline, reform, "uk"), - "local_authority": uk_local_authority_breakdown(baseline, reform, "uk"), - } - - _assert_matches_snapshot( - "area_breakdowns_numpy_incomes", - _observed(results, uk_datasets, comparison_logs), - ) - - -def test_snapshot_inputs_reach_every_income_change_bucket(uk_datasets): - """Guard the inputs: each bucket and a NaN area change must stay exercised.""" - baseline, reform = _uk_breakdown_economies() - - result = uk_constituency_breakdown(baseline, reform, "uk") - - assert result is not None - assert all(count > 0 for count in result.outcomes_by_region["uk"].values()) - assert any( - math.isnan(area.relative_household_income_change) - for area in result.by_constituency.values() - ) - assert uk_datasets.calls