From 281c497044d35533f20157785a1083d5b18b6182 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 12:06:51 +1100 Subject: [PATCH 01/17] Fix Add full household outputs #82 Fix Versioning action runs twice #80 --- .github/workflows/versioning.yaml | 5 +- .../calculate_household_comparison.py | 40 ++-- .../single/calculate_single_household.py | 178 ++++++++++++++++-- policyengine/utils/calculations.py | 16 +- 4 files changed, 209 insertions(+), 30 deletions(-) diff --git a/.github/workflows/versioning.yaml b/.github/workflows/versioning.yaml index 53709c4e..c4ccf09b 100644 --- a/.github/workflows/versioning.yaml +++ b/.github/workflows/versioning.yaml @@ -4,10 +4,11 @@ name: Versioning updates on: push: branches: - - main + - main paths: - - changelog_entry.yaml + - changelog_entry.yaml + - "!pyproject.toml" jobs: Versioning: diff --git a/policyengine/outputs/household/comparison/calculate_household_comparison.py b/policyengine/outputs/household/comparison/calculate_household_comparison.py index adf81963..e91df1c3 100644 --- a/policyengine/outputs/household/comparison/calculate_household_comparison.py +++ b/policyengine/outputs/household/comparison/calculate_household_comparison.py @@ -8,22 +8,23 @@ from pydantic import BaseModel from policyengine.utils.calculations import get_change from policyengine_core.simulations import Simulation as CountrySimulation +from policyengine.outputs.household.single.calculate_single_household import ( + SingleHousehold, + fill_and_calculate, + FullHouseholdSpecification, +) from typing import Literal, List class HouseholdComparison(BaseModel): - household_net_income: float - """The net income of the household.""" + full_household_baseline: FullHouseholdSpecification + """The full completion of the household under the baseline scenario.""" + full_household_reform: FullHouseholdSpecification + """The full completion of the household under the reform scenario.""" -def calculate_net_income( - baseline: CountrySimulation, - reform: CountrySimulation, -) -> float: - return ( - reform.calculate("household_net_income").sum() - - baseline.calculate("household_net_income").sum() - ) + change: FullHouseholdSpecification + """The change in the household from the baseline to the reform scenario.""" def calculate_household_comparison( @@ -33,11 +34,20 @@ def calculate_household_comparison( if not simulation.is_comparison: raise ValueError("Simulation must be a comparison simulation.") - baseline = simulation.baseline_simulation - reform = simulation.reform_simulation - - net_income = calculate_net_income(baseline, reform) + baseline_household = fill_and_calculate( + simulation.options.data, simulation.baseline_simulation + ) + reform_household = fill_and_calculate( + simulation.options.data, simulation.reform_simulation + ) return HouseholdComparison( - household_net_income=net_income, + full_household_baseline=baseline_household, + full_household_reform=reform_household, + change=get_change( + baseline_household, + reform_household, + relative=False, + skip_mismatch=True, + ), ) diff --git a/policyengine/outputs/household/single/calculate_single_household.py b/policyengine/outputs/household/single/calculate_single_household.py index 26631794..04097877 100644 --- a/policyengine/outputs/household/single/calculate_single_household.py +++ b/policyengine/outputs/household/single/calculate_single_household.py @@ -7,18 +7,26 @@ from pydantic import BaseModel from policyengine_core.simulations import Simulation as CountrySimulation -from typing import List +from typing import List, Dict +from datetime import date +from policyengine_core.variables import Variable +from policyengine_core.entities import Entity +from policyengine_core.model_api import YEAR, MONTH, ETERNITY, Enum +import dpath.util +import math +import json +Value = float | str | bool | None +FullHouseholdSpecification = Dict[ + str, Dict[str, Dict[str, Dict[str, Value]]] +] # {people: {person: {variable: {time_period: value}}}} -class SingleHousehold(BaseModel): - household_net_income: float - """The net income of the household.""" +class SingleHousehold(BaseModel): + """Statistics for a single household scenario.""" -def calculate_net_income( - simulation: CountrySimulation, -) -> float: - return simulation.calculate("household_net_income").sum() + full_household: FullHouseholdSpecification + """Full variable calculations for the household.""" def calculate_single_household( @@ -30,8 +38,156 @@ def calculate_single_household( "This function is for single economy simulations only." ) - net_income = calculate_net_income(simulation.baseline_simulation) - return SingleHousehold( - household_net_income=net_income, + full_household=fill_and_calculate( + simulation.options.data, simulation.baseline_simulation + ) + ) + + +def fill_and_calculate( + household: FullHouseholdSpecification, simulation: CountrySimulation +): + """Fill in missing variables and calculate all variables for a household""" + # Copy the household to avoid modifying the original + household = json.loads(json.dumps(household)) + household = add_yearly_variables(household, simulation) + household = calculate_all_variables(household, simulation) + return household + + +def get_requested_computations( + household: FullHouseholdSpecification, +) -> List[tuple[str, str, str, str]]: + requested_computations = dpath.util.search( + household, + "*/*/*/*", + afilter=lambda t: t is None, + yielded=True, + ) + requested_computation_data = [] + + for computation in requested_computations: + path = computation[0] + entity_plural, entity_id, variable_name, period = path.split("/") + requested_computation_data.append( + (entity_plural, entity_id, variable_name, period) + ) + + return requested_computation_data + + +def calculate_all_variables( + household: FullHouseholdSpecification, simulation: CountrySimulation +) -> FullHouseholdSpecification: + requested_computations = get_requested_computations(household) + + for ( + entity_plural, + entity_id, + variable_name, + period, + ) in requested_computations: + variable = simulation.tax_benefit_system.get_variable(variable_name) + result = simulation.calculate(variable_name, period) + population = simulation.get_population(entity_plural) + + if "axes" in household: + count_entities = len(household[entity_plural]) + entity_index = 0 + for _entity_id in household[entity_plural].keys(): + if _entity_id == entity_id: + break + entity_index += 1 + result = ( + result.astype(float) + .reshape((-1, count_entities)) + .T[entity_index] + .tolist() + ) + # If the result contains infinities, throw an error + if any([math.isinf(value) for value in result]): + raise ValueError("Infinite value") + else: + household[entity_plural][entity_id][variable_name][ + period + ] = result + else: + entity_index = population.get_index(entity_id) + if variable.value_type == Enum: + entity_result = result.decode()[entity_index].name + elif variable.value_type == float: + entity_result = float(str(result[entity_index])) + # Convert infinities to JSON infinities + if entity_result == float("inf"): + entity_result = "Infinity" + elif entity_result == float("-inf"): + entity_result = "-Infinity" + elif variable.value_type == str: + entity_result = str(result[entity_index]) + else: + entity_result = result.tolist()[entity_index] + + household[entity_plural][entity_id][variable_name][ + period + ] = entity_result + + return household + + +def get_household_year(household: FullHouseholdSpecification) -> str: + """Given a household dict, get the household's year + + Args: + household (dict): The household itself + """ + + # Set household_year based on current year + household_year = date.today().year + + # Determine if "age" variable present within household and return list of values at it + household_age_list = list( + household.get("people", {}).get("you", {}).get("age", {}).keys() ) + # If it is, overwrite household_year with the value present + if len(household_age_list) > 0: + household_year = household_age_list[0] + + return str(household_year) + + +def add_yearly_variables( + household: FullHouseholdSpecification, simulation: CountrySimulation +) -> FullHouseholdSpecification: + """ + Add yearly variables to a household dict before enqueueing calculation + """ + + variables: Dict[str, Variable] = simulation.tax_benefit_system.variables + entities: Dict[str, Entity] = ( + simulation.tax_benefit_system.entities_by_singular() + ) + household_year = get_household_year(household) + + for variable in variables: + if variables[variable].definition_period in (YEAR, MONTH, ETERNITY): + entity_plural = entities[variables[variable].entity.key].plural + if entity_plural in household: + possible_entities = household[entity_plural].keys() + for entity in possible_entities: + if ( + not variables[variable].name + in household[entity_plural][entity] + ): + if variables[variable].is_input_variable(): + value = variables[variable].default_value + if isinstance(value, Enum): + value = value.name + household[entity_plural][entity][ + variables[variable].name + ] = {household_year: value} + else: + household[entity_plural][entity][ + variables[variable].name + ] = {household_year: None} + return household diff --git a/policyengine/utils/calculations.py b/policyengine/utils/calculations.py index b6c24e7e..b0578ef8 100644 --- a/policyengine/utils/calculations.py +++ b/policyengine/utils/calculations.py @@ -8,6 +8,7 @@ def get_change( x: Output | Dict[str, Output], y: Output | Dict[str, Output], relative: bool, + skip_mismatch: bool = False, ) -> Output | Dict[str, Output]: """Take two objects of nested str-float relations and create a similarly-structured object with the differences.""" if isinstance(x, BaseModel): @@ -24,9 +25,20 @@ def get_change( elif x[key] is None and y[key] is None: result[key] = None elif x[key] is None: - raise ValueError(f"Key {key} is None in x but not in y") + if skip_mismatch: + result[key] = None + else: + raise ValueError(f"Key {key} is None in x but not in y") elif y[key] is None: - raise ValueError(f"Key {key} is None in y but not in x") + if skip_mismatch: + result[key] = None + else: + raise ValueError(f"Key {key} is None in y but not in x") + elif isinstance(x[key], str) or isinstance(y[key], str): + if x[key] == y[key]: + result[key] = 0 + else: + result[key] = f"{x[key]} -> {y[key]}" elif not relative: result[key] = y[key] - x[key] else: From b91a2d8e9e7f926e1d074de0b7ad841558aa0987 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 13:28:58 +1100 Subject: [PATCH 02/17] Add type hint checks --- .../calculate_household_comparison.py | 13 +++++---- .../single/calculate_single_household.py | 29 +++++++++++++------ policyengine/utils/calculations.py | 6 ++++ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/policyengine/outputs/household/comparison/calculate_household_comparison.py b/policyengine/outputs/household/comparison/calculate_household_comparison.py index e91df1c3..48c28f0c 100644 --- a/policyengine/outputs/household/comparison/calculate_household_comparison.py +++ b/policyengine/outputs/household/comparison/calculate_household_comparison.py @@ -40,14 +40,15 @@ def calculate_household_comparison( reform_household = fill_and_calculate( simulation.options.data, simulation.reform_simulation ) + change = get_change( + baseline_household, + reform_household, + relative=False, + skip_mismatch=True, + ) return HouseholdComparison( full_household_baseline=baseline_household, full_household_reform=reform_household, - change=get_change( - baseline_household, - reform_household, - relative=False, - skip_mismatch=True, - ), + change=change, ) diff --git a/policyengine/outputs/household/single/calculate_single_household.py b/policyengine/outputs/household/single/calculate_single_household.py index 04097877..858a0d6c 100644 --- a/policyengine/outputs/household/single/calculate_single_household.py +++ b/policyengine/outputs/household/single/calculate_single_household.py @@ -16,9 +16,13 @@ import math import json -Value = float | str | bool | None +Value = float | str | bool | list | None +Axes = List[List[Dict[str, str | int]]] +TimePeriodValues = Dict[str, Value] +EntityValues = Dict[str, TimePeriodValues] +EntityGroupValues = Dict[str, EntityValues] FullHouseholdSpecification = Dict[ - str, Dict[str, Dict[str, Dict[str, Value]]] + str, EntityGroupValues | Axes ] # {people: {person: {variable: {time_period: value}}}} @@ -53,6 +57,7 @@ def fill_and_calculate( household = json.loads(json.dumps(household)) household = add_yearly_variables(household, simulation) household = calculate_all_variables(household, simulation) + household.pop("axes", None) return household @@ -60,9 +65,9 @@ def get_requested_computations( household: FullHouseholdSpecification, ) -> List[tuple[str, str, str, str]]: requested_computations = dpath.util.search( - household, + {k: v for k, v in household.items() if k != "axes"}, "*/*/*/*", - afilter=lambda t: t is None, + # afilter=lambda t: t is None, yielded=True, ) requested_computation_data = [] @@ -99,14 +104,20 @@ def calculate_all_variables( if _entity_id == entity_id: break entity_index += 1 + try: + result = result.astype(float) + except: + pass result = ( - result.astype(float) - .reshape((-1, count_entities)) - .T[entity_index] - .tolist() + result.reshape((-1, count_entities)).T[entity_index].tolist() ) # If the result contains infinities, throw an error - if any([math.isinf(value) for value in result]): + if any( + [ + not isinstance(value, str) and math.isinf(value) + for value in result + ] + ): raise ValueError("Infinite value") else: household[entity_plural][entity_id][variable_name][ diff --git a/policyengine/utils/calculations.py b/policyengine/utils/calculations.py index b0578ef8..0a9aa709 100644 --- a/policyengine/utils/calculations.py +++ b/policyengine/utils/calculations.py @@ -1,5 +1,6 @@ from typing import Dict from pydantic import BaseModel +import numpy as np Output = Dict[str, float | None] @@ -22,6 +23,11 @@ def get_change( for key in x: if isinstance(x[key], dict): result[key] = get_change(x[key], y[key], relative=relative) + elif isinstance(x[key], list): + try: + result[key] = list(np.array(y[key]) - np.array(x[key])) + except: + result[key] = None elif x[key] is None and y[key] is None: result[key] = None elif x[key] is None: From 728201aa6e038957c10319b872f8365977559e3b Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 13:44:54 +1100 Subject: [PATCH 03/17] Add headlines output to schema to match app --- .../calculate_economy_comparison.py | 31 ++++++++++++++++++- policyengine/outputs/macro/single/budget.py | 5 ++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/policyengine/outputs/macro/comparison/calculate_economy_comparison.py b/policyengine/outputs/macro/comparison/calculate_economy_comparison.py index d2ddde59..4410f84f 100644 --- a/policyengine/outputs/macro/comparison/calculate_economy_comparison.py +++ b/policyengine/outputs/macro/comparison/calculate_economy_comparison.py @@ -39,6 +39,15 @@ class InequalityComparison(BaseModel): relative_change: InequalitySummary +class Headlines(BaseModel): + budgetary_impact: float + """The change in the (federal) government budget balance.""" + poverty_impact: float + """The relative change in the regular poverty rate.""" + winner_share: float + """The share of people that are better off in the reform scenario.""" + + class PovertyRateMetricComparison(BaseModel): age_group: Literal["child", "working_age", "senior", "all"] """The age group of the population.""" @@ -61,6 +70,8 @@ class PovertyRateMetricComparison(BaseModel): class EconomyComparison(BaseModel): + headlines: Headlines + """Headline statistics for the comparison.""" fiscal: FiscalComparison """Government budgets and other top-level fiscal statistics.""" inequality: InequalityComparison @@ -118,7 +129,7 @@ def calculate_economy_comparison( baseline_poverty_metrics = calculate_poverty(baseline, options) reform_poverty_metrics = calculate_poverty(reform, options) - poverty_metrics = [] + poverty_metrics: List[PovertyRateMetricComparison] = [] for baseline_metric, reform_metric in zip( baseline_poverty_metrics, reform_poverty_metrics ): @@ -144,7 +155,25 @@ def calculate_economy_comparison( baseline, reform, options ) + # Headlines + budgetary_impact = fiscal_comparison.change.federal_balance + poverty_impact = next( + filter( + lambda metric: metric.age_group == "all" + and metric.racial_group == "all" + and metric.poverty_rate in ("us_spm", "uk_hbai_bhc"), + poverty_metrics, + ) + ).relative_change + winner_share = decile_impacts.income.winners_and_losers.all.gain_share + headlines = Headlines( + budgetary_impact=budgetary_impact, + poverty_impact=poverty_impact, + winner_share=winner_share, + ) + return EconomyComparison( + headlines=headlines, fiscal=fiscal_comparison, inequality=inequality_comparison, distributional=decile_impacts, diff --git a/policyengine/outputs/macro/single/budget.py b/policyengine/outputs/macro/single/budget.py index c349c364..d5fbdb1a 100644 --- a/policyengine/outputs/macro/single/budget.py +++ b/policyengine/outputs/macro/single/budget.py @@ -35,10 +35,12 @@ class FiscalSummary(BaseModel): """The total tax revenue collected by the government.""" federal_tax: float """The total tax revenue collected by the federal (or national) government.""" + federal_balance: float + """Federal taxes subtract spending.""" state_tax: float """The total tax revenue collected by the state government.""" government_spending: float - """The total spending by the government on modeled programs.""" + """The total spending by the (federal) government on modeled programs.""" tax_benefit_programs: dict[str, float] """The total revenue change to the government from each tax-benefit program.""" household_net_income: float @@ -73,6 +75,7 @@ def calculate_government_balance( return FiscalSummary( tax_revenue=total_tax, federal_tax=national_tax, + federal_balance=national_tax - total_spending, state_tax=total_state_tax, government_spending=total_spending, tax_benefit_programs=tb_programs, From 98634ea93113cbddc76514f9f90ee72278ff0192 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 16:30:32 +1100 Subject: [PATCH 04/17] Add budget chart --- .../outputs/macro/comparison/charts/budget.py | 89 +++++++++++++++++++ policyengine/simulation.py | 4 + 2 files changed, 93 insertions(+) create mode 100644 policyengine/outputs/macro/comparison/charts/budget.py diff --git a/policyengine/outputs/macro/comparison/charts/budget.py b/policyengine/outputs/macro/comparison/charts/budget.py new file mode 100644 index 00000000..1d9b8e84 --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/budget.py @@ -0,0 +1,89 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + +from pydantic import BaseModel +from policyengine.utils.charts import * + + +def create_budget_comparison_chart( + simulation: "Simulation", +) -> go.Figure: + """Create a budget comparison chart.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + + economy = simulation.calculate_economy_comparison() + + if simulation.options.country == "uk": + x_values = [ + "Tax revenues", + "Government spending", + "Public sector net worth", + ] + y_values = [ + economy.fiscal.change.federal_tax, + economy.fiscal.change.government_spending, + economy.fiscal.change.federal_balance, + ] + else: + x_values = [ + "Federal tax revenues", + "State tax revenues", + "Federal government spending", + ] + y_values = [ + economy.fiscal.change.federal_tax, + economy.fiscal.change.state_tax, + economy.fiscal.change.government_spending, + ] + + y_values = [value / 1e9 for value in y_values] + + net_change = round(economy.fiscal.change.federal_balance / 1e9, 1) + + if net_change > 0: + description = f"raise {net_change}bn" + elif net_change < 0: + description = f"cost {-net_change}bn" + else: + description = "have no effect on government finances" + + chart = go.Figure( + data=[ + go.Waterfall( + x=x_values, + y=y_values, + measure=["relative"] * (len(x_values) - 1) + ["total"], + textposition="inside", + text=[f"{value:.1f}bn" for value in y_values], + increasing=dict( + marker=dict( + color=BLUE, + ) + ), + decreasing=dict( + marker=dict( + color=DARK_GRAY, + ) + ), + totals=dict( + marker=dict( + color=BLUE if net_change > 0 else DARK_GRAY, + ) + ), + ), + ] + ).update_layout( + title=f"{simulation.options.title} would {description}", + yaxis_title="Budgetary impact (bn)", + uniformtext=dict( + mode="hide", + minsize=12, + ), + ) + + return format_fig(chart) diff --git a/policyengine/simulation.py b/policyengine/simulation.py index 09036e7b..fa499871 100644 --- a/policyengine/simulation.py +++ b/policyengine/simulation.py @@ -70,6 +70,10 @@ class SimulationOptions(BaseModel): None, description="How many, if a subsample, households to randomly simulate.", ) + title: str | None = Field( + "[Analysis title]", + description="The title of the analysis (for charts). If not provided, a default title will be generated.", + ) class Simulation: From 1ba161a7052d89ec109f8c83c9ff7c4e0b8d7bbc Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 16:45:21 +1100 Subject: [PATCH 05/17] Add budgetary impact by program --- .../outputs/macro/comparison/charts/budget.py | 8 +- .../comparison/charts/budget_by_program.py | 95 +++++++++++++++++++ policyengine/utils/charts.py | 29 ++++++ 3 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 policyengine/outputs/macro/comparison/charts/budget_by_program.py diff --git a/policyengine/outputs/macro/comparison/charts/budget.py b/policyengine/outputs/macro/comparison/charts/budget.py index 1d9b8e84..33ea9eae 100644 --- a/policyengine/outputs/macro/comparison/charts/budget.py +++ b/policyengine/outputs/macro/comparison/charts/budget.py @@ -46,9 +46,9 @@ def create_budget_comparison_chart( net_change = round(economy.fiscal.change.federal_balance / 1e9, 1) if net_change > 0: - description = f"raise {net_change}bn" + description = f"raise ${net_change}bn" elif net_change < 0: - description = f"cost {-net_change}bn" + description = f"cost ${-net_change}bn" else: description = "have no effect on government finances" @@ -59,7 +59,7 @@ def create_budget_comparison_chart( y=y_values, measure=["relative"] * (len(x_values) - 1) + ["total"], textposition="inside", - text=[f"{value:.1f}bn" for value in y_values], + text=[f"${value:.1f}bn" for value in y_values], increasing=dict( marker=dict( color=BLUE, @@ -79,7 +79,7 @@ def create_budget_comparison_chart( ] ).update_layout( title=f"{simulation.options.title} would {description}", - yaxis_title="Budgetary impact (bn)", + yaxis_title="Budgetary impact ($bn)", uniformtext=dict( mode="hide", minsize=12, diff --git a/policyengine/outputs/macro/comparison/charts/budget_by_program.py b/policyengine/outputs/macro/comparison/charts/budget_by_program.py new file mode 100644 index 00000000..018702fa --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/budget_by_program.py @@ -0,0 +1,95 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + +from pydantic import BaseModel +from policyengine.utils.charts import * + + +def create_budget_program_comparison_chart( + simulation: "Simulation", +) -> go.Figure: + """Create a budget comparison chart.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + + if not simulation.options.country == "uk": + raise ValueError("This chart is only available for the UK.") + + economy = simulation.calculate_economy_comparison() + + change_programs = economy.fiscal.change.tax_benefit_programs + + change_programs = { + program: change_programs[program] + for program in change_programs + if round(change_programs[program] / 1e9, 1) != 0 + } + + labels = [ + simulation.baseline_simulation.tax_benefit_system.variables.get( + program + ).label + for program in change_programs + ] + + x_values = labels + y_values = [ + round(change_programs[program] / 1e9, 1) for program in change_programs + ] + + total_from_programs = round(sum(y_values)) + total_overall = round(economy.fiscal.change.federal_balance / 1e9) + + if total_from_programs != total_overall: + x_values.append("Other") + y_values.append(total_overall - total_from_programs) + + x_values.append("Combined") + y_values.append(total_overall) + + if total_overall > 0: + description = f"raise ${total_overall}bn" + elif total_overall < 0: + description = f"cost ${-total_overall}bn" + else: + description = "have no effect on government finances" + + chart = go.Figure( + data=[ + go.Waterfall( + x=x_values, + y=y_values, + measure=["relative"] * (len(x_values) - 1) + ["total"], + textposition="inside", + text=[f"${value:.1f}bn" for value in y_values], + increasing=dict( + marker=dict( + color=BLUE, + ) + ), + decreasing=dict( + marker=dict( + color=DARK_GRAY, + ) + ), + totals=dict( + marker=dict( + color=BLUE if total_overall > 0 else DARK_GRAY, + ) + ), + ), + ] + ).update_layout( + title=f"{simulation.options.title} would {description}", + yaxis_title="Budgetary impact (bn)", + uniformtext=dict( + mode="hide", + minsize=12, + ), + ) + + return format_fig(chart) diff --git a/policyengine/utils/charts.py b/policyengine/utils/charts.py index bf3a665b..1f414ab9 100644 --- a/policyengine/utils/charts.py +++ b/policyengine/utils/charts.py @@ -119,9 +119,38 @@ def format_fig( margin_l=120, margin_r=120, ) + + # Auto-format currency + + if country == "uk": + currency = "£" + else: + currency = "$" + + fig.update_layout( + title=correct_text_currency(fig.layout.title.text or "", currency), + yaxis_title=correct_text_currency( + fig.layout.yaxis.title.text or "", currency + ), + xaxis_title=correct_text_currency( + fig.layout.xaxis.title.text or "", currency + ), + ) + + for trace in fig.data: + if "text" in trace: + trace.text = [ + correct_text_currency(t, currency) for t in trace.text + ] + return fig +def correct_text_currency(text: str, currency: str) -> str: + """Correct text to match the currency symbol.""" + return text.replace("$", currency).replace(f"{currency}-", f"-{currency}") + + def cardinal(n: int) -> int: """Convert an integer to a cardinal string.""" ending_number = n % 10 From aad557b53ed99f7b5497ad5c4f4d8d91c969fafe Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 17:31:45 +1100 Subject: [PATCH 06/17] Add decile charts --- .../outputs/macro/comparison/charts/decile.py | 88 +++++++++++++++++++ policyengine/simulation.py | 5 +- policyengine/utils/charts.py | 26 ++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 policyengine/outputs/macro/comparison/charts/decile.py diff --git a/policyengine/outputs/macro/comparison/charts/decile.py b/policyengine/outputs/macro/comparison/charts/decile.py new file mode 100644 index 00000000..0b454ccd --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/decile.py @@ -0,0 +1,88 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + +from pydantic import BaseModel +from policyengine.utils.charts import * +from typing import Literal + + +def create_decile_chart( + simulation: "Simulation", + decile_variable: Literal["income", "wealth"], + relative: bool, +) -> go.Figure: + """Create a budget comparison chart.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + + economy = simulation.calculate_economy_comparison() + + if decile_variable == "income": + data = economy.distributional.income.income_change + else: + data = economy.distributional.wealth.income_change + + if relative: + data = data.relative + else: + data = data.average + + avg_change = sum(data.values()) / len(data) + + if relative: + text = [f"{value:.1%}" for value in data.values()] + avg_change = round(avg_change, 3) + else: + text = [f"${value:,.0f}" for value in data.values()] + avg_change = round(avg_change) + + avg_change_str = ( + f"${abs(avg_change):,.0f}" + if not relative + else f"{abs(avg_change):.1%}" + ) + + if avg_change > 0: + description = ( + f"increase the net income of households by {avg_change_str}" + ) + elif avg_change < 0: + description = ( + f"decrease the net income of households by {-avg_change_str}" + ) + else: + description = "have no effect on household net income" + + chart = go.Figure( + data=[ + go.Bar( + x=list(data.keys()), + y=list(data.values()), + text=text, + marker=dict( + color=[ + BLUE if value > 0 else DARK_GRAY + for value in data.values() + ] + ), + ) + ] + ).update_layout( + title=f"{simulation.options.title} would {description}", + yaxis_title=f"Average change to net income ({'%' if relative else '$'})", + yaxis_tickformat="$,.0f" if not relative else ".0%", + xaxis_title=( + "Income decile" if decile_variable == "income" else "Wealth decile" + ), + uniformtext=dict( + mode="hide", + minsize=12, + ), + xaxis_tickvals=list(data.keys()), + ) + + return format_fig(chart) diff --git a/policyengine/simulation.py b/policyengine/simulation.py index fa499871..5f6c7af0 100644 --- a/policyengine/simulation.py +++ b/policyengine/simulation.py @@ -42,10 +42,13 @@ HouseholdComparison, calculate_household_comparison, ) +from typing import Any CountryType = Literal["uk", "us"] ScopeType = Literal["household", "macro"] -DataType = str | dict | None # Needs stricter typing +DataType = ( + str | dict | Any | None +) # Needs stricter typing. Any==policyengine_core.data.Dataset, but pydantic refuses for some reason. TimePeriodType = int ReformType = ( ParametricReform | SimulationAdjustment | Type[StructuralReform] | None diff --git a/policyengine/utils/charts.py b/policyengine/utils/charts.py index 1f414ab9..67caae22 100644 --- a/policyengine/utils/charts.py +++ b/policyengine/utils/charts.py @@ -118,6 +118,10 @@ def format_fig( margin_t=120, margin_l=120, margin_r=120, + uniformtext=dict( + mode="hide", + minsize=12, + ), ) # Auto-format currency @@ -137,6 +141,10 @@ def format_fig( ), ) + fig.update_layout( + title=wrap_text(fig.layout.title.text or ""), + ) + for trace in fig.data: if "text" in trace: trace.text = [ @@ -146,6 +154,24 @@ def format_fig( return fig +def wrap_text(text: str, max_length: int = 80) -> str: + """Wrap text to a maximum length, respecting spaces.""" + if len(text) <= max_length: + return text + + split_text = text.split(" ") + wrapped_text = "" + line_length = 0 + for word in split_text: + if line_length + len(word) > max_length: + wrapped_text += "
" + line_length = 0 + wrapped_text += word + " " + line_length += len(word) + 1 + + return wrapped_text + + def correct_text_currency(text: str, currency: str) -> str: """Correct text to match the currency symbol.""" return text.replace("$", currency).replace(f"{currency}-", f"-{currency}") From 784a945306b0e9396f1bbe7fb4d4bf13618cc209 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 18:45:52 +1100 Subject: [PATCH 07/17] Add intra-decile charts --- .../macro/comparison/charts/winners_losers.py | 149 ++++++++++++++++++ .../outputs/macro/comparison/decile.py | 4 +- policyengine/utils/charts.py | 2 + 3 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 policyengine/outputs/macro/comparison/charts/winners_losers.py diff --git a/policyengine/outputs/macro/comparison/charts/winners_losers.py b/policyengine/outputs/macro/comparison/charts/winners_losers.py new file mode 100644 index 00000000..9d17d495 --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/winners_losers.py @@ -0,0 +1,149 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + +from pydantic import BaseModel +from policyengine.utils.charts import * +from typing import Literal, Dict + + +COLOR_MAP = { + "Gain more than 5%": BLUE, + "Gain less than 5%": BLUE_95, + "No change": LIGHT_GRAY, + "Lose less than 5%": MEDIUM_LIGHT_GRAY, + "Lose more than 5%": DARK_GRAY, +} + +FORMATTED_KEYS = { + "gain_more_than_5_percent_share": "Gain more than 5%", + "gain_less_than_5_percent_share": "Gain less than 5%", + "no_change_share": "No change", + "lose_less_than_5_percent_share": "Lose less than 5%", + "lose_more_than_5_percent_share": "Lose more than 5%", +} + + +def create_winners_losers_chart( + simulation: "Simulation", + decile_variable: Literal["income", "wealth"], +) -> go.Figure: + """Create a budget comparison chart.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + + economy = simulation.calculate_economy_comparison() + + if decile_variable == "income": + data = economy.distributional.income.winners_and_losers + else: + data = economy.distributional.wealth.winners_and_losers + + all_decile_data = {} + for key in FORMATTED_KEYS.keys(): + all_decile_data[FORMATTED_KEYS[key]] = data.all.model_dump()[key] + + all_decile_chart = go.Bar( + x=list(all_decile_data.values()), + y=["All"] * len(all_decile_data), + name="All deciles", + orientation="h", + yaxis="y", + xaxis="x", + showlegend=False, + text=[f"{value:.1%}" for value in all_decile_data.values()], + marker=dict(color=[COLOR_MAP[key] for key in all_decile_data.keys()]), + ) + + x_values = [] + y_values = [] + color_values = [] + text = [] + hover_text = [] + for outcome_type in FORMATTED_KEYS.keys(): + for decile in range(1, 11): + value = data.deciles[decile].model_dump()[outcome_type] + x_values.append(value) + y_values.append(decile) + color_values.append(COLOR_MAP[FORMATTED_KEYS[outcome_type]]) + text.append(f"{value:.1%}") + hover_text.append( + f"{FORMATTED_KEYS[outcome_type]}, {decile}: {value:.1%}" + ) + + decile_chart = go.Bar( + x=x_values, + y=y_values, + name="Deciles", + orientation="h", + yaxis="y2", + xaxis="x2", + text=text, + textposition="inside", + marker=dict( + color=color_values, + ), + customdata=hover_text, + hovertemplate="%{customdata}", + # Need to sort out showlegend, currently fiddly. + ) + + fig = go.Figure( + data=[ + all_decile_chart, + decile_chart, + ] + ) + + winner_share = round( + economy.distributional.income.winners_and_losers.all.gain_share, 3 + ) + + if winner_share > 0: + description = f"raise the net income of {winner_share:.1%} of people" + elif winner_share < 0: + description = ( + f"decrease the net income of {-winner_share:.1%} of people" + ) + else: + description = "have no effect on household net income" + + fig.update_layout( + barmode="stack", + grid=dict( + rows=2, + columns=1, + ), + yaxis=dict( + title="", + tickvals=["All"], + domain=[0.91, 1], + ), + xaxis=dict( + title="", + tickformat=".0%", + anchor="y", + matches="x2", + showgrid=False, + showticklabels=False, + fixedrange=True, + ), + xaxis2=dict( + title="Population share", + tickformat=".0%", + anchor="y2", + fixedrange=True, + ), + yaxis2=dict( + title="Population share", + tickvals=list(range(1, 11)), + anchor="x2", + domain=[0, 0.85], + ), + title=f"{simulation.options.title} would {description}", + ) + + return format_fig(fig, country=simulation.options.country) diff --git a/policyengine/outputs/macro/comparison/decile.py b/policyengine/outputs/macro/comparison/decile.py index 61dc7381..a7041e7c 100644 --- a/policyengine/outputs/macro/comparison/decile.py +++ b/policyengine/outputs/macro/comparison/decile.py @@ -125,11 +125,11 @@ def calculate_income_specific_decile_winners_losers( BOUNDS = [ (-np.inf, -0.05), - (-0.05, 0), + (-0.05, -1e-3), (-np.inf, -1e-3), (-1e-3, 1e-3), (1e-3, np.inf), - (0, 0.05), + (1e-3, 0.05), (0.05, np.inf), ] LABELS = [ diff --git a/policyengine/utils/charts.py b/policyengine/utils/charts.py index 67caae22..9cbe124c 100644 --- a/policyengine/utils/charts.py +++ b/policyengine/utils/charts.py @@ -21,6 +21,7 @@ def add_fonts(): BLUE_PRIMARY = BLUE = "#2C6496" BLUE_PRESSED = "#17354F" BLUE_98 = "#F7FAFD" +BLUE_95 = "#D8E6F3" TEAL_LIGHT = "#D7F4F2" TEAL_ACCENT = "#39C6C0" TEAL_PRESSED = "#227773" @@ -29,6 +30,7 @@ def add_fonts(): GRAY = "#808080" LIGHT_GRAY = "#F2F2F2" MEDIUM_DARK_GRAY = "#D2D2D2" +MEDIUM_LIGHT_GRAY = "#BDBDBD" WHITE = "#FFFFFF" TEAL_98 = "#F7FDFC" BLACK = "#000000" From 0e36976b45ed918f2d7a1a2f4a367fbe9d0139c6 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 19:22:56 +1100 Subject: [PATCH 08/17] Add poverty charts --- .../calculate_economy_comparison.py | 9 +- .../macro/comparison/charts/poverty.py | 112 ++++++++++++++++++ policyengine/outputs/macro/single/poverty.py | 33 ++---- policyengine/utils/charts.py | 2 +- 4 files changed, 133 insertions(+), 23 deletions(-) create mode 100644 policyengine/outputs/macro/comparison/charts/poverty.py diff --git a/policyengine/outputs/macro/comparison/calculate_economy_comparison.py b/policyengine/outputs/macro/comparison/calculate_economy_comparison.py index 4410f84f..309d0e52 100644 --- a/policyengine/outputs/macro/comparison/calculate_economy_comparison.py +++ b/policyengine/outputs/macro/comparison/calculate_economy_comparison.py @@ -56,7 +56,12 @@ class PovertyRateMetricComparison(BaseModel): relative: bool """Whether the poverty rate is relative to the total population, or a headcount.""" poverty_rate: Literal[ - "uk_hbai_bhc", "uk_hbai_bhc_half", "us_spm", "us_spm_half" + "regular", + "deep", + "uk_hbai_bhc", + "uk_hbai_bhc_half", + "us_spm", + "us_spm_half", ] """The poverty rate definition being calculated.""" baseline: float @@ -161,7 +166,7 @@ def calculate_economy_comparison( filter( lambda metric: metric.age_group == "all" and metric.racial_group == "all" - and metric.poverty_rate in ("us_spm", "uk_hbai_bhc"), + and metric.poverty_rate == "regular", poverty_metrics, ) ).relative_change diff --git a/policyengine/outputs/macro/comparison/charts/poverty.py b/policyengine/outputs/macro/comparison/charts/poverty.py new file mode 100644 index 00000000..36a93b70 --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/poverty.py @@ -0,0 +1,112 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + from policyengine.outputs.macro.comparison.calculate_economy_comparison import ( + PovertyRateMetricComparison, + ) + +from pydantic import BaseModel +from policyengine.utils.charts import * +from typing import Literal + + +def create_poverty_chart( + simulation: "Simulation", + age_group: str, + racial_group: str, + poverty_rate: str, + rate_relative: bool, + change_relative: bool = True, +) -> go.Figure: + """Create a budget comparison chart.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + + economy = simulation.calculate_economy_comparison() + + def poverty_filter(comparison: "PovertyRateMetricComparison"): + if age_group is not None: + if comparison.age_group != age_group: + return False + if racial_group is not None: + if comparison.racial_group != racial_group: + return False + if poverty_rate is not None: + if comparison.poverty_rate != poverty_rate: + return False + if rate_relative is not None: + if comparison.relative != rate_relative: + return False + return True + + overall_poverty_rate = list( + filter( + lambda comparison: comparison.age_group == "all" + and comparison.racial_group == "all" + and comparison.poverty_rate == (poverty_rate or "regular"), + economy.poverty, + ) + )[0].relative_change + + overall_poverty_rate = round(overall_poverty_rate, 3) + + if overall_poverty_rate > 0: + description = f"raise the {'deep ' if poverty_rate == 'deep' else ''}poverty rate by {overall_poverty_rate:.1%}" + elif overall_poverty_rate < 0: + description = f"lower the {'deep ' if poverty_rate == 'deep' else ''}poverty rate by {-overall_poverty_rate:.1%}" + else: + description = "have no effect on the poverty rate" + + poverty_rates = list( + filter( + poverty_filter, + economy.poverty, + ) + ) + + if age_group is None: + x_values = [poverty.age_group for poverty in poverty_rates] + x_titles = ["Child", "Working-age", "Senior", "All"] + elif racial_group is None: + x_values = [poverty.racial_group for poverty in poverty_rates] + x_titles = ["White", "Black", "Hispanic", "Other", "All"] + elif poverty_rate is None: + x_values = [poverty.poverty_rate for poverty in poverty_rates] + x_titles = ["Regular", "Deep", "All"] + elif rate_relative is None: + x_values = [poverty.relative for poverty in poverty_rates] + x_titles = ["Relative", "Headcount", "All"] + + if change_relative: + y_values = [poverty.relative_change for poverty in poverty_rates] + else: + y_values = [poverty.change for poverty in poverty_rates] + + colors = [BLUE if value > 0 else DARK_GRAY for value in y_values] + text = [ + f"{value:.1%}" if change_relative else f"{value:,.0f}" + for value in y_values + ] + + fig = go.Figure( + data=[ + go.Bar( + x=x_values, + y=y_values, + marker=dict(color=colors), + text=text, + ) + ] + ).update_layout( + title=f"{simulation.options.title} would {description}", + yaxis_title="Change in poverty rate", + yaxis_tickformat=".0%" if change_relative else ",.0f", + xaxis_title="Group", + xaxis_tickvals=x_values, + xaxis_ticktext=x_titles, + ) + + return format_fig(fig) diff --git a/policyengine/outputs/macro/single/poverty.py b/policyengine/outputs/macro/single/poverty.py index 91aa8639..2f3c2ff4 100644 --- a/policyengine/outputs/macro/single/poverty.py +++ b/policyengine/outputs/macro/single/poverty.py @@ -18,7 +18,12 @@ class PovertyRateMetric(BaseModel): relative: bool """Whether the poverty rate is relative to the total population, or a headcount.""" poverty_rate: Literal[ - "uk_hbai_bhc", "uk_hbai_bhc_half", "us_spm", "us_spm_half" + "regular", + "deep", + "uk_hbai_bhc", + "uk_hbai_bhc_half", + "us_spm", + "us_spm_half", ] """The poverty rate definition being calculated.""" value: float @@ -55,28 +60,16 @@ def calculate_poverty( else: in_racial_group = np.ones_like(age, dtype=bool) for relative in [True, False]: - for poverty_rate in [ - "uk_hbai_bhc", - "uk_hbai_bhc_half", - "us_spm", - "us_spm_half", - ]: - if not poverty_rate.startswith(options.country): - continue - - if poverty_rate == "uk_hbai_bhc": - in_poverty = simulation.calculate( - "in_poverty", map_to="person" - ) - elif poverty_rate == "uk_hbai_bhc_half": - in_poverty = simulation.calculate( - "in_deep_poverty", map_to="person" - ) - elif poverty_rate == "us_spm": + for poverty_rate in ["regular", "deep"]: + if poverty_rate in ("regular", "uk_hbai_bhc", "us_spm"): in_poverty = simulation.calculate( "in_poverty", map_to="person" ) - elif poverty_rate == "us_spm_half": + elif poverty_rate in ( + "deep", + "uk_hbai_bhc_half", + "us_spm_half", + ): in_poverty = simulation.calculate( "in_deep_poverty", map_to="person" ) diff --git a/policyengine/utils/charts.py b/policyengine/utils/charts.py index 9cbe124c..52aae5b3 100644 --- a/policyengine/utils/charts.py +++ b/policyengine/utils/charts.py @@ -148,7 +148,7 @@ def format_fig( ) for trace in fig.data: - if "text" in trace: + if trace.text is not None: trace.text = [ correct_text_currency(t, currency) for t in trace.text ] From a904d4adc090376b3cfb59ce7112595f3f320ecd Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Thu, 30 Jan 2025 19:26:48 +1100 Subject: [PATCH 09/17] Minor edits --- policyengine/outputs/macro/comparison/charts/budget.py | 2 +- .../outputs/macro/comparison/charts/budget_by_program.py | 2 +- policyengine/outputs/macro/comparison/charts/decile.py | 2 +- policyengine/outputs/macro/comparison/charts/poverty.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/policyengine/outputs/macro/comparison/charts/budget.py b/policyengine/outputs/macro/comparison/charts/budget.py index 33ea9eae..42f478ad 100644 --- a/policyengine/outputs/macro/comparison/charts/budget.py +++ b/policyengine/outputs/macro/comparison/charts/budget.py @@ -86,4 +86,4 @@ def create_budget_comparison_chart( ), ) - return format_fig(chart) + return format_fig(chart, country=simulation.options.country, add_zero_line=True) diff --git a/policyengine/outputs/macro/comparison/charts/budget_by_program.py b/policyengine/outputs/macro/comparison/charts/budget_by_program.py index 018702fa..748eca3d 100644 --- a/policyengine/outputs/macro/comparison/charts/budget_by_program.py +++ b/policyengine/outputs/macro/comparison/charts/budget_by_program.py @@ -92,4 +92,4 @@ def create_budget_program_comparison_chart( ), ) - return format_fig(chart) + return format_fig(chart, country=simulation.options.country, add_zero_line=True) diff --git a/policyengine/outputs/macro/comparison/charts/decile.py b/policyengine/outputs/macro/comparison/charts/decile.py index 0b454ccd..18db088a 100644 --- a/policyengine/outputs/macro/comparison/charts/decile.py +++ b/policyengine/outputs/macro/comparison/charts/decile.py @@ -85,4 +85,4 @@ def create_decile_chart( xaxis_tickvals=list(data.keys()), ) - return format_fig(chart) + return format_fig(chart, country=simulation.options.country, add_zero_line=True) diff --git a/policyengine/outputs/macro/comparison/charts/poverty.py b/policyengine/outputs/macro/comparison/charts/poverty.py index 36a93b70..62c3d745 100644 --- a/policyengine/outputs/macro/comparison/charts/poverty.py +++ b/policyengine/outputs/macro/comparison/charts/poverty.py @@ -102,11 +102,11 @@ def poverty_filter(comparison: "PovertyRateMetricComparison"): ] ).update_layout( title=f"{simulation.options.title} would {description}", - yaxis_title="Change in poverty rate", + yaxis_title="Poverty rate change", yaxis_tickformat=".0%" if change_relative else ",.0f", xaxis_title="Group", xaxis_tickvals=x_values, xaxis_ticktext=x_titles, ) - return format_fig(fig) + return format_fig(fig, country=simulation.options.country, add_zero_line=True) From dc6e5c8eddd2667b11881f6451a57dbfc0784a92 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 07:00:06 +1100 Subject: [PATCH 10/17] Add poverty chart --- .../outputs/macro/comparison/charts/budget.py | 4 +- .../comparison/charts/budget_by_program.py | 4 +- .../outputs/macro/comparison/charts/decile.py | 4 +- .../macro/comparison/charts/inequality.py | 78 +++++++++++++++++++ .../macro/comparison/charts/poverty.py | 26 +++++-- 5 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 policyengine/outputs/macro/comparison/charts/inequality.py diff --git a/policyengine/outputs/macro/comparison/charts/budget.py b/policyengine/outputs/macro/comparison/charts/budget.py index 42f478ad..7a8d8b38 100644 --- a/policyengine/outputs/macro/comparison/charts/budget.py +++ b/policyengine/outputs/macro/comparison/charts/budget.py @@ -86,4 +86,6 @@ def create_budget_comparison_chart( ), ) - return format_fig(chart, country=simulation.options.country, add_zero_line=True) + return format_fig( + chart, country=simulation.options.country, add_zero_line=True + ) diff --git a/policyengine/outputs/macro/comparison/charts/budget_by_program.py b/policyengine/outputs/macro/comparison/charts/budget_by_program.py index 748eca3d..27eb23de 100644 --- a/policyengine/outputs/macro/comparison/charts/budget_by_program.py +++ b/policyengine/outputs/macro/comparison/charts/budget_by_program.py @@ -92,4 +92,6 @@ def create_budget_program_comparison_chart( ), ) - return format_fig(chart, country=simulation.options.country, add_zero_line=True) + return format_fig( + chart, country=simulation.options.country, add_zero_line=True + ) diff --git a/policyengine/outputs/macro/comparison/charts/decile.py b/policyengine/outputs/macro/comparison/charts/decile.py index 18db088a..d9592477 100644 --- a/policyengine/outputs/macro/comparison/charts/decile.py +++ b/policyengine/outputs/macro/comparison/charts/decile.py @@ -85,4 +85,6 @@ def create_decile_chart( xaxis_tickvals=list(data.keys()), ) - return format_fig(chart, country=simulation.options.country, add_zero_line=True) + return format_fig( + chart, country=simulation.options.country, add_zero_line=True + ) diff --git a/policyengine/outputs/macro/comparison/charts/inequality.py b/policyengine/outputs/macro/comparison/charts/inequality.py new file mode 100644 index 00000000..5fa00d39 --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/inequality.py @@ -0,0 +1,78 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + +from pydantic import BaseModel +from policyengine.utils.charts import * + + +def create_inequality_chart( + simulation: "Simulation", + relative: bool, +) -> go.Figure: + """Create a budget comparison chart.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + + economy = simulation.calculate_economy_comparison() + + x_values = [ + "Gini index", + "Top 10% share", + "Top 1% share", + ] + + if relative: + data = economy.inequality.relative_change + else: + data = economy.inequality.change + + y_values = [ + data.gini, + data.top_10_share, + data.top_1_share, + ] + + if all(value < 0 for value in y_values): + description = f"lower inequality" + elif all(value > 0 for value in y_values): + description = f"raise inequality" + else: + description = "have an ambiguous effect on inequality" + + if not relative: + y_values = [value * 100 for value in y_values] + + chart = go.Figure( + data=[ + go.Bar( + x=x_values, + y=y_values, + text=[ + f"{value:.1%}" if relative else f"{value:.1f}pp" + for value in y_values + ], + marker=dict( + color=[ + BLUE if value < 0 else DARK_GRAY for value in y_values + ] + ), + ), + ] + ).update_layout( + title=f"{simulation.options.title} would {description}", + yaxis_title="Change" + (" (%)" if relative else ""), + yaxis_ticksuffix="pp" if not relative else "", + yaxis_tickformat=".0%" if relative else ".1f", + uniformtext=dict( + mode="hide", + minsize=12, + ), + ) + + return format_fig( + chart, country=simulation.options.country, add_zero_line=True + ) diff --git a/policyengine/outputs/macro/comparison/charts/poverty.py b/policyengine/outputs/macro/comparison/charts/poverty.py index 62c3d745..bcca889d 100644 --- a/policyengine/outputs/macro/comparison/charts/poverty.py +++ b/policyengine/outputs/macro/comparison/charts/poverty.py @@ -82,12 +82,18 @@ def poverty_filter(comparison: "PovertyRateMetricComparison"): if change_relative: y_values = [poverty.relative_change for poverty in poverty_rates] - else: + elif not rate_relative: y_values = [poverty.change for poverty in poverty_rates] + else: + y_values = [poverty.change * 100 for poverty in poverty_rates] colors = [BLUE if value > 0 else DARK_GRAY for value in y_values] text = [ - f"{value:.1%}" if change_relative else f"{value:,.0f}" + ( + f"{value:.1%}" + if change_relative + else (f"{value:,.0f}" if not rate_relative else f"{value:.1f}pp") + ) for value in y_values ] @@ -102,11 +108,21 @@ def poverty_filter(comparison: "PovertyRateMetricComparison"): ] ).update_layout( title=f"{simulation.options.title} would {description}", - yaxis_title="Poverty rate change", - yaxis_tickformat=".0%" if change_relative else ",.0f", + yaxis_title="Poverty rate change" + + (" (%)" if rate_relative and not change_relative else ""), + yaxis_tickformat=( + ".0%" + if change_relative + else (",.0f" if not rate_relative else ".1f") + ), + yaxis_ticksuffix=( + "pp" if (rate_relative and not change_relative) else "" + ), xaxis_title="Group", xaxis_tickvals=x_values, xaxis_ticktext=x_titles, ) - return format_fig(fig, country=simulation.options.country, add_zero_line=True) + return format_fig( + fig, country=simulation.options.country, add_zero_line=True + ) From 02d0b5ffc35471a9e8a1a6a5b7fe5f7fbeb2b7fb Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 09:18:27 +1100 Subject: [PATCH 11/17] Add labor supply charts --- .../macro/comparison/charts/labor_supply.py | 126 ++++++++++++++++++ .../outputs/macro/comparison/labor_supply.py | 21 ++- policyengine/utils/charts.py | 6 + 3 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 policyengine/outputs/macro/comparison/charts/labor_supply.py diff --git a/policyengine/outputs/macro/comparison/charts/labor_supply.py b/policyengine/outputs/macro/comparison/charts/labor_supply.py new file mode 100644 index 00000000..c3946e60 --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/labor_supply.py @@ -0,0 +1,126 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + from policyengine.outputs.macro.comparison.calculate_economy_comparison import ( + LaborSupplyMetricImpact, + ) + +from pydantic import BaseModel +from policyengine.utils.charts import * +from typing import Literal + + +def create_labor_supply_chart( + simulation: "Simulation", + decile: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "all"] | None, + elasticity: Literal["income", "substitution", "all"] | None, + unit: Literal["earnings", "hours"] | None, + change_relative: bool = True, + change_average: bool = False, +) -> go.Figure: + """Create a budget comparison chart.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + + economy = simulation.calculate_economy_comparison() + + def lsr_filter(comparison: "LaborSupplyMetricImpact"): + if decile is not None: + if comparison.decile != decile: + return False + if elasticity is not None: + if comparison.elasticity != elasticity: + return False + if unit is not None: + if comparison.unit != unit: + return False + return True + + overall_lsr_impact = list( + filter( + lambda comparison: comparison.decile == "all" + and comparison.elasticity == "all" + and comparison.unit == "earnings", + economy.labor_supply, + ) + )[0].relative_change + + overall_lsr_impact = round(overall_lsr_impact, 3) + + if overall_lsr_impact > 0: + description = f"raise labor supply by {overall_lsr_impact:.1%}" + elif overall_lsr_impact < 0: + description = f"lower labor supply by {-overall_lsr_impact:.1%}" + else: + description = "have no effect on labor supply" + + lsr_impacts = list( + filter( + lsr_filter, + economy.labor_supply, + ) + ) + + if decile is None: + x_values = [lsr.decile for lsr in lsr_impacts] + x_titles = list(range(1, 11)) + elif elasticity is None: + x_values = [lsr.elasticity for lsr in lsr_impacts] + x_titles = ["Income", "Substitution", "All"] + elif unit is None: + x_values = [lsr.unit for lsr in lsr_impacts] + x_titles = ["Earnings", "Hours"] + + if change_relative: + y_values = [lsr.relative_change for lsr in lsr_impacts] + elif change_average: + y_values = [lsr.average_change for lsr in lsr_impacts] + else: + y_values = [lsr.change / 1e9 for lsr in lsr_impacts] + + colors = [BLUE if value > 0 else DARK_GRAY for value in y_values] + text = [ + ( + f"{value:.1%}" + if change_relative + else ( + f"{value:,.1%}" + if change_relative + else ( + f"${value:.1f}bn" + if not change_average + else f"${value:,.0f}" + ) + ) + ) + for value in y_values + ] + + fig = go.Figure( + data=[ + go.Bar( + x=x_values, + y=y_values, + marker=dict(color=colors), + text=text, + ) + ] + ).update_layout( + title=f"{simulation.options.title} would {description}", + yaxis_title="Labor supply change" + + (" ($bn)" if not change_average else ""), + yaxis_tickformat=(".0%" if change_relative else ",.0f"), + yaxis_ticksuffix=( + " ($bn)" if not change_average and not change_relative else "" + ), + xaxis_title="Group", + xaxis_tickvals=x_values, + xaxis_ticktext=x_titles, + ) + + return format_fig( + fig, country=simulation.options.country, add_zero_line=True + ) diff --git a/policyengine/outputs/macro/comparison/labor_supply.py b/policyengine/outputs/macro/comparison/labor_supply.py index 6f1b8126..fd8269e6 100644 --- a/policyengine/outputs/macro/comparison/labor_supply.py +++ b/policyengine/outputs/macro/comparison/labor_supply.py @@ -25,6 +25,8 @@ class LaborSupplyMetricImpact(BaseModel): """The change in the labor supply metric value.""" relative_change: float """The relative change in the labor supply metric value.""" + average_change: float + """The average change in the labor supply metric value (per household).""" def calculate_labor_supply_impact( @@ -53,6 +55,8 @@ def calculate_labor_supply_impact( ) ) + return lsr_metrics + def calculate_specific_lsr_metric( baseline: Microsimulation, @@ -82,19 +86,25 @@ def calculate_specific_lsr_metric( else: variable = "weekly_hours_worked" - baseline_values = baseline.calculate(baseline_variable) - reform_values = reformed.calculate(baseline_variable) + reformed.calculate( - variable - ) + baseline_values = baseline.calculate(baseline_variable, map_to="household") + reform_values = reformed.calculate( + baseline_variable, map_to="household" + ) + reformed.calculate(variable, map_to="household") if decile == "all": in_decile = np.ones_like(baseline_values, dtype=bool) else: - in_decile = reformed.calculate("household_income_decile") == decile + in_decile = ( + reformed.calculate("household_income_decile").values == decile + ) baseline_total = (baseline_values * in_decile).sum() reform_total = (reform_values * in_decile).sum() + households = ( + in_decile * baseline.calculate("household_weight").values + ).sum() change = reform_total - baseline_total + average_change = change / households relative_change = change / baseline_total return LaborSupplyMetricImpact( @@ -104,6 +114,7 @@ def calculate_specific_lsr_metric( baseline=baseline_total, reform=reform_total, change=change, + average_change=average_change, relative_change=relative_change, ) diff --git a/policyengine/utils/charts.py b/policyengine/utils/charts.py index 52aae5b3..9f624c47 100644 --- a/policyengine/utils/charts.py +++ b/policyengine/utils/charts.py @@ -138,9 +138,15 @@ def format_fig( yaxis_title=correct_text_currency( fig.layout.yaxis.title.text or "", currency ), + yaxis_ticksuffix=correct_text_currency( + fig.layout.yaxis.ticksuffix or "", currency + ), xaxis_title=correct_text_currency( fig.layout.xaxis.title.text or "", currency ), + xaxis_ticksuffix=correct_text_currency( + fig.layout.xaxis.ticksuffix or "", currency + ), ) fig.update_layout( From c018a04dffb8aec967e2dece4d889ab6e3594109 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 11:24:07 +1100 Subject: [PATCH 12/17] Add all charts function --- .../calculate_economy_comparison.py | 3 + .../comparison/charts/create_all_charts.py | 65 ++++++++++++++ .../macro/comparison/charts/poverty.py | 25 ++++-- policyengine/outputs/macro/single/poverty.py | 88 +++++++++++-------- policyengine/simulation.py | 10 ++- 5 files changed, 147 insertions(+), 44 deletions(-) create mode 100644 policyengine/outputs/macro/comparison/charts/create_all_charts.py diff --git a/policyengine/outputs/macro/comparison/calculate_economy_comparison.py b/policyengine/outputs/macro/comparison/calculate_economy_comparison.py index 309d0e52..f9d04fd6 100644 --- a/policyengine/outputs/macro/comparison/calculate_economy_comparison.py +++ b/policyengine/outputs/macro/comparison/calculate_economy_comparison.py @@ -51,6 +51,8 @@ class Headlines(BaseModel): class PovertyRateMetricComparison(BaseModel): age_group: Literal["child", "working_age", "senior", "all"] """The age group of the population.""" + gender: Literal["male", "female", "all"] + """The gender of the population.""" racial_group: Literal["white", "black", "hispanic", "other", "all"] """The racial group of the population.""" relative: bool @@ -146,6 +148,7 @@ def calculate_economy_comparison( poverty_metrics.append( PovertyRateMetricComparison( age_group=baseline_metric.age_group, + gender=baseline_metric.gender, racial_group=baseline_metric.racial_group, relative=baseline_metric.relative, poverty_rate=baseline_metric.poverty_rate, diff --git a/policyengine/outputs/macro/comparison/charts/create_all_charts.py b/policyengine/outputs/macro/comparison/charts/create_all_charts.py new file mode 100644 index 00000000..eb6b2e3a --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/create_all_charts.py @@ -0,0 +1,65 @@ +import plotly.express as px +import plotly.graph_objects as go +import typing + +if typing.TYPE_CHECKING: + from policyengine import Simulation + +from pydantic import BaseModel +from policyengine.utils.charts import * +from .budget import create_budget_comparison_chart +from .budget_by_program import create_budget_program_comparison_chart +from .decile import create_decile_chart +from .winners_losers import create_winners_losers_chart +from .poverty import create_poverty_chart +from .inequality import create_inequality_chart +from .labor_supply import create_labor_supply_chart +from typing import Any + + +class MacroCharts(BaseModel): + budget: Any + budget_programs: Any + decile_income_relative: Any + decile_income_average: Any + decile_wealth_relative: Any + decile_wealth_average: Any + winners_and_losers_income_decile: Any + winners_and_losers_wealth_decile: Any + + +def create_all_charts( + simulation: "Simulation", +) -> MacroCharts: + """Create all charts.""" + if not simulation.is_comparison: + raise ValueError("Simulation must be a comparison simulation.") + if not simulation.options.scope == "macro": + raise ValueError( + "This function is only available for macro simulations." + ) + + return MacroCharts( + budget=create_budget_comparison_chart(simulation).to_dict(), + budget_programs=create_budget_program_comparison_chart( + simulation + ).to_dict(), + decile_income_relative=create_decile_chart( + simulation, "income", True + ).to_dict(), + decile_income_average=create_decile_chart( + simulation, "income", False + ).to_dict(), + decile_wealth_relative=create_decile_chart( + simulation, "wealth", True + ).to_dict(), + decile_wealth_average=create_decile_chart( + simulation, "wealth", True + ).to_dict(), + winners_and_losers_income_decile=create_winners_losers_chart( + simulation, "income" + ).to_dict(), + winners_and_losers_wealth_decile=create_winners_losers_chart( + simulation, "wealth" + ).to_dict(), + ) diff --git a/policyengine/outputs/macro/comparison/charts/poverty.py b/policyengine/outputs/macro/comparison/charts/poverty.py index bcca889d..271071fb 100644 --- a/policyengine/outputs/macro/comparison/charts/poverty.py +++ b/policyengine/outputs/macro/comparison/charts/poverty.py @@ -16,6 +16,7 @@ def create_poverty_chart( simulation: "Simulation", age_group: str, + gender: str, racial_group: str, poverty_rate: str, rate_relative: bool, @@ -34,6 +35,9 @@ def poverty_filter(comparison: "PovertyRateMetricComparison"): if racial_group is not None: if comparison.racial_group != racial_group: return False + if gender is not None: + if comparison.gender != gender: + return False if poverty_rate is not None: if comparison.poverty_rate != poverty_rate: return False @@ -42,10 +46,21 @@ def poverty_filter(comparison: "PovertyRateMetricComparison"): return False return True + poverty_rates = list( + filter( + poverty_filter, + economy.poverty, + ) + ) + + if len(poverty_rates) == 0: + raise ValueError("No data found for the selected filters.") + overall_poverty_rate = list( filter( lambda comparison: comparison.age_group == "all" and comparison.racial_group == "all" + and comparison.gender == "all" and comparison.poverty_rate == (poverty_rate or "regular"), economy.poverty, ) @@ -60,16 +75,12 @@ def poverty_filter(comparison: "PovertyRateMetricComparison"): else: description = "have no effect on the poverty rate" - poverty_rates = list( - filter( - poverty_filter, - economy.poverty, - ) - ) - if age_group is None: x_values = [poverty.age_group for poverty in poverty_rates] x_titles = ["Child", "Working-age", "Senior", "All"] + elif gender is None: + x_values = [poverty.gender for poverty in poverty_rates] + x_titles = ["Male", "Female", "All"] elif racial_group is None: x_values = [poverty.racial_group for poverty in poverty_rates] x_titles = ["White", "Black", "Hispanic", "Other", "All"] diff --git a/policyengine/outputs/macro/single/poverty.py b/policyengine/outputs/macro/single/poverty.py index 2f3c2ff4..76f294ae 100644 --- a/policyengine/outputs/macro/single/poverty.py +++ b/policyengine/outputs/macro/single/poverty.py @@ -15,6 +15,8 @@ class PovertyRateMetric(BaseModel): """The age group of the population.""" racial_group: Literal["white", "black", "hispanic", "other", "all"] """The racial group of the population.""" + gender: Literal["male", "female", "all"] + """The gender of the population.""" relative: bool """Whether the poverty rate is relative to the total population, or a headcount.""" poverty_rate: Literal[ @@ -46,6 +48,7 @@ def calculate_poverty( poverty_metrics = [] age = simulation.calculate("age") + gender = simulation.calculate("gender") person_weight = simulation.calculate("person_weight").values if options.country == "us": racial_groups = ["white", "black", "hispanic", "other", "all"] @@ -54,44 +57,57 @@ def calculate_poverty( for age_group in ["child", "working_age", "senior", "all"]: lower_age, upper_age = AGE_BOUNDS[age_group] in_age_group = (age >= lower_age) & (age < upper_age) - for racial_group in racial_groups: - if racial_group != "all": - in_racial_group = simulation.calculate("race") == racial_group - else: - in_racial_group = np.ones_like(age, dtype=bool) - for relative in [True, False]: - for poverty_rate in ["regular", "deep"]: - if poverty_rate in ("regular", "uk_hbai_bhc", "us_spm"): - in_poverty = simulation.calculate( - "in_poverty", map_to="person" - ) - elif poverty_rate in ( - "deep", - "uk_hbai_bhc_half", - "us_spm_half", - ): - in_poverty = simulation.calculate( - "in_deep_poverty", map_to="person" - ) + for gender_group in ["male", "female", "all"]: + in_gender = (gender_group == "all") | ( + gender == gender_group.upper() + ) + for racial_group in racial_groups: + if racial_group != "all": + in_racial_group = ( + simulation.calculate("race") == racial_group + ) + else: + in_racial_group = np.ones_like(age, dtype=bool) + for relative in [True, False]: + for poverty_rate in ["regular", "deep"]: + if poverty_rate in ( + "regular", + "uk_hbai_bhc", + "us_spm", + ): + in_poverty = simulation.calculate( + "in_poverty", map_to="person" + ) + elif poverty_rate in ( + "deep", + "uk_hbai_bhc_half", + "us_spm_half", + ): + in_poverty = simulation.calculate( + "in_deep_poverty", map_to="person" + ) - in_group = np.array(in_age_group & in_racial_group) - total_in_group = (in_group * person_weight).sum() - total_in_group_in_poverty = ( - in_group * in_poverty * person_weight - ).sum() - if relative: - result = total_in_group_in_poverty / total_in_group - else: - result = total_in_group_in_poverty + in_group = np.array( + in_age_group & in_racial_group & in_gender + ) + total_in_group = (in_group * person_weight).sum() + total_in_group_in_poverty = ( + in_group * in_poverty * person_weight + ).sum() + if relative: + result = total_in_group_in_poverty / total_in_group + else: + result = total_in_group_in_poverty - poverty_metrics.append( - PovertyRateMetric( - age_group=age_group, - racial_group=racial_group, - relative=relative, - poverty_rate=poverty_rate, - value=result, + poverty_metrics.append( + PovertyRateMetric( + age_group=age_group, + gender=gender_group, + racial_group=racial_group, + relative=relative, + poverty_rate=poverty_rate, + value=result, + ) ) - ) return poverty_metrics diff --git a/policyengine/simulation.py b/policyengine/simulation.py index 5f6c7af0..f62a9011 100644 --- a/policyengine/simulation.py +++ b/policyengine/simulation.py @@ -42,7 +42,11 @@ HouseholdComparison, calculate_household_comparison, ) -from typing import Any +from .outputs.macro.comparison.charts.create_all_charts import ( + create_all_charts, + MacroCharts, +) +from typing import Any, Tuple CountryType = Literal["uk", "us"] ScopeType = Literal["household", "macro"] @@ -349,3 +353,7 @@ def calculate_single_household(self) -> SingleHousehold: def calculate_household_comparison(self) -> HouseholdComparison: """Calculate comparison statistics between two household scenarios.""" return calculate_household_comparison(self) + + def create_all_charts(self) -> MacroCharts: + """Create all macro charts for the simulation.""" + return create_all_charts(self) From 35bf1eefa48d3660861f5b392fd6d20367f921d9 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 11:30:44 +1100 Subject: [PATCH 13/17] Fix bug --- policyengine/outputs/macro/single/poverty.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/policyengine/outputs/macro/single/poverty.py b/policyengine/outputs/macro/single/poverty.py index 76f294ae..9b0a7f70 100644 --- a/policyengine/outputs/macro/single/poverty.py +++ b/policyengine/outputs/macro/single/poverty.py @@ -48,7 +48,15 @@ def calculate_poverty( poverty_metrics = [] age = simulation.calculate("age") - gender = simulation.calculate("gender") + if simulation.options.country == "uk": + gender = simulation.calculate("gender") + else: + gender = simulation.calculate("is_male").map( + { + True: "MALE", + False: "FEMALE", + } + ) person_weight = simulation.calculate("person_weight").values if options.country == "us": racial_groups = ["white", "black", "hispanic", "other", "all"] From 6f2d05cff6052e6f6e7b760c1efd344dc96a43ca Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 11:34:02 +1100 Subject: [PATCH 14/17] FIx bad ref --- policyengine/outputs/macro/single/poverty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine/outputs/macro/single/poverty.py b/policyengine/outputs/macro/single/poverty.py index 9b0a7f70..f0f2c93c 100644 --- a/policyengine/outputs/macro/single/poverty.py +++ b/policyengine/outputs/macro/single/poverty.py @@ -48,7 +48,7 @@ def calculate_poverty( poverty_metrics = [] age = simulation.calculate("age") - if simulation.options.country == "uk": + if options.country == "uk": gender = simulation.calculate("gender") else: gender = simulation.calculate("is_male").map( From 056fac4574aa3d83ede967bd5458179ab445b4a7 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 13:01:17 +1100 Subject: [PATCH 15/17] Add to docs --- changelog_entry.yaml | 4 + docs/_toc.yml | 4 + .../calculate_economy_comparison.ipynb | 267 ++++++++++++++++++ .../calculate_household_comparison.ipynb | 155 ++++++++++ docs/reference/calculate_single_economy.ipynb | 157 ++++++++++ .../calculate_single_household.ipynb | 123 ++++++++ docs/reference/simulation.ipynb | 2 +- 7 files changed, 711 insertions(+), 1 deletion(-) create mode 100644 docs/reference/calculate_economy_comparison.ipynb create mode 100644 docs/reference/calculate_household_comparison.ipynb create mode 100644 docs/reference/calculate_single_economy.ipynb create mode 100644 docs/reference/calculate_single_household.ipynb diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29b..d1b35788 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Chart generation for macro charts. diff --git a/docs/_toc.yml b/docs/_toc.yml index 6b3c3baa..02f70105 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -4,4 +4,8 @@ parts: - caption: Reference chapters: - file: reference/simulation + - file: reference/calculate_single_household + - file: reference/calculate_household_comparison + - file: reference/calculate_single_economy + - file: reference/calculate_economy_comparison \ No newline at end of file diff --git a/docs/reference/calculate_economy_comparison.ipynb b/docs/reference/calculate_economy_comparison.ipynb new file mode 100644 index 00000000..81819fdb --- /dev/null +++ b/docs/reference/calculate_economy_comparison.ipynb @@ -0,0 +1,267 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Compare outcomes large population scenarios\n", + "\n", + "Use `Simulation.calculate_economy_comparison()` to use PolicyEngine's tax-benefit model to compare how taxes, benefits and other household properties change under a reform scenario. This notebook demonstrates how to use this function." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "EconomyComparison(headlines=Headlines(budgetary_impact=-20688882917.062927, poverty_impact=-0.009522052793635733, winner_share=0.8345739005338316), fiscal=FiscalComparison(baseline=FiscalSummary(tax_revenue=658911285719.5891, federal_tax=658911285719.5891, federal_balance=309089098855.4849, state_tax=0.0, government_spending=349822186864.1042, tax_benefit_programs={'income_tax': 333376287037.05945, 'national_insurance': 52985626776.773834, 'ni_employer': 126330649370.35953, 'vat': 211671832822.39133, 'council_tax': 49007055050.00724, 'fuel_duty': 26506672341.204205, 'tax_credits': -34929879.49872104, 'universal_credit': -73459549194.97665, 'child_benefit': -14311471487.935827, 'state_pension': -132795868621.44594, 'pension_credit': -6252358021.417119}, household_net_income=1566028514855.0789), reform=FiscalSummary(tax_revenue=637686731286.3723, federal_tax=637686731286.3723, federal_balance=288400215938.422, state_tax=0.0, government_spending=349286515347.9503, tax_benefit_programs={'income_tax': 312151564998.6612, 'national_insurance': 52985626776.773834, 'ni_employer': 126330649370.35953, 'vat': 211671832822.39133, 'council_tax': 49007055050.00724, 'fuel_duty': 26506672341.204205, 'tax_credits': -34929879.49872104, 'universal_credit': -73099552696.20555, 'child_benefit': -14311471487.935827, 'state_pension': -132795868621.44594, 'pension_credit': -6181432287.167828}, household_net_income=1586717263122.5647), change=FiscalSummary(tax_revenue=-21224554433.216797, federal_tax=-21224554433.216797, federal_balance=-20688882917.062927, state_tax=0.0, government_spending=-535671516.1538696, tax_benefit_programs={'income_tax': -21224722038.398254, 'national_insurance': 0.0, 'ni_employer': 0.0, 'vat': 0.0, 'council_tax': 0.0, 'fuel_duty': 0.0, 'tax_credits': 0.0, 'universal_credit': 359996498.7711029, 'child_benefit': 0.0, 'state_pension': 0.0, 'pension_credit': 70925734.24929142}, household_net_income=20688748267.48584), relative_change=FiscalSummary(tax_revenue=-0.03221155092227281, federal_tax=-0.03221155092227281, federal_balance=-0.06693501321680725, state_tax=0.0, government_spending=-0.0015312679877619157, tax_benefit_programs={'income_tax': -0.06366596204858095, 'national_insurance': 0.0, 'ni_employer': 0.0, 'vat': 0.0, 'council_tax': 0.0, 'fuel_duty': 0.0, 'tax_credits': -0.0, 'universal_credit': -0.004900608603186478, 'child_benefit': -0.0, 'state_pension': -0.0, 'pension_credit': -0.01134383763795021}, household_net_income=0.013210965235457662)), inequality=InequalityComparison(baseline=InequalitySummary(gini=0.36255397405553097, top_10_share=0.3260927004295773, top_1_share=0.13145609415091833), reform=InequalitySummary(gini=0.36210731670764496, top_10_share=0.32470725674255146, top_1_share=0.1300325825945402), change=InequalitySummary(gini=-0.00044665734788601474, top_10_share=-0.0013854436870258113, top_1_share=-0.0014235115563781264), relative_change=InequalitySummary(gini=-0.0012319747674800056, top_10_share=-0.004248619135603775, top_1_share=-0.010828798509286775)), distributional=DecileImpacts(income=IncomeMeasureSpecificDecileImpacts(income_change=IncomeMeasureSpecificDecileIncomeChange(relative={1: 0.006888474767108438, 2: 0.012069475259054711, 3: 0.01362845736941666, 4: 0.016099117695117186, 5: 0.01374886913709005, 6: 0.016868831714948112, 7: 0.01753355279609095, 8: 0.016653943875645758, 9: 0.016768055951517695, 10: 0.007412182918826308}, average={1: 104.9760740138715, 2: 345.3564489476293, 3: 505.97466813661975, 4: 693.4608276094718, 5: 633.0583755079966, 6: 916.7936598562601, 7: 1160.9595929383368, 8: 1339.97187253835, 9: 1645.9437072259466, 10: 1555.8434910392261}), winners_and_losers=IncomeMeasureSpecificDecileWinnersLosers(deciles={1: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.00016742770708814546, lose_share=0.00016742770708814546, no_change_share=0.7777537048412824, gain_share=0.22207886745162944, gain_less_than_5_percent_share=0.1144106783823729, gain_more_than_5_percent_share=0.10766818906925654), 2: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.008307581771885529, lose_share=0.008307581771885529, no_change_share=0.32972785891469386, gain_share=0.6619645593134206, gain_less_than_5_percent_share=0.5622028657847857, gain_more_than_5_percent_share=0.09976169352863486), 3: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=7.96157411760938e-05, lose_share=7.96157411760938e-05, no_change_share=0.24138511806270913, gain_share=0.7585352661961148, gain_less_than_5_percent_share=0.5985049210228661, gain_more_than_5_percent_share=0.16003034517324866), 4: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.1274989463786316, gain_share=0.8725010536213684, gain_less_than_5_percent_share=0.8390939448490379, gain_more_than_5_percent_share=0.033407108772330475), 5: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.039569164250678565, gain_share=0.9604308357493214, gain_less_than_5_percent_share=0.9576740433060911, gain_more_than_5_percent_share=0.002756792443230341), 6: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.047391872522306096, gain_share=0.9526081274776939, gain_less_than_5_percent_share=0.9027059122375464, gain_more_than_5_percent_share=0.049902215240147485), 7: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=5.104037721866914e-07, lose_share=5.104037721866914e-07, no_change_share=0.018556113733354175, gain_share=0.9814433758628737, gain_less_than_5_percent_share=0.9347186693787157, gain_more_than_5_percent_share=0.046724706484157955), 8: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.0078485273494483, gain_share=0.9921514726505517, gain_less_than_5_percent_share=0.9786379299391095, gain_more_than_5_percent_share=0.013513542711442263), 9: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.015484773300234851, gain_share=0.9845152266997651, gain_less_than_5_percent_share=0.9787986406237482, gain_more_than_5_percent_share=0.00571658607601697), 10: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.09527409658848911, gain_share=0.9047259034115109, gain_less_than_5_percent_share=0.9047259034115109, gain_more_than_5_percent_share=0.0)}, all=IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0008383266758003545, lose_share=0.0008383266758003545, no_change_share=0.16458777279036807, gain_share=0.8345739005338316, gain_less_than_5_percent_share=0.7829682328930914, gain_more_than_5_percent_share=0.051605667640740344))), wealth=IncomeMeasureSpecificDecileImpacts(income_change=IncomeMeasureSpecificDecileIncomeChange(relative={1: 0.008709379861137914, 2: 0.009055801198624003, 3: 0.012492298883059996, 4: 0.011526120801509328, 5: 0.013211703237973082, 6: 0.015499285448067033, 7: 0.01507564337455649, 8: 0.013872555873086258, 9: 0.01486308861202786, 10: 0.013423195198765777}, average={1: 369.7032427197275, 2: 348.23782779938443, 3: 552.8656647582718, 4: 568.6862981673706, 5: 788.7199254212952, 6: 1082.0251273410129, 7: 995.5414649462065, 8: 980.0266932010006, 9: 1104.9136440223838, 10: 1148.3564916388677}), winners_and_losers=IncomeMeasureSpecificDecileWinnersLosers(deciles={1: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=2.6088522714454665e-05, lose_share=2.6088522714454665e-05, no_change_share=0.4508804429091072, gain_share=0.5490934685681783, gain_less_than_5_percent_share=0.5339710702731965, gain_more_than_5_percent_share=0.015122398294981835), 2: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.494450094962446, gain_share=0.505549905037554, gain_less_than_5_percent_share=0.4987036905338977, gain_more_than_5_percent_share=0.006846214503656335), 3: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.02404055636315165, gain_share=0.9759594436368484, gain_less_than_5_percent_share=0.9651062221540542, gain_more_than_5_percent_share=0.010853221482794195), 4: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=8.272884193213454e-07, lose_share=8.272884193213454e-07, no_change_share=0.36369619644130957, gain_share=0.6363029762702711, gain_less_than_5_percent_share=0.5981897126055431, gain_more_than_5_percent_share=0.038113263664727975), 5: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.00024460254119754784, lose_share=0.00024460254119754784, no_change_share=0.16613074345434808, gain_share=0.8336246540044544, gain_less_than_5_percent_share=0.8172183492970724, gain_more_than_5_percent_share=0.01640630470738194), 6: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.025790631489697343, gain_share=0.9742093685103027, gain_less_than_5_percent_share=0.9603855562910443, gain_more_than_5_percent_share=0.013823812219258384), 7: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=4.163438722119544e-05, lose_share=4.163438722119544e-05, no_change_share=0.08407934672412146, gain_share=0.9158790188886573, gain_less_than_5_percent_share=0.8182000678180964, gain_more_than_5_percent_share=0.09767895107056095), 8: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.056524409525954916, gain_share=0.9434755904740451, gain_less_than_5_percent_share=0.880163733821962, gain_more_than_5_percent_share=0.06331185665208312), 9: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.008053685714292545, lose_share=0.008053685714292545, no_change_share=0.027806404912023186, gain_share=0.9641399093736843, gain_less_than_5_percent_share=0.9093577338323074, gain_more_than_5_percent_share=0.05478217554137677), 10: IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0, lose_share=0.0, no_change_share=0.06985295508301145, gain_share=0.9301470449169885, gain_less_than_5_percent_share=0.7369159251943521, gain_more_than_5_percent_share=0.1932311197226364)}, all=IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes(lose_more_than_5_percent_share=0.0, lose_less_than_5_percent_share=0.0008383266758003545, lose_share=0.0008383266758003545, no_change_share=0.16458777279036807, gain_share=0.8345739005338316, gain_less_than_5_percent_share=0.7829682328930914, gain_more_than_5_percent_share=0.051605667640740344)))), poverty=[PovertyRateMetricComparison(age_group='child', gender='male', racial_group='all', relative=True, poverty_rate='regular', baseline=0.0937829241156578, reform=0.09370723366737366, change=-7.569044828414917e-05, relative_change=-0.000807081342343345), PovertyRateMetricComparison(age_group='child', gender='male', racial_group='all', relative=True, poverty_rate='deep', baseline=0.006947669200599194, reform=0.0069471439346671104, change=-5.252659320831299e-07, relative_change=-7.560318675475064e-05), PovertyRateMetricComparison(age_group='child', gender='male', racial_group='all', relative=False, poverty_rate='regular', baseline=770371.125, reform=769749.375, change=-621.75, relative_change=-0.000807078536335328), PovertyRateMetricComparison(age_group='child', gender='male', racial_group='all', relative=False, poverty_rate='deep', baseline=57070.984375, reform=57066.66796875, change=-4.31640625, relative_change=-7.5632237594465e-05), PovertyRateMetricComparison(age_group='child', gender='female', racial_group='all', relative=True, poverty_rate='regular', baseline=0.08119998127222061, reform=0.0811329185962677, change=-6.706267595291138e-05, relative_change=-0.0008258952145331866), PovertyRateMetricComparison(age_group='child', gender='female', racial_group='all', relative=True, poverty_rate='deep', baseline=0.005070670507848263, reform=0.00507007073611021, change=-5.997717380523682e-07, relative_change=-0.00011828253031311259), PovertyRateMetricComparison(age_group='child', gender='female', racial_group='all', relative=False, poverty_rate='regular', baseline=866217.375, reform=865501.9375, change=-715.4375, relative_change=-0.0008259329824687481), PovertyRateMetricComparison(age_group='child', gender='female', racial_group='all', relative=False, poverty_rate='deep', baseline=54092.4140625, reform=54086.015625, change=-6.3984375, relative_change=-0.00011828715007259712), PovertyRateMetricComparison(age_group='child', gender='all', racial_group='all', relative=True, poverty_rate='regular', baseline=0.0866740345954895, reform=0.08660321682691574, change=-7.081776857376099e-05, relative_change=-0.0008170586370447596), PovertyRateMetricComparison(age_group='child', gender='all', racial_group='all', relative=True, poverty_rate='deep', baseline=0.005887233652174473, reform=0.005886665545403957, change=-5.681067705154419e-07, relative_change=-9.649808451302242e-05), PovertyRateMetricComparison(age_group='child', gender='all', racial_group='all', relative=False, poverty_rate='regular', baseline=1636588.5, reform=1635251.25, change=-1337.25, relative_change=-0.0008170960507176972), PovertyRateMetricComparison(age_group='child', gender='all', racial_group='all', relative=False, poverty_rate='deep', baseline=111163.3828125, reform=111152.65625, change=-10.7265625, relative_change=-9.649366750643566e-05), PovertyRateMetricComparison(age_group='working_age', gender='male', racial_group='all', relative=True, poverty_rate='regular', baseline=0.09058031439781189, reform=0.08946093916893005, change=-0.001119375228881836, relative_change=-0.012357820088432772), PovertyRateMetricComparison(age_group='working_age', gender='male', racial_group='all', relative=True, poverty_rate='deep', baseline=0.0320294052362442, reform=0.03202878311276436, change=-6.221234798431396e-07, relative_change=-1.9423510216766375e-05), PovertyRateMetricComparison(age_group='working_age', gender='male', racial_group='all', relative=False, poverty_rate='regular', baseline=2207394.25, reform=2180115.75, change=-27278.5, relative_change=-0.012357783390982377), PovertyRateMetricComparison(age_group='working_age', gender='male', racial_group='all', relative=False, poverty_rate='deep', baseline=780539.625, reform=780524.5, change=-15.125, relative_change=-1.9377619682024472e-05), PovertyRateMetricComparison(age_group='working_age', gender='female', racial_group='all', relative=True, poverty_rate='regular', baseline=0.06885619461536407, reform=0.06755512952804565, change=-0.0013010650873184204, relative_change=-0.018895396334146386), PovertyRateMetricComparison(age_group='working_age', gender='female', racial_group='all', relative=True, poverty_rate='deep', baseline=0.006544755306094885, reform=0.006544151809066534, change=-6.034970283508301e-07, relative_change=-9.221078560245269e-05), PovertyRateMetricComparison(age_group='working_age', gender='female', racial_group='all', relative=False, poverty_rate='regular', baseline=1406585.625, reform=1380007.625, change=-26578.0, relative_change=-0.01889540140864158), PovertyRateMetricComparison(age_group='working_age', gender='female', racial_group='all', relative=False, poverty_rate='deep', baseline=133695.4375, reform=133683.109375, change=-12.328125, relative_change=-9.221051391525608e-05), PovertyRateMetricComparison(age_group='working_age', gender='all', racial_group='all', relative=True, poverty_rate='regular', baseline=0.08067396283149719, reform=0.07947173714637756, change=-0.001202225685119629, relative_change=-0.014902276309776728), PovertyRateMetricComparison(age_group='working_age', gender='all', racial_group='all', relative=True, poverty_rate='deep', baseline=0.02040824294090271, reform=0.020407630130648613, change=-6.128102540969849e-07, relative_change=-3.0027585219929702e-05), PovertyRateMetricComparison(age_group='working_age', gender='all', racial_group='all', relative=False, poverty_rate='regular', baseline=3613979.5, reform=3560123.0, change=-53856.5, relative_change=-0.014902270474970874), PovertyRateMetricComparison(age_group='working_age', gender='all', racial_group='all', relative=False, poverty_rate='deep', baseline=914235.1875, reform=914207.75, change=-27.4375, relative_change=-3.001142416649764e-05), PovertyRateMetricComparison(age_group='senior', gender='male', racial_group='all', relative=True, poverty_rate='regular', baseline=0.019906071946024895, reform=0.01964147388935089, change=-0.0002645980566740036, relative_change=-0.013292328963316242), PovertyRateMetricComparison(age_group='senior', gender='male', racial_group='all', relative=True, poverty_rate='deep', baseline=0.0001492029696237296, reform=0.00014823427773080766, change=-9.686918929219246e-07, relative_change=-0.0064924437855683375), PovertyRateMetricComparison(age_group='senior', gender='male', racial_group='all', relative=False, poverty_rate='regular', baseline=120630.84375, reform=119027.3828125, change=-1603.4609375, relative_change=-0.013292296461285425), PovertyRateMetricComparison(age_group='senior', gender='male', racial_group='all', relative=False, poverty_rate='deep', baseline=904.1703491210938, reform=898.3001098632812, change=-5.8702392578125, relative_change=-0.006492404073545117), PovertyRateMetricComparison(age_group='senior', gender='female', racial_group='all', relative=True, poverty_rate='regular', baseline=0.04476610943675041, reform=0.04424646124243736, change=-0.0005196481943130493, relative_change=-0.011608071392651511), PovertyRateMetricComparison(age_group='senior', gender='female', racial_group='all', relative=True, poverty_rate='deep', baseline=0.0012297447538003325, reform=0.0012286300770938396, change=-1.1146767064929008e-06, relative_change=-0.0009064293244985742), PovertyRateMetricComparison(age_group='senior', gender='female', racial_group='all', relative=False, poverty_rate='regular', baseline=315600.8125, reform=311937.28125, change=-3663.53125, relative_change=-0.011608117295325404), PovertyRateMetricComparison(age_group='senior', gender='female', racial_group='all', relative=False, poverty_rate='deep', baseline=8669.693359375, reform=8661.8349609375, change=-7.8583984375, relative_change=-0.000906421728169001), PovertyRateMetricComparison(age_group='senior', gender='all', racial_group='all', relative=True, poverty_rate='regular', baseline=0.03327473625540733, reform=0.03287297859787941, change=-0.0004017576575279236, relative_change=-0.012073954679735011), PovertyRateMetricComparison(age_group='senior', gender='all', racial_group='all', relative=True, poverty_rate='deep', baseline=0.0007302720914594829, reform=0.0007292248192243278, change=-1.0472722351551056e-06, relative_change=-0.0014340849765490601), PovertyRateMetricComparison(age_group='senior', gender='all', racial_group='all', relative=False, poverty_rate='regular', baseline=436231.65625, reform=430964.59375, change=-5267.0625, relative_change=-0.01207400339828043), PovertyRateMetricComparison(age_group='senior', gender='all', racial_group='all', relative=False, poverty_rate='deep', baseline=9573.8642578125, reform=9560.134765625, change=-13.7294921875, relative_change=-0.0014340596250146758), PovertyRateMetricComparison(age_group='all', gender='male', racial_group='all', relative=True, poverty_rate='regular', baseline=0.08017819374799728, reform=0.0794147327542305, change=-0.0007634609937667847, relative_change=-0.009522052793635733), PovertyRateMetricComparison(age_group='all', gender='male', racial_group='all', relative=True, poverty_rate='deep', baseline=0.021698515862226486, reform=0.021697862073779106, change=-6.537884473800659e-07, relative_change=-3.013056061212938e-05), PovertyRateMetricComparison(age_group='all', gender='male', racial_group='all', relative=False, poverty_rate='regular', baseline=3098396.25, reform=3068893.0, change=-29503.25, relative_change=-0.009522103572130259), PovertyRateMetricComparison(age_group='all', gender='male', racial_group='all', relative=False, poverty_rate='deep', baseline=838514.75, reform=838489.5, change=-25.25, relative_change=-3.0112767843380216e-05), PovertyRateMetricComparison(age_group='all', gender='female', racial_group='all', relative=True, poverty_rate='regular', baseline=0.06785593926906586, reform=0.06704439222812653, change=-0.0008115470409393311, relative_change=-0.011959852736270336), PovertyRateMetricComparison(age_group='all', gender='female', racial_group='all', relative=True, poverty_rate='deep', baseline=0.005150204990059137, reform=0.005149507895112038, change=-6.970949470996857e-07, relative_change=-0.0001353528545844699), PovertyRateMetricComparison(age_group='all', gender='female', racial_group='all', relative=False, poverty_rate='regular', baseline=2588403.75, reform=2557447.0, change=-30956.75, relative_change=-0.011959784094734062), PovertyRateMetricComparison(age_group='all', gender='female', racial_group='all', relative=False, poverty_rate='deep', baseline=196457.53125, reform=196430.9375, change=-26.59375, relative_change=-0.000135366406320959), PovertyRateMetricComparison(age_group='all', gender='all', racial_group='all', relative=True, poverty_rate='regular', baseline=0.07405701279640198, reform=0.07326967269182205, change=-0.0007873401045799255, relative_change=-0.01063154014521874), PovertyRateMetricComparison(age_group='all', gender='all', racial_group='all', relative=True, poverty_rate='deep', baseline=0.013478054665029049, reform=0.013477379456162453, change=-6.752088665962219e-07, relative_change=-5.0096908150117427e-05), PovertyRateMetricComparison(age_group='all', gender='all', racial_group='all', relative=False, poverty_rate='regular', baseline=5686797.5, reform=5626338.5, change=-60459.0, relative_change=-0.010631467007573243), PovertyRateMetricComparison(age_group='all', gender='all', racial_group='all', relative=False, poverty_rate='deep', baseline=1034972.5, reform=1034920.625, change=-51.875, relative_change=-5.012210469360297e-05)], labor_supply=[])" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine import Simulation\n", + "\n", + "sim = Simulation({\n", + " \"scope\": \"macro\", # Required for this\n", + " \"country\": \"uk\", # or \"us\"\n", + " \"time_period\": 2025,\n", + " \"reform\": {\n", + " \"gov.hmrc.income_tax.allowances.personal_allowance.amount\": 15_000,\n", + " }\n", + "})\n", + "\n", + "sim.calculate()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Output schema\n", + "\n", + "`calculate_economy_comparison` or `calculate` (when `scope=household` and `reform is not None`) return the following schema." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$defs': {'DecileImpacts': {'properties': {'income': {'$ref': '#/$defs/IncomeMeasureSpecificDecileImpacts'},\n", + " 'wealth': {'anyOf': [{'$ref': '#/$defs/IncomeMeasureSpecificDecileImpacts'},\n", + " {'type': 'null'}]}},\n", + " 'required': ['income', 'wealth'],\n", + " 'title': 'DecileImpacts',\n", + " 'type': 'object'},\n", + " 'FiscalComparison': {'properties': {'baseline': {'$ref': '#/$defs/FiscalSummary'},\n", + " 'reform': {'$ref': '#/$defs/FiscalSummary'},\n", + " 'change': {'$ref': '#/$defs/FiscalSummary'},\n", + " 'relative_change': {'$ref': '#/$defs/FiscalSummary'}},\n", + " 'required': ['baseline', 'reform', 'change', 'relative_change'],\n", + " 'title': 'FiscalComparison',\n", + " 'type': 'object'},\n", + " 'FiscalSummary': {'properties': {'tax_revenue': {'title': 'Tax Revenue',\n", + " 'type': 'number'},\n", + " 'federal_tax': {'title': 'Federal Tax', 'type': 'number'},\n", + " 'federal_balance': {'title': 'Federal Balance', 'type': 'number'},\n", + " 'state_tax': {'title': 'State Tax', 'type': 'number'},\n", + " 'government_spending': {'title': 'Government Spending', 'type': 'number'},\n", + " 'tax_benefit_programs': {'additionalProperties': {'type': 'number'},\n", + " 'title': 'Tax Benefit Programs',\n", + " 'type': 'object'},\n", + " 'household_net_income': {'title': 'Household Net Income',\n", + " 'type': 'number'}},\n", + " 'required': ['tax_revenue',\n", + " 'federal_tax',\n", + " 'federal_balance',\n", + " 'state_tax',\n", + " 'government_spending',\n", + " 'tax_benefit_programs',\n", + " 'household_net_income'],\n", + " 'title': 'FiscalSummary',\n", + " 'type': 'object'},\n", + " 'Headlines': {'properties': {'budgetary_impact': {'title': 'Budgetary Impact',\n", + " 'type': 'number'},\n", + " 'poverty_impact': {'title': 'Poverty Impact', 'type': 'number'},\n", + " 'winner_share': {'title': 'Winner Share', 'type': 'number'}},\n", + " 'required': ['budgetary_impact', 'poverty_impact', 'winner_share'],\n", + " 'title': 'Headlines',\n", + " 'type': 'object'},\n", + " 'IncomeMeasureSpecificDecileImpacts': {'properties': {'income_change': {'$ref': '#/$defs/IncomeMeasureSpecificDecileIncomeChange'},\n", + " 'winners_and_losers': {'$ref': '#/$defs/IncomeMeasureSpecificDecileWinnersLosers'}},\n", + " 'required': ['income_change', 'winners_and_losers'],\n", + " 'title': 'IncomeMeasureSpecificDecileImpacts',\n", + " 'type': 'object'},\n", + " 'IncomeMeasureSpecificDecileIncomeChange': {'properties': {'relative': {'additionalProperties': {'type': 'number'},\n", + " 'title': 'Relative',\n", + " 'type': 'object'},\n", + " 'average': {'additionalProperties': {'type': 'number'},\n", + " 'title': 'Average',\n", + " 'type': 'object'}},\n", + " 'required': ['relative', 'average'],\n", + " 'title': 'IncomeMeasureSpecificDecileIncomeChange',\n", + " 'type': 'object'},\n", + " 'IncomeMeasureSpecificDecileWinnersLosers': {'properties': {'deciles': {'additionalProperties': {'$ref': '#/$defs/IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes'},\n", + " 'title': 'Deciles',\n", + " 'type': 'object'},\n", + " 'all': {'$ref': '#/$defs/IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes'}},\n", + " 'required': ['deciles', 'all'],\n", + " 'title': 'IncomeMeasureSpecificDecileWinnersLosers',\n", + " 'type': 'object'},\n", + " 'IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes': {'properties': {'lose_more_than_5_percent_share': {'title': 'Lose More Than 5 Percent Share',\n", + " 'type': 'number'},\n", + " 'lose_less_than_5_percent_share': {'title': 'Lose Less Than 5 Percent Share',\n", + " 'type': 'number'},\n", + " 'lose_share': {'title': 'Lose Share', 'type': 'number'},\n", + " 'no_change_share': {'title': 'No Change Share', 'type': 'number'},\n", + " 'gain_share': {'title': 'Gain Share', 'type': 'number'},\n", + " 'gain_less_than_5_percent_share': {'title': 'Gain Less Than 5 Percent Share',\n", + " 'type': 'number'},\n", + " 'gain_more_than_5_percent_share': {'title': 'Gain More Than 5 Percent Share',\n", + " 'type': 'number'}},\n", + " 'required': ['lose_more_than_5_percent_share',\n", + " 'lose_less_than_5_percent_share',\n", + " 'lose_share',\n", + " 'no_change_share',\n", + " 'gain_share',\n", + " 'gain_less_than_5_percent_share',\n", + " 'gain_more_than_5_percent_share'],\n", + " 'title': 'IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes',\n", + " 'type': 'object'},\n", + " 'InequalityComparison': {'properties': {'baseline': {'$ref': '#/$defs/InequalitySummary'},\n", + " 'reform': {'$ref': '#/$defs/InequalitySummary'},\n", + " 'change': {'$ref': '#/$defs/InequalitySummary'},\n", + " 'relative_change': {'$ref': '#/$defs/InequalitySummary'}},\n", + " 'required': ['baseline', 'reform', 'change', 'relative_change'],\n", + " 'title': 'InequalityComparison',\n", + " 'type': 'object'},\n", + " 'InequalitySummary': {'properties': {'gini': {'title': 'Gini',\n", + " 'type': 'number'},\n", + " 'top_10_share': {'title': 'Top 10 Share', 'type': 'number'},\n", + " 'top_1_share': {'title': 'Top 1 Share', 'type': 'number'}},\n", + " 'required': ['gini', 'top_10_share', 'top_1_share'],\n", + " 'title': 'InequalitySummary',\n", + " 'type': 'object'},\n", + " 'LaborSupplyMetricImpact': {'properties': {'elasticity': {'enum': ['income',\n", + " 'substitution',\n", + " 'all'],\n", + " 'title': 'Elasticity',\n", + " 'type': 'string'},\n", + " 'decile': {'enum': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'all'],\n", + " 'title': 'Decile'},\n", + " 'unit': {'enum': ['earnings', 'hours'], 'title': 'Unit', 'type': 'string'},\n", + " 'baseline': {'title': 'Baseline', 'type': 'number'},\n", + " 'reform': {'title': 'Reform', 'type': 'number'},\n", + " 'change': {'title': 'Change', 'type': 'number'},\n", + " 'relative_change': {'title': 'Relative Change', 'type': 'number'},\n", + " 'average_change': {'title': 'Average Change', 'type': 'number'}},\n", + " 'required': ['elasticity',\n", + " 'decile',\n", + " 'unit',\n", + " 'baseline',\n", + " 'reform',\n", + " 'change',\n", + " 'relative_change',\n", + " 'average_change'],\n", + " 'title': 'LaborSupplyMetricImpact',\n", + " 'type': 'object'},\n", + " 'PovertyRateMetricComparison': {'properties': {'age_group': {'enum': ['child',\n", + " 'working_age',\n", + " 'senior',\n", + " 'all'],\n", + " 'title': 'Age Group',\n", + " 'type': 'string'},\n", + " 'gender': {'enum': ['male', 'female', 'all'],\n", + " 'title': 'Gender',\n", + " 'type': 'string'},\n", + " 'racial_group': {'enum': ['white', 'black', 'hispanic', 'other', 'all'],\n", + " 'title': 'Racial Group',\n", + " 'type': 'string'},\n", + " 'relative': {'title': 'Relative', 'type': 'boolean'},\n", + " 'poverty_rate': {'enum': ['regular',\n", + " 'deep',\n", + " 'uk_hbai_bhc',\n", + " 'uk_hbai_bhc_half',\n", + " 'us_spm',\n", + " 'us_spm_half'],\n", + " 'title': 'Poverty Rate',\n", + " 'type': 'string'},\n", + " 'baseline': {'title': 'Baseline', 'type': 'number'},\n", + " 'reform': {'title': 'Reform', 'type': 'number'},\n", + " 'change': {'title': 'Change', 'type': 'number'},\n", + " 'relative_change': {'title': 'Relative Change', 'type': 'number'}},\n", + " 'required': ['age_group',\n", + " 'gender',\n", + " 'racial_group',\n", + " 'relative',\n", + " 'poverty_rate',\n", + " 'baseline',\n", + " 'reform',\n", + " 'change',\n", + " 'relative_change'],\n", + " 'title': 'PovertyRateMetricComparison',\n", + " 'type': 'object'}},\n", + " 'properties': {'headlines': {'$ref': '#/$defs/Headlines'},\n", + " 'fiscal': {'$ref': '#/$defs/FiscalComparison'},\n", + " 'inequality': {'$ref': '#/$defs/InequalityComparison'},\n", + " 'distributional': {'$ref': '#/$defs/DecileImpacts'},\n", + " 'poverty': {'items': {'$ref': '#/$defs/PovertyRateMetricComparison'},\n", + " 'title': 'Poverty',\n", + " 'type': 'array'},\n", + " 'labor_supply': {'items': {'$ref': '#/$defs/LaborSupplyMetricImpact'},\n", + " 'title': 'Labor Supply',\n", + " 'type': 'array'}},\n", + " 'required': ['headlines',\n", + " 'fiscal',\n", + " 'inequality',\n", + " 'distributional',\n", + " 'poverty',\n", + " 'labor_supply'],\n", + " 'title': 'EconomyComparison',\n", + " 'type': 'object'}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine.outputs.macro.comparison.calculate_economy_comparison import EconomyComparison\n", + "\n", + "EconomyComparison.model_json_schema()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/reference/calculate_household_comparison.ipynb b/docs/reference/calculate_household_comparison.ipynb new file mode 100644 index 00000000..f86d3f12 --- /dev/null +++ b/docs/reference/calculate_household_comparison.ipynb @@ -0,0 +1,155 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Compare outcomes for specific household scenarios\n", + "\n", + "Use `Simulation.calculate_household_comparison()` to use PolicyEngine's tax-benefit model to compare how taxes, benefits and other household properties change under a change in tax or benefit rules. This notebook demonstrates how to use this function to compare outcomes for specific households." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "HouseholdComparison(full_household_baseline={'people': {'person': {'age': {'2025': 30.0}, 'employment_income': {'2025': 30000.0}, 'employment_income_before_lsr': {'2025': 30000.0}, 'private_pension_income': {'2025': 0.0}, 'pension_income': {'2025': 0.0}, 'state_pension': {'2025': 0.0}, 'self_employment_income': {'2025': 0.0}, 'property_income': {'2025': 0.0}, 'savings_interest_income': {'2025': 0.0}, 'dividend_income': {'2025': 0.0}, 'sublet_income': {'2025': 0.0}, 'miscellaneous_income': {'2025': 0.0}, 'private_transfer_income': {'2025': 0.0}, 'lump_sum_income': {'2025': 0.0}, 'maintenance_income': {'2025': 0.0}, 'other_investment_income': {'2025': 0.0}, 'dla_sc_category': {'2025': 'NONE'}, 'dla_m_category': {'2025': 'NONE'}, 'pip_m_category': {'2025': 'NONE'}, 'pip_dl_category': {'2025': 'NONE'}, 'receives_carers_allowance': {'2025': False}, 'childcare_expenses': {'2025': 0.0}, 'employer_pension_contributions': {'2025': 0.0}, 'employee_pension_contributions': {'2025': 0.0}, 'personal_pension_contributions': {'2025': 0.0}, 'maintenance_expenses': {'2025': 0.0}, 'bi_individual_phaseout': {'2025': 0.0}, 'bi_household_phaseout': {'2025': 0.0}, 'bi_phaseout': {'2025': 0.0}, 'basic_income': {'2025': 0.0}, 'bi_maximum': {'2025': 0.0}, 'attends_private_school': {'2025': False}, 'employer_cost': {'2025': 32884.2}, 'baseline_employer_cost': {'2025': 32884.2}, 'adjusted_employer_cost': {'2025': 32884.2}, 'employer_ni_response_consumer_incidence': {'2025': 0.0}, 'employer_ni_response_capital_incidence': {'2025': 0.0}, 'employer_ni_fixed_employer_cost_change': {'2025': 0.0}, 'marginal_tax_rate': {'2025': 0.27999997}, 'cliff_evaluated': {'2025': True}, 'cliff_gap': {'2025': 0.0}, 'is_on_cliff': {'2025': False}, 'person_id': {'2025': 0.0}, 'people': {'2025': 1.0}, 'raw_person_weight': {'2025': 1.0}, 'person_weight': {'2025': 1.0}, 'adult_index': {'2025': 1.0}, 'birth_year': {'2025': 1995.0}, 'over_16': {'2025': True}, 'is_adult': {'2025': True}, 'is_child': {'2025': False}, 'child_index': {'2025': -1.0}, 'is_eldest_child': {'2025': True}, 'is_benunit_eldest_child': {'2025': False}, 'marital_status': {'2025': 'SINGLE'}, 'current_education': {'2025': 'NOT_IN_EDUCATION'}, 'highest_education': {'2025': 'UPPER_SECONDARY'}, 'in_FE': {'2025': False}, 'in_HE': {'2025': False}, 'gender': {'2025': 'MALE'}, 'is_male': {'2025': True}, 'is_female': {'2025': False}, 'is_household_head': {'2025': True}, 'is_benunit_head': {'2025': True}, 'in_social_housing': {'2025': False}, 'is_WA_adult': {'2025': True}, 'is_young_child': {'2025': False}, 'age_under_18': {'2025': False}, 'age_18_64': {'2025': True}, 'age_over_64': {'2025': False}, 'is_older_child': {'2025': False}, 'is_higher_earner': {'2025': True}, 'person_benunit_id': {'2025': 0.0}, 'person_household_id': {'2025': 0.0}, 'role': {'2025': ''}, 'person_benunit_role': {'2025': ''}, 'person_household_role': {'2025': ''}, 'is_disabled_for_benefits': {'2025': False}, 'is_enhanced_disabled_for_benefits': {'2025': False}, 'is_severely_disabled_for_benefits': {'2025': False}, 'person_state_id': {'2025': 0.0}, 'person_state_role': {'2025': ''}, 'is_blind': {'2025': False}, 'is_carer_for_benefits': {'2025': False}, 'personal_rent': {'2025': 0.0}, 'weekly_childcare_expenses': {'2025': 0.0}, 'earned_income': {'2025': 30000.0}, 'market_income': {'2025': 30000.0}, 'hours_worked': {'2025': 0.0}, 'in_work': {'2025': True}, 'weekly_hours': {'2025': 0.0}, 'employment_status': {'2025': 'UNEMPLOYED'}, 'capital_income': {'2025': 0.0}, 'base_net_income': {'2025': 0.0}, 'is_apprentice': {'2025': False}, 'minimum_wage_category': {'2025': 'OVER_24'}, 'minimum_wage': {'2025': 9.5}, 'income_decile': {'2025': 10.0}, 'household_statutory_maternity_pay': {'2025': 0.0}, 'household_statutory_paternity_pay': {'2025': 0.0}, 'household_statutory_sick_pay': {'2025': 0.0}, 'capital_gains': {'2025': 0.0}, 'is_QYP': {'2025': False}, 'is_child_or_QYP': {'2025': False}, 'employment_benefits': {'2025': 0.0}, 'relative_income_change': {'2025': 0.0}, 'relative_wage_change': {'2025': 0.0}, 'income_elasticity_lsr': {'2025': 0.0}, 'substitution_elasticity_lsr': {'2025': 0.0}, 'employment_income_behavioral_response': {'2025': 0.0}, 'income_support_reported': {'2025': 0.0}, 'attendance_allowance': {'2025': 0.0}, 'attendance_allowance_reported': {'2025': 0.0}, 'aa_category': {'2025': 'NONE'}, 'esa_income_reported': {'2025': 0.0}, 'iidb': {'2025': 0.0}, 'iidb_reported': {'2025': 0.0}, 'jsa_contrib': {'2025': 0.0}, 'jsa_contrib_reported': {'2025': 0.0}, 'maternity_allowance_reported': {'2025': 0.0}, 'maternity_allowance': {'2025': 0.0}, 'ssmg_reported': {'2025': 0.0}, 'ssmg': {'2025': 0.0}, 'council_tax_benefit_reported': {'2025': 0.0}, 'working_tax_credit_reported': {'2025': 0.0}, 'child_tax_credit_reported': {'2025': 0.0}, 'is_CTC_child_limit_exempt': {'2025': True}, 'is_child_for_CTC': {'2025': False}, 'jsa_income_reported': {'2025': 0.0}, 'bsp': {'2025': 0.0}, 'bsp_reported': {'2025': 0.0}, 'incapacity_benefit': {'2025': 0.0}, 'incapacity_benefit_reported': {'2025': 0.0}, 'sda': {'2025': 0.0}, 'sda_reported': {'2025': 0.0}, 'carers_allowance': {'2025': 0.0}, 'care_hours': {'2025': 0.0}, 'carers_allowance_reported': {'2025': 0.0}, 'armed_forces_independence_payment': {'2025': 0.0}, 'esa_contrib': {'2025': 0.0}, 'esa_contrib_reported': {'2025': 0.0}, 'esa': {'2025': 0.0}, 'afcs': {'2025': 0.0}, 'afcs_reported': {'2025': 0.0}, 'student_loans': {'2025': 0.0}, 'adult_ema': {'2025': 0.0}, 'child_ema': {'2025': 0.0}, 'access_fund': {'2025': 0.0}, 'education_grants': {'2025': 0.0}, 'student_payments': {'2025': 0.0}, 'state_pension_age': {'2025': 66.0}, 'is_SP_age': {'2025': False}, 'state_pension_type': {'2025': 'NONE'}, 'basic_state_pension': {'2025': 0.0}, 'additional_state_pension': {'2025': 0.0}, 'new_state_pension': {'2025': 0.0}, 'state_pension_reported': {'2025': 0.0}, 'winter_fuel_allowance_reported': {'2025': 0.0}, 'dla_m_reported': {'2025': 0.0}, 'dla_m': {'2025': 0.0}, 'dla_sc_reported': {'2025': 0.0}, 'dla_sc': {'2025': 0.0}, 'dla_sc_middle_plus': {'2025': False}, 'receives_highest_dla_sc': {'2025': False}, 'dla': {'2025': 0.0}, 'housing_benefit_reported': {'2025': 0.0}, 'household_benefits_individual_non_dep_deduction': {'2025': 6476.6}, 'housing_benefit_individual_non_dep_deduction_eligible': {'2025': True}, 'universal_credit_reported': {'2025': 0.0}, 'uc_is_child_born_before_child_limit': {'2025': False}, 'uc_individual_child_element': {'2025': 0.0}, 'uc_individual_disabled_child_element': {'2025': 0.0}, 'uc_individual_severely_disabled_child_element': {'2025': 0.0}, 'uc_is_in_startup_period': {'2025': False}, 'uc_minimum_income_floor': {'2025': 17290.0}, 'uc_mif_applies': {'2025': False}, 'uc_mif_capped_earned_income': {'2025': 30000.0}, 'uc_limited_capability_for_WRA': {'2025': False}, 'uc_individual_non_dep_deduction_eligible': {'2025': True}, 'uc_individual_non_dep_deduction': {'2025': 1125.3458}, 'uc_non_dep_deduction_exempt': {'2025': False}, 'pip_m_reported': {'2025': 0.0}, 'pip_m': {'2025': 0.0}, 'pip_dl_reported': {'2025': 0.0}, 'pip_dl': {'2025': 0.0}, 'receives_enhanced_pip_dl': {'2025': False}, 'pip': {'2025': 0.0}, 'pension_credit_reported': {'2025': 0.0}, 'tax': {'2025': 4880.4033}, 'tax_reported': {'2025': 0.0}, 'tax_modelling': {'2025': 4880.4033}, 'child_benefit_reported': {'2025': 0.0}, 'child_benefit_respective_amount': {'2025': 0.0}, 'relative_capital_gains_mtr_change': {'2025': 0.0}, 'capital_gains_elasticity': {'2025': 0.0}, 'capital_gains_behavioural_response': {'2025': 0.0}, 'capital_gains_before_response': {'2025': 0.0}, 'adult_index_cg': {'2025': 1.0}, 'marginal_tax_rate_on_capital_gains': {'2025': 0.0}, 'capital_gains_tax': {'2025': 0.0}, 'ni_employee': {'2025': 1394.4033}, 'national_insurance': {'2025': 1394.4033}, 'ni_employer': {'2025': 2884.2}, 'ni_self_employed': {'2025': 0.0}, 'total_national_insurance': {'2025': 4278.6035}, 'ni_class_4_maximum': {'2025': 917.73663}, 'ni_class_4': {'2025': 0.0}, 'ni_class_4_main': {'2025': 0.0}, 'ni_class_3': {'2025': 0.0}, 'ni_class_2': {'2025': 0.0}, 'ni_class_1_employee_primary': {'2025': 1394.4033}, 'ni_class_1_income': {'2025': 30000.0}, 'ni_class_1_employee': {'2025': 1394.4033}, 'ni_class_1_employee_additional': {'2025': 0.0}, 'ni_liable': {'2025': True}, 'ni_class_1_employer': {'2025': 2884.2}, 'other_tax_credits': {'2025': 0.0}, 'earned_income_tax': {'2025': 3486.0}, 'total_income': {'2025': 30000.0}, 'income_tax': {'2025': 3486.0}, 'taxed_income': {'2025': 17430.0}, 'adjusted_net_income': {'2025': 30000.0}, 'total_pension_income': {'2025': 0.0}, 'social_security_income': {'2025': 0.0}, 'income_tax_pre_charges': {'2025': 3486.0}, 'personal_allowance': {'2025': 12570.0}, 'blind_persons_allowance': {'2025': 0.0}, 'married_couples_allowance': {'2025': 0.0}, 'married_couples_allowance_deduction': {'2025': 0.0}, 'capped_mcad': {'2025': 0.0}, 'pension_annual_allowance': {'2025': 40000.0}, 'trading_allowance': {'2025': 1000.0}, 'trading_allowance_deduction': {'2025': 0.0}, 'property_allowance': {'2025': 1000.0}, 'property_allowance_deduction': {'2025': 0.0}, 'savings_allowance': {'2025': 1000.0}, 'dividend_allowance': {'2025': 500.0}, 'gift_aid': {'2025': 0.0}, 'covenanted_payments': {'2025': 0.0}, 'charitable_investment_gifts': {'2025': 0.0}, 'other_deductions': {'2025': 0.0}, 'allowances': {'2025': 12570.0}, 'unused_personal_allowance': {'2025': 0.0}, 'meets_marriage_allowance_income_conditions': {'2025': True}, 'partners_unused_personal_allowance': {'2025': 0.0}, 'marriage_allowance': {'2025': 0.0}, 'received_allowances': {'2025': 12570.0}, 'received_allowances_savings_income': {'2025': 0.0}, 'received_allowances_dividend_income': {'2025': 0.0}, 'received_allowances_earned_income': {'2025': 12570.0}, 'savings_income_tax': {'2025': 0.0}, 'dividend_income_tax': {'2025': 0.0}, 'loss_relief': {'2025': 0.0}, 'capital_allowances': {'2025': 0.0}, 'deficiency_relief': {'2025': 0.0}, 'employment_deductions': {'2025': 0.0}, 'employment_expenses': {'2025': 0.0}, 'CB_HITC': {'2025': 0.0}, 'higher_rate_earned_income': {'2025': 0.0}, 'add_rate_earned_income': {'2025': 0.0}, 'earned_taxable_income': {'2025': 17430.0}, 'basic_rate_earned_income': {'2025': 17430.0}, 'taxable_pension_income': {'2025': 0.0}, 'taxable_miscellaneous_income': {'2025': 0.0}, 'taxed_dividend_income': {'2025': 0.0}, 'taxable_dividend_income': {'2025': 0.0}, 'taxable_social_security_income': {'2025': 0.0}, 'taxable_employment_income': {'2025': 30000.0}, 'trading_loss': {'2025': 0.0}, 'taxable_self_employment_income': {'2025': 0.0}, 'taxable_property_income': {'2025': 0.0}, 'individual_savings_account_interest_income': {'2025': 0.0}, 'taxable_savings_interest_income': {'2025': 0.0}, 'tax_free_savings_income': {'2025': 0.0}, 'higher_rate_earned_income_tax': {'2025': 0.0}, 'add_rate_earned_income_tax': {'2025': 0.0}, 'tax_band': {'2025': 'BASIC'}, 'basic_rate_earned_income_tax': {'2025': 3486.0}, 'basic_rate_savings_income_pre_starter': {'2025': 0.0}, 'taxed_savings_income': {'2025': 0.0}, 'higher_rate_savings_income': {'2025': 0.0}, 'savings_starter_rate_income': {'2025': 0.0}, 'add_rate_savings_income': {'2025': 0.0}, 'basic_rate_savings_income': {'2025': 0.0}, 'personal_pension_contributions_tax': {'2025': 0.0}, 'pension_contributions_relief': {'2025': 0.0}, 'pension_contributions': {'2025': 0.0}, 'statutory_maternity_pay': {'2025': 0.0}, 'statutory_sick_pay': {'2025': 0.0}, 'pays_scottish_income_tax': {'2025': False}}}}, full_household_reform={'people': {'person': {'age': {'2025': 30.0}, 'employment_income': {'2025': 30000.0}, 'employment_income_before_lsr': {'2025': 30000.0}, 'private_pension_income': {'2025': 0.0}, 'pension_income': {'2025': 0.0}, 'state_pension': {'2025': 0.0}, 'self_employment_income': {'2025': 0.0}, 'property_income': {'2025': 0.0}, 'savings_interest_income': {'2025': 0.0}, 'dividend_income': {'2025': 0.0}, 'sublet_income': {'2025': 0.0}, 'miscellaneous_income': {'2025': 0.0}, 'private_transfer_income': {'2025': 0.0}, 'lump_sum_income': {'2025': 0.0}, 'maintenance_income': {'2025': 0.0}, 'other_investment_income': {'2025': 0.0}, 'dla_sc_category': {'2025': 'NONE'}, 'dla_m_category': {'2025': 'NONE'}, 'pip_m_category': {'2025': 'NONE'}, 'pip_dl_category': {'2025': 'NONE'}, 'receives_carers_allowance': {'2025': False}, 'childcare_expenses': {'2025': 0.0}, 'employer_pension_contributions': {'2025': 0.0}, 'employee_pension_contributions': {'2025': 0.0}, 'personal_pension_contributions': {'2025': 0.0}, 'maintenance_expenses': {'2025': 0.0}, 'bi_individual_phaseout': {'2025': 0.0}, 'bi_household_phaseout': {'2025': 0.0}, 'bi_phaseout': {'2025': 0.0}, 'basic_income': {'2025': 0.0}, 'bi_maximum': {'2025': 0.0}, 'attends_private_school': {'2025': False}, 'employer_cost': {'2025': 32884.2}, 'baseline_employer_cost': {'2025': 32884.2}, 'adjusted_employer_cost': {'2025': 32884.2}, 'employer_ni_response_consumer_incidence': {'2025': 0.0}, 'employer_ni_response_capital_incidence': {'2025': 0.0}, 'employer_ni_fixed_employer_cost_change': {'2025': 0.0}, 'marginal_tax_rate': {'2025': 0.27999997}, 'cliff_evaluated': {'2025': True}, 'cliff_gap': {'2025': 0.0}, 'is_on_cliff': {'2025': False}, 'person_id': {'2025': 0.0}, 'people': {'2025': 1.0}, 'raw_person_weight': {'2025': 1.0}, 'person_weight': {'2025': 1.0}, 'adult_index': {'2025': 1.0}, 'birth_year': {'2025': 1995.0}, 'over_16': {'2025': True}, 'is_adult': {'2025': True}, 'is_child': {'2025': False}, 'child_index': {'2025': -1.0}, 'is_eldest_child': {'2025': True}, 'is_benunit_eldest_child': {'2025': False}, 'marital_status': {'2025': 'SINGLE'}, 'current_education': {'2025': 'NOT_IN_EDUCATION'}, 'highest_education': {'2025': 'UPPER_SECONDARY'}, 'in_FE': {'2025': False}, 'in_HE': {'2025': False}, 'gender': {'2025': 'MALE'}, 'is_male': {'2025': True}, 'is_female': {'2025': False}, 'is_household_head': {'2025': True}, 'is_benunit_head': {'2025': True}, 'in_social_housing': {'2025': False}, 'is_WA_adult': {'2025': True}, 'is_young_child': {'2025': False}, 'age_under_18': {'2025': False}, 'age_18_64': {'2025': True}, 'age_over_64': {'2025': False}, 'is_older_child': {'2025': False}, 'is_higher_earner': {'2025': True}, 'person_benunit_id': {'2025': 0.0}, 'person_household_id': {'2025': 0.0}, 'role': {'2025': ''}, 'person_benunit_role': {'2025': ''}, 'person_household_role': {'2025': ''}, 'is_disabled_for_benefits': {'2025': False}, 'is_enhanced_disabled_for_benefits': {'2025': False}, 'is_severely_disabled_for_benefits': {'2025': False}, 'person_state_id': {'2025': 0.0}, 'person_state_role': {'2025': ''}, 'is_blind': {'2025': False}, 'is_carer_for_benefits': {'2025': False}, 'personal_rent': {'2025': 0.0}, 'weekly_childcare_expenses': {'2025': 0.0}, 'earned_income': {'2025': 30000.0}, 'market_income': {'2025': 30000.0}, 'hours_worked': {'2025': 0.0}, 'in_work': {'2025': True}, 'weekly_hours': {'2025': 0.0}, 'employment_status': {'2025': 'UNEMPLOYED'}, 'capital_income': {'2025': 0.0}, 'base_net_income': {'2025': 0.0}, 'is_apprentice': {'2025': False}, 'minimum_wage_category': {'2025': 'OVER_24'}, 'minimum_wage': {'2025': 9.5}, 'income_decile': {'2025': 10.0}, 'household_statutory_maternity_pay': {'2025': 0.0}, 'household_statutory_paternity_pay': {'2025': 0.0}, 'household_statutory_sick_pay': {'2025': 0.0}, 'capital_gains': {'2025': 0.0}, 'is_QYP': {'2025': False}, 'is_child_or_QYP': {'2025': False}, 'employment_benefits': {'2025': 0.0}, 'relative_income_change': {'2025': 0.019470373}, 'relative_wage_change': {'2025': 0.0}, 'income_elasticity_lsr': {'2025': 0.0}, 'substitution_elasticity_lsr': {'2025': 0.0}, 'employment_income_behavioral_response': {'2025': 0.0}, 'income_support_reported': {'2025': 0.0}, 'attendance_allowance': {'2025': 0.0}, 'attendance_allowance_reported': {'2025': 0.0}, 'aa_category': {'2025': 'NONE'}, 'esa_income_reported': {'2025': 0.0}, 'iidb': {'2025': 0.0}, 'iidb_reported': {'2025': 0.0}, 'jsa_contrib': {'2025': 0.0}, 'jsa_contrib_reported': {'2025': 0.0}, 'maternity_allowance_reported': {'2025': 0.0}, 'maternity_allowance': {'2025': 0.0}, 'ssmg_reported': {'2025': 0.0}, 'ssmg': {'2025': 0.0}, 'council_tax_benefit_reported': {'2025': 0.0}, 'working_tax_credit_reported': {'2025': 0.0}, 'child_tax_credit_reported': {'2025': 0.0}, 'is_CTC_child_limit_exempt': {'2025': True}, 'is_child_for_CTC': {'2025': False}, 'jsa_income_reported': {'2025': 0.0}, 'bsp': {'2025': 0.0}, 'bsp_reported': {'2025': 0.0}, 'incapacity_benefit': {'2025': 0.0}, 'incapacity_benefit_reported': {'2025': 0.0}, 'sda': {'2025': 0.0}, 'sda_reported': {'2025': 0.0}, 'carers_allowance': {'2025': 0.0}, 'care_hours': {'2025': 0.0}, 'carers_allowance_reported': {'2025': 0.0}, 'armed_forces_independence_payment': {'2025': 0.0}, 'esa_contrib': {'2025': 0.0}, 'esa_contrib_reported': {'2025': 0.0}, 'esa': {'2025': 0.0}, 'afcs': {'2025': 0.0}, 'afcs_reported': {'2025': 0.0}, 'student_loans': {'2025': 0.0}, 'adult_ema': {'2025': 0.0}, 'child_ema': {'2025': 0.0}, 'access_fund': {'2025': 0.0}, 'education_grants': {'2025': 0.0}, 'student_payments': {'2025': 0.0}, 'state_pension_age': {'2025': 66.0}, 'is_SP_age': {'2025': False}, 'state_pension_type': {'2025': 'NONE'}, 'basic_state_pension': {'2025': 0.0}, 'additional_state_pension': {'2025': 0.0}, 'new_state_pension': {'2025': 0.0}, 'state_pension_reported': {'2025': 0.0}, 'winter_fuel_allowance_reported': {'2025': 0.0}, 'dla_m_reported': {'2025': 0.0}, 'dla_m': {'2025': 0.0}, 'dla_sc_reported': {'2025': 0.0}, 'dla_sc': {'2025': 0.0}, 'dla_sc_middle_plus': {'2025': False}, 'receives_highest_dla_sc': {'2025': False}, 'dla': {'2025': 0.0}, 'housing_benefit_reported': {'2025': 0.0}, 'household_benefits_individual_non_dep_deduction': {'2025': 6476.6}, 'housing_benefit_individual_non_dep_deduction_eligible': {'2025': True}, 'universal_credit_reported': {'2025': 0.0}, 'uc_is_child_born_before_child_limit': {'2025': False}, 'uc_individual_child_element': {'2025': 0.0}, 'uc_individual_disabled_child_element': {'2025': 0.0}, 'uc_individual_severely_disabled_child_element': {'2025': 0.0}, 'uc_is_in_startup_period': {'2025': False}, 'uc_minimum_income_floor': {'2025': 17290.0}, 'uc_mif_applies': {'2025': False}, 'uc_mif_capped_earned_income': {'2025': 30000.0}, 'uc_limited_capability_for_WRA': {'2025': False}, 'uc_individual_non_dep_deduction_eligible': {'2025': True}, 'uc_individual_non_dep_deduction': {'2025': 1125.3458}, 'uc_non_dep_deduction_exempt': {'2025': False}, 'pip_m_reported': {'2025': 0.0}, 'pip_m': {'2025': 0.0}, 'pip_dl_reported': {'2025': 0.0}, 'pip_dl': {'2025': 0.0}, 'receives_enhanced_pip_dl': {'2025': False}, 'pip': {'2025': 0.0}, 'pension_credit_reported': {'2025': 0.0}, 'tax': {'2025': 4394.4033}, 'tax_reported': {'2025': 0.0}, 'tax_modelling': {'2025': 4394.4033}, 'child_benefit_reported': {'2025': 0.0}, 'child_benefit_respective_amount': {'2025': 0.0}, 'relative_capital_gains_mtr_change': {'2025': 0.0}, 'capital_gains_elasticity': {'2025': 0.0}, 'capital_gains_behavioural_response': {'2025': 0.0}, 'capital_gains_before_response': {'2025': 0.0}, 'adult_index_cg': {'2025': 1.0}, 'marginal_tax_rate_on_capital_gains': {'2025': 0.0}, 'capital_gains_tax': {'2025': 0.0}, 'ni_employee': {'2025': 1394.4033}, 'national_insurance': {'2025': 1394.4033}, 'ni_employer': {'2025': 2884.2}, 'ni_self_employed': {'2025': 0.0}, 'total_national_insurance': {'2025': 4278.6035}, 'ni_class_4_maximum': {'2025': 917.73663}, 'ni_class_4': {'2025': 0.0}, 'ni_class_4_main': {'2025': 0.0}, 'ni_class_3': {'2025': 0.0}, 'ni_class_2': {'2025': 0.0}, 'ni_class_1_employee_primary': {'2025': 1394.4033}, 'ni_class_1_income': {'2025': 30000.0}, 'ni_class_1_employee': {'2025': 1394.4033}, 'ni_class_1_employee_additional': {'2025': 0.0}, 'ni_liable': {'2025': True}, 'ni_class_1_employer': {'2025': 2884.2}, 'other_tax_credits': {'2025': 0.0}, 'earned_income_tax': {'2025': 3000.0}, 'total_income': {'2025': 30000.0}, 'income_tax': {'2025': 3000.0}, 'taxed_income': {'2025': 15000.0}, 'adjusted_net_income': {'2025': 30000.0}, 'total_pension_income': {'2025': 0.0}, 'social_security_income': {'2025': 0.0}, 'income_tax_pre_charges': {'2025': 3000.0}, 'personal_allowance': {'2025': 15000.0}, 'blind_persons_allowance': {'2025': 0.0}, 'married_couples_allowance': {'2025': 0.0}, 'married_couples_allowance_deduction': {'2025': 0.0}, 'capped_mcad': {'2025': 0.0}, 'pension_annual_allowance': {'2025': 40000.0}, 'trading_allowance': {'2025': 1000.0}, 'trading_allowance_deduction': {'2025': 0.0}, 'property_allowance': {'2025': 1000.0}, 'property_allowance_deduction': {'2025': 0.0}, 'savings_allowance': {'2025': 1000.0}, 'dividend_allowance': {'2025': 500.0}, 'gift_aid': {'2025': 0.0}, 'covenanted_payments': {'2025': 0.0}, 'charitable_investment_gifts': {'2025': 0.0}, 'other_deductions': {'2025': 0.0}, 'allowances': {'2025': 15000.0}, 'unused_personal_allowance': {'2025': 0.0}, 'meets_marriage_allowance_income_conditions': {'2025': True}, 'partners_unused_personal_allowance': {'2025': 0.0}, 'marriage_allowance': {'2025': 0.0}, 'received_allowances': {'2025': 15000.0}, 'received_allowances_savings_income': {'2025': 0.0}, 'received_allowances_dividend_income': {'2025': 0.0}, 'received_allowances_earned_income': {'2025': 15000.0}, 'savings_income_tax': {'2025': 0.0}, 'dividend_income_tax': {'2025': 0.0}, 'loss_relief': {'2025': 0.0}, 'capital_allowances': {'2025': 0.0}, 'deficiency_relief': {'2025': 0.0}, 'employment_deductions': {'2025': 0.0}, 'employment_expenses': {'2025': 0.0}, 'CB_HITC': {'2025': 0.0}, 'higher_rate_earned_income': {'2025': 0.0}, 'add_rate_earned_income': {'2025': 0.0}, 'earned_taxable_income': {'2025': 15000.0}, 'basic_rate_earned_income': {'2025': 15000.0}, 'taxable_pension_income': {'2025': 0.0}, 'taxable_miscellaneous_income': {'2025': 0.0}, 'taxed_dividend_income': {'2025': 0.0}, 'taxable_dividend_income': {'2025': 0.0}, 'taxable_social_security_income': {'2025': 0.0}, 'taxable_employment_income': {'2025': 30000.0}, 'trading_loss': {'2025': 0.0}, 'taxable_self_employment_income': {'2025': 0.0}, 'taxable_property_income': {'2025': 0.0}, 'individual_savings_account_interest_income': {'2025': 0.0}, 'taxable_savings_interest_income': {'2025': 0.0}, 'tax_free_savings_income': {'2025': 0.0}, 'higher_rate_earned_income_tax': {'2025': 0.0}, 'add_rate_earned_income_tax': {'2025': 0.0}, 'tax_band': {'2025': 'BASIC'}, 'basic_rate_earned_income_tax': {'2025': 3000.0}, 'basic_rate_savings_income_pre_starter': {'2025': 0.0}, 'taxed_savings_income': {'2025': 0.0}, 'higher_rate_savings_income': {'2025': 0.0}, 'savings_starter_rate_income': {'2025': 0.0}, 'add_rate_savings_income': {'2025': 0.0}, 'basic_rate_savings_income': {'2025': 0.0}, 'personal_pension_contributions_tax': {'2025': 0.0}, 'pension_contributions_relief': {'2025': 0.0}, 'pension_contributions': {'2025': 0.0}, 'statutory_maternity_pay': {'2025': 0.0}, 'statutory_sick_pay': {'2025': 0.0}, 'pays_scottish_income_tax': {'2025': False}}}}, change={'people': {'person': {'age': {'2025': 0.0}, 'employment_income': {'2025': 0.0}, 'employment_income_before_lsr': {'2025': 0.0}, 'private_pension_income': {'2025': 0.0}, 'pension_income': {'2025': 0.0}, 'state_pension': {'2025': 0.0}, 'self_employment_income': {'2025': 0.0}, 'property_income': {'2025': 0.0}, 'savings_interest_income': {'2025': 0.0}, 'dividend_income': {'2025': 0.0}, 'sublet_income': {'2025': 0.0}, 'miscellaneous_income': {'2025': 0.0}, 'private_transfer_income': {'2025': 0.0}, 'lump_sum_income': {'2025': 0.0}, 'maintenance_income': {'2025': 0.0}, 'other_investment_income': {'2025': 0.0}, 'dla_sc_category': {'2025': 0.0}, 'dla_m_category': {'2025': 0.0}, 'pip_m_category': {'2025': 0.0}, 'pip_dl_category': {'2025': 0.0}, 'receives_carers_allowance': {'2025': 0.0}, 'childcare_expenses': {'2025': 0.0}, 'employer_pension_contributions': {'2025': 0.0}, 'employee_pension_contributions': {'2025': 0.0}, 'personal_pension_contributions': {'2025': 0.0}, 'maintenance_expenses': {'2025': 0.0}, 'bi_individual_phaseout': {'2025': 0.0}, 'bi_household_phaseout': {'2025': 0.0}, 'bi_phaseout': {'2025': 0.0}, 'basic_income': {'2025': 0.0}, 'bi_maximum': {'2025': 0.0}, 'attends_private_school': {'2025': 0.0}, 'employer_cost': {'2025': 0.0}, 'baseline_employer_cost': {'2025': 0.0}, 'adjusted_employer_cost': {'2025': 0.0}, 'employer_ni_response_consumer_incidence': {'2025': 0.0}, 'employer_ni_response_capital_incidence': {'2025': 0.0}, 'employer_ni_fixed_employer_cost_change': {'2025': 0.0}, 'marginal_tax_rate': {'2025': 0.0}, 'cliff_evaluated': {'2025': 0.0}, 'cliff_gap': {'2025': 0.0}, 'is_on_cliff': {'2025': 0.0}, 'person_id': {'2025': 0.0}, 'people': {'2025': 0.0}, 'raw_person_weight': {'2025': 0.0}, 'person_weight': {'2025': 0.0}, 'adult_index': {'2025': 0.0}, 'birth_year': {'2025': 0.0}, 'over_16': {'2025': 0.0}, 'is_adult': {'2025': 0.0}, 'is_child': {'2025': 0.0}, 'child_index': {'2025': 0.0}, 'is_eldest_child': {'2025': 0.0}, 'is_benunit_eldest_child': {'2025': 0.0}, 'marital_status': {'2025': 0.0}, 'current_education': {'2025': 0.0}, 'highest_education': {'2025': 0.0}, 'in_FE': {'2025': 0.0}, 'in_HE': {'2025': 0.0}, 'gender': {'2025': 0.0}, 'is_male': {'2025': 0.0}, 'is_female': {'2025': 0.0}, 'is_household_head': {'2025': 0.0}, 'is_benunit_head': {'2025': 0.0}, 'in_social_housing': {'2025': 0.0}, 'is_WA_adult': {'2025': 0.0}, 'is_young_child': {'2025': 0.0}, 'age_under_18': {'2025': 0.0}, 'age_18_64': {'2025': 0.0}, 'age_over_64': {'2025': 0.0}, 'is_older_child': {'2025': 0.0}, 'is_higher_earner': {'2025': 0.0}, 'person_benunit_id': {'2025': 0.0}, 'person_household_id': {'2025': 0.0}, 'role': {'2025': 0.0}, 'person_benunit_role': {'2025': 0.0}, 'person_household_role': {'2025': 0.0}, 'is_disabled_for_benefits': {'2025': 0.0}, 'is_enhanced_disabled_for_benefits': {'2025': 0.0}, 'is_severely_disabled_for_benefits': {'2025': 0.0}, 'person_state_id': {'2025': 0.0}, 'person_state_role': {'2025': 0.0}, 'is_blind': {'2025': 0.0}, 'is_carer_for_benefits': {'2025': 0.0}, 'personal_rent': {'2025': 0.0}, 'weekly_childcare_expenses': {'2025': 0.0}, 'earned_income': {'2025': 0.0}, 'market_income': {'2025': 0.0}, 'hours_worked': {'2025': 0.0}, 'in_work': {'2025': 0.0}, 'weekly_hours': {'2025': 0.0}, 'employment_status': {'2025': 0.0}, 'capital_income': {'2025': 0.0}, 'base_net_income': {'2025': 0.0}, 'is_apprentice': {'2025': 0.0}, 'minimum_wage_category': {'2025': 0.0}, 'minimum_wage': {'2025': 0.0}, 'income_decile': {'2025': 0.0}, 'household_statutory_maternity_pay': {'2025': 0.0}, 'household_statutory_paternity_pay': {'2025': 0.0}, 'household_statutory_sick_pay': {'2025': 0.0}, 'capital_gains': {'2025': 0.0}, 'is_QYP': {'2025': 0.0}, 'is_child_or_QYP': {'2025': 0.0}, 'employment_benefits': {'2025': 0.0}, 'relative_income_change': {'2025': 0.019470373}, 'relative_wage_change': {'2025': 0.0}, 'income_elasticity_lsr': {'2025': 0.0}, 'substitution_elasticity_lsr': {'2025': 0.0}, 'employment_income_behavioral_response': {'2025': 0.0}, 'income_support_reported': {'2025': 0.0}, 'attendance_allowance': {'2025': 0.0}, 'attendance_allowance_reported': {'2025': 0.0}, 'aa_category': {'2025': 0.0}, 'esa_income_reported': {'2025': 0.0}, 'iidb': {'2025': 0.0}, 'iidb_reported': {'2025': 0.0}, 'jsa_contrib': {'2025': 0.0}, 'jsa_contrib_reported': {'2025': 0.0}, 'maternity_allowance_reported': {'2025': 0.0}, 'maternity_allowance': {'2025': 0.0}, 'ssmg_reported': {'2025': 0.0}, 'ssmg': {'2025': 0.0}, 'council_tax_benefit_reported': {'2025': 0.0}, 'working_tax_credit_reported': {'2025': 0.0}, 'child_tax_credit_reported': {'2025': 0.0}, 'is_CTC_child_limit_exempt': {'2025': 0.0}, 'is_child_for_CTC': {'2025': 0.0}, 'jsa_income_reported': {'2025': 0.0}, 'bsp': {'2025': 0.0}, 'bsp_reported': {'2025': 0.0}, 'incapacity_benefit': {'2025': 0.0}, 'incapacity_benefit_reported': {'2025': 0.0}, 'sda': {'2025': 0.0}, 'sda_reported': {'2025': 0.0}, 'carers_allowance': {'2025': 0.0}, 'care_hours': {'2025': 0.0}, 'carers_allowance_reported': {'2025': 0.0}, 'armed_forces_independence_payment': {'2025': 0.0}, 'esa_contrib': {'2025': 0.0}, 'esa_contrib_reported': {'2025': 0.0}, 'esa': {'2025': 0.0}, 'afcs': {'2025': 0.0}, 'afcs_reported': {'2025': 0.0}, 'student_loans': {'2025': 0.0}, 'adult_ema': {'2025': 0.0}, 'child_ema': {'2025': 0.0}, 'access_fund': {'2025': 0.0}, 'education_grants': {'2025': 0.0}, 'student_payments': {'2025': 0.0}, 'state_pension_age': {'2025': 0.0}, 'is_SP_age': {'2025': 0.0}, 'state_pension_type': {'2025': 0.0}, 'basic_state_pension': {'2025': 0.0}, 'additional_state_pension': {'2025': 0.0}, 'new_state_pension': {'2025': 0.0}, 'state_pension_reported': {'2025': 0.0}, 'winter_fuel_allowance_reported': {'2025': 0.0}, 'dla_m_reported': {'2025': 0.0}, 'dla_m': {'2025': 0.0}, 'dla_sc_reported': {'2025': 0.0}, 'dla_sc': {'2025': 0.0}, 'dla_sc_middle_plus': {'2025': 0.0}, 'receives_highest_dla_sc': {'2025': 0.0}, 'dla': {'2025': 0.0}, 'housing_benefit_reported': {'2025': 0.0}, 'household_benefits_individual_non_dep_deduction': {'2025': 0.0}, 'housing_benefit_individual_non_dep_deduction_eligible': {'2025': 0.0}, 'universal_credit_reported': {'2025': 0.0}, 'uc_is_child_born_before_child_limit': {'2025': 0.0}, 'uc_individual_child_element': {'2025': 0.0}, 'uc_individual_disabled_child_element': {'2025': 0.0}, 'uc_individual_severely_disabled_child_element': {'2025': 0.0}, 'uc_is_in_startup_period': {'2025': 0.0}, 'uc_minimum_income_floor': {'2025': 0.0}, 'uc_mif_applies': {'2025': 0.0}, 'uc_mif_capped_earned_income': {'2025': 0.0}, 'uc_limited_capability_for_WRA': {'2025': 0.0}, 'uc_individual_non_dep_deduction_eligible': {'2025': 0.0}, 'uc_individual_non_dep_deduction': {'2025': 0.0}, 'uc_non_dep_deduction_exempt': {'2025': 0.0}, 'pip_m_reported': {'2025': 0.0}, 'pip_m': {'2025': 0.0}, 'pip_dl_reported': {'2025': 0.0}, 'pip_dl': {'2025': 0.0}, 'receives_enhanced_pip_dl': {'2025': 0.0}, 'pip': {'2025': 0.0}, 'pension_credit_reported': {'2025': 0.0}, 'tax': {'2025': -486.0}, 'tax_reported': {'2025': 0.0}, 'tax_modelling': {'2025': -486.0}, 'child_benefit_reported': {'2025': 0.0}, 'child_benefit_respective_amount': {'2025': 0.0}, 'relative_capital_gains_mtr_change': {'2025': 0.0}, 'capital_gains_elasticity': {'2025': 0.0}, 'capital_gains_behavioural_response': {'2025': 0.0}, 'capital_gains_before_response': {'2025': 0.0}, 'adult_index_cg': {'2025': 0.0}, 'marginal_tax_rate_on_capital_gains': {'2025': 0.0}, 'capital_gains_tax': {'2025': 0.0}, 'ni_employee': {'2025': 0.0}, 'national_insurance': {'2025': 0.0}, 'ni_employer': {'2025': 0.0}, 'ni_self_employed': {'2025': 0.0}, 'total_national_insurance': {'2025': 0.0}, 'ni_class_4_maximum': {'2025': 0.0}, 'ni_class_4': {'2025': 0.0}, 'ni_class_4_main': {'2025': 0.0}, 'ni_class_3': {'2025': 0.0}, 'ni_class_2': {'2025': 0.0}, 'ni_class_1_employee_primary': {'2025': 0.0}, 'ni_class_1_income': {'2025': 0.0}, 'ni_class_1_employee': {'2025': 0.0}, 'ni_class_1_employee_additional': {'2025': 0.0}, 'ni_liable': {'2025': 0.0}, 'ni_class_1_employer': {'2025': 0.0}, 'other_tax_credits': {'2025': 0.0}, 'earned_income_tax': {'2025': -486.0}, 'total_income': {'2025': 0.0}, 'income_tax': {'2025': -486.0}, 'taxed_income': {'2025': -2430.0}, 'adjusted_net_income': {'2025': 0.0}, 'total_pension_income': {'2025': 0.0}, 'social_security_income': {'2025': 0.0}, 'income_tax_pre_charges': {'2025': -486.0}, 'personal_allowance': {'2025': 2430.0}, 'blind_persons_allowance': {'2025': 0.0}, 'married_couples_allowance': {'2025': 0.0}, 'married_couples_allowance_deduction': {'2025': 0.0}, 'capped_mcad': {'2025': 0.0}, 'pension_annual_allowance': {'2025': 0.0}, 'trading_allowance': {'2025': 0.0}, 'trading_allowance_deduction': {'2025': 0.0}, 'property_allowance': {'2025': 0.0}, 'property_allowance_deduction': {'2025': 0.0}, 'savings_allowance': {'2025': 0.0}, 'dividend_allowance': {'2025': 0.0}, 'gift_aid': {'2025': 0.0}, 'covenanted_payments': {'2025': 0.0}, 'charitable_investment_gifts': {'2025': 0.0}, 'other_deductions': {'2025': 0.0}, 'allowances': {'2025': 2430.0}, 'unused_personal_allowance': {'2025': 0.0}, 'meets_marriage_allowance_income_conditions': {'2025': 0.0}, 'partners_unused_personal_allowance': {'2025': 0.0}, 'marriage_allowance': {'2025': 0.0}, 'received_allowances': {'2025': 2430.0}, 'received_allowances_savings_income': {'2025': 0.0}, 'received_allowances_dividend_income': {'2025': 0.0}, 'received_allowances_earned_income': {'2025': 2430.0}, 'savings_income_tax': {'2025': 0.0}, 'dividend_income_tax': {'2025': 0.0}, 'loss_relief': {'2025': 0.0}, 'capital_allowances': {'2025': 0.0}, 'deficiency_relief': {'2025': 0.0}, 'employment_deductions': {'2025': 0.0}, 'employment_expenses': {'2025': 0.0}, 'CB_HITC': {'2025': 0.0}, 'higher_rate_earned_income': {'2025': 0.0}, 'add_rate_earned_income': {'2025': 0.0}, 'earned_taxable_income': {'2025': -2430.0}, 'basic_rate_earned_income': {'2025': -2430.0}, 'taxable_pension_income': {'2025': 0.0}, 'taxable_miscellaneous_income': {'2025': 0.0}, 'taxed_dividend_income': {'2025': 0.0}, 'taxable_dividend_income': {'2025': 0.0}, 'taxable_social_security_income': {'2025': 0.0}, 'taxable_employment_income': {'2025': 0.0}, 'trading_loss': {'2025': 0.0}, 'taxable_self_employment_income': {'2025': 0.0}, 'taxable_property_income': {'2025': 0.0}, 'individual_savings_account_interest_income': {'2025': 0.0}, 'taxable_savings_interest_income': {'2025': 0.0}, 'tax_free_savings_income': {'2025': 0.0}, 'higher_rate_earned_income_tax': {'2025': 0.0}, 'add_rate_earned_income_tax': {'2025': 0.0}, 'tax_band': {'2025': 0.0}, 'basic_rate_earned_income_tax': {'2025': -486.0}, 'basic_rate_savings_income_pre_starter': {'2025': 0.0}, 'taxed_savings_income': {'2025': 0.0}, 'higher_rate_savings_income': {'2025': 0.0}, 'savings_starter_rate_income': {'2025': 0.0}, 'add_rate_savings_income': {'2025': 0.0}, 'basic_rate_savings_income': {'2025': 0.0}, 'personal_pension_contributions_tax': {'2025': 0.0}, 'pension_contributions_relief': {'2025': 0.0}, 'pension_contributions': {'2025': 0.0}, 'statutory_maternity_pay': {'2025': 0.0}, 'statutory_sick_pay': {'2025': 0.0}, 'pays_scottish_income_tax': {'2025': 0.0}}}})" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine import Simulation\n", + "\n", + "sim = Simulation({\n", + " \"scope\": \"household\", # Required for this\n", + " \"country\": \"uk\", # or \"us\"\n", + " \"time_period\": 2025,\n", + " \"data\": { # Required for this\n", + " \"people\": {\n", + " \"person\": {\n", + " \"age\": {\n", + " \"2025\": 30,\n", + " },\n", + " \"employment_income\": {\n", + " \"2025\": 30_000,\n", + " },\n", + " }\n", + " }\n", + " },\n", + " \"reform\": {\n", + " \"gov.hmrc.income_tax.allowances.personal_allowance.amount\": 15_000,\n", + " }\n", + "})\n", + "\n", + "sim.calculate()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Output schema\n", + "\n", + "`calculate_household_comparison` or `calculate` (when `scope=household` and `reform is not None`) return the following schema." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'properties': {'full_household_baseline': {'additionalProperties': {'anyOf': [{'additionalProperties': {'additionalProperties': {'additionalProperties': {'anyOf': [{'type': 'number'},\n", + " {'type': 'string'},\n", + " {'type': 'boolean'},\n", + " {'items': {}, 'type': 'array'},\n", + " {'type': 'null'}]},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " {'items': {'items': {'additionalProperties': {'anyOf': [{'type': 'string'},\n", + " {'type': 'integer'}]},\n", + " 'type': 'object'},\n", + " 'type': 'array'},\n", + " 'type': 'array'}]},\n", + " 'title': 'Full Household Baseline',\n", + " 'type': 'object'},\n", + " 'full_household_reform': {'additionalProperties': {'anyOf': [{'additionalProperties': {'additionalProperties': {'additionalProperties': {'anyOf': [{'type': 'number'},\n", + " {'type': 'string'},\n", + " {'type': 'boolean'},\n", + " {'items': {}, 'type': 'array'},\n", + " {'type': 'null'}]},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " {'items': {'items': {'additionalProperties': {'anyOf': [{'type': 'string'},\n", + " {'type': 'integer'}]},\n", + " 'type': 'object'},\n", + " 'type': 'array'},\n", + " 'type': 'array'}]},\n", + " 'title': 'Full Household Reform',\n", + " 'type': 'object'},\n", + " 'change': {'additionalProperties': {'anyOf': [{'additionalProperties': {'additionalProperties': {'additionalProperties': {'anyOf': [{'type': 'number'},\n", + " {'type': 'string'},\n", + " {'type': 'boolean'},\n", + " {'items': {}, 'type': 'array'},\n", + " {'type': 'null'}]},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " {'items': {'items': {'additionalProperties': {'anyOf': [{'type': 'string'},\n", + " {'type': 'integer'}]},\n", + " 'type': 'object'},\n", + " 'type': 'array'},\n", + " 'type': 'array'}]},\n", + " 'title': 'Change',\n", + " 'type': 'object'}},\n", + " 'required': ['full_household_baseline', 'full_household_reform', 'change'],\n", + " 'title': 'HouseholdComparison',\n", + " 'type': 'object'}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine.outputs.household.comparison.calculate_household_comparison import HouseholdComparison\n", + "\n", + "HouseholdComparison.model_json_schema()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/reference/calculate_single_economy.ipynb b/docs/reference/calculate_single_economy.ipynb new file mode 100644 index 00000000..0966e8ee --- /dev/null +++ b/docs/reference/calculate_single_economy.ipynb @@ -0,0 +1,157 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Simulate outcomes for a large population\n", + "\n", + "Use `Simulation.calculate_single_economy()` to use PolicyEngine's tax-benefit model to compute taxes, benefits and other household properties for a large dataset (usually representing a country). This notebook demonstrates how to use this function." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "SingleEconomy(fiscal=FiscalSummary(tax_revenue=658911285719.5891, federal_tax=658911285719.5891, federal_balance=309089098855.4849, state_tax=0.0, government_spending=349822186864.1042, tax_benefit_programs={'income_tax': 333376287037.05945, 'national_insurance': 52985626776.773834, 'ni_employer': 126330649370.35953, 'vat': 211671832822.39133, 'council_tax': 49007055050.00724, 'fuel_duty': 26506672341.204205, 'tax_credits': -34929879.49872104, 'universal_credit': -73459549194.97665, 'child_benefit': -14311471487.935827, 'state_pension': -132795868621.44594, 'pension_credit': -6252358021.417119}, household_net_income=1566028514855.0789), inequality=InequalitySummary(gini=0.36255397405553097, top_10_share=0.3260927004295773, top_1_share=0.13145609415091833), poverty=[PovertyRateMetric(age_group='child', racial_group='all', gender='male', relative=True, poverty_rate='regular', value=0.0937829241156578), PovertyRateMetric(age_group='child', racial_group='all', gender='male', relative=True, poverty_rate='deep', value=0.006947669200599194), PovertyRateMetric(age_group='child', racial_group='all', gender='male', relative=False, poverty_rate='regular', value=770371.125), PovertyRateMetric(age_group='child', racial_group='all', gender='male', relative=False, poverty_rate='deep', value=57070.984375), PovertyRateMetric(age_group='child', racial_group='all', gender='female', relative=True, poverty_rate='regular', value=0.08119998127222061), PovertyRateMetric(age_group='child', racial_group='all', gender='female', relative=True, poverty_rate='deep', value=0.005070670507848263), PovertyRateMetric(age_group='child', racial_group='all', gender='female', relative=False, poverty_rate='regular', value=866217.375), PovertyRateMetric(age_group='child', racial_group='all', gender='female', relative=False, poverty_rate='deep', value=54092.4140625), PovertyRateMetric(age_group='child', racial_group='all', gender='all', relative=True, poverty_rate='regular', value=0.0866740345954895), PovertyRateMetric(age_group='child', racial_group='all', gender='all', relative=True, poverty_rate='deep', value=0.005887233652174473), PovertyRateMetric(age_group='child', racial_group='all', gender='all', relative=False, poverty_rate='regular', value=1636588.5), PovertyRateMetric(age_group='child', racial_group='all', gender='all', relative=False, poverty_rate='deep', value=111163.3828125), PovertyRateMetric(age_group='working_age', racial_group='all', gender='male', relative=True, poverty_rate='regular', value=0.09058031439781189), PovertyRateMetric(age_group='working_age', racial_group='all', gender='male', relative=True, poverty_rate='deep', value=0.0320294052362442), PovertyRateMetric(age_group='working_age', racial_group='all', gender='male', relative=False, poverty_rate='regular', value=2207394.25), PovertyRateMetric(age_group='working_age', racial_group='all', gender='male', relative=False, poverty_rate='deep', value=780539.625), PovertyRateMetric(age_group='working_age', racial_group='all', gender='female', relative=True, poverty_rate='regular', value=0.06885619461536407), PovertyRateMetric(age_group='working_age', racial_group='all', gender='female', relative=True, poverty_rate='deep', value=0.006544755306094885), PovertyRateMetric(age_group='working_age', racial_group='all', gender='female', relative=False, poverty_rate='regular', value=1406585.625), PovertyRateMetric(age_group='working_age', racial_group='all', gender='female', relative=False, poverty_rate='deep', value=133695.4375), PovertyRateMetric(age_group='working_age', racial_group='all', gender='all', relative=True, poverty_rate='regular', value=0.08067396283149719), PovertyRateMetric(age_group='working_age', racial_group='all', gender='all', relative=True, poverty_rate='deep', value=0.02040824294090271), PovertyRateMetric(age_group='working_age', racial_group='all', gender='all', relative=False, poverty_rate='regular', value=3613979.5), PovertyRateMetric(age_group='working_age', racial_group='all', gender='all', relative=False, poverty_rate='deep', value=914235.1875), PovertyRateMetric(age_group='senior', racial_group='all', gender='male', relative=True, poverty_rate='regular', value=0.019906071946024895), PovertyRateMetric(age_group='senior', racial_group='all', gender='male', relative=True, poverty_rate='deep', value=0.0001492029696237296), PovertyRateMetric(age_group='senior', racial_group='all', gender='male', relative=False, poverty_rate='regular', value=120630.84375), PovertyRateMetric(age_group='senior', racial_group='all', gender='male', relative=False, poverty_rate='deep', value=904.1703491210938), PovertyRateMetric(age_group='senior', racial_group='all', gender='female', relative=True, poverty_rate='regular', value=0.04476610943675041), PovertyRateMetric(age_group='senior', racial_group='all', gender='female', relative=True, poverty_rate='deep', value=0.0012297447538003325), PovertyRateMetric(age_group='senior', racial_group='all', gender='female', relative=False, poverty_rate='regular', value=315600.8125), PovertyRateMetric(age_group='senior', racial_group='all', gender='female', relative=False, poverty_rate='deep', value=8669.693359375), PovertyRateMetric(age_group='senior', racial_group='all', gender='all', relative=True, poverty_rate='regular', value=0.03327473625540733), PovertyRateMetric(age_group='senior', racial_group='all', gender='all', relative=True, poverty_rate='deep', value=0.0007302720914594829), PovertyRateMetric(age_group='senior', racial_group='all', gender='all', relative=False, poverty_rate='regular', value=436231.65625), PovertyRateMetric(age_group='senior', racial_group='all', gender='all', relative=False, poverty_rate='deep', value=9573.8642578125), PovertyRateMetric(age_group='all', racial_group='all', gender='male', relative=True, poverty_rate='regular', value=0.08017819374799728), PovertyRateMetric(age_group='all', racial_group='all', gender='male', relative=True, poverty_rate='deep', value=0.021698515862226486), PovertyRateMetric(age_group='all', racial_group='all', gender='male', relative=False, poverty_rate='regular', value=3098396.25), PovertyRateMetric(age_group='all', racial_group='all', gender='male', relative=False, poverty_rate='deep', value=838514.75), PovertyRateMetric(age_group='all', racial_group='all', gender='female', relative=True, poverty_rate='regular', value=0.06785593926906586), PovertyRateMetric(age_group='all', racial_group='all', gender='female', relative=True, poverty_rate='deep', value=0.005150204990059137), PovertyRateMetric(age_group='all', racial_group='all', gender='female', relative=False, poverty_rate='regular', value=2588403.75), PovertyRateMetric(age_group='all', racial_group='all', gender='female', relative=False, poverty_rate='deep', value=196457.53125), PovertyRateMetric(age_group='all', racial_group='all', gender='all', relative=True, poverty_rate='regular', value=0.07405701279640198), PovertyRateMetric(age_group='all', racial_group='all', gender='all', relative=True, poverty_rate='deep', value=0.013478054665029049), PovertyRateMetric(age_group='all', racial_group='all', gender='all', relative=False, poverty_rate='regular', value=5686797.5), PovertyRateMetric(age_group='all', racial_group='all', gender='all', relative=False, poverty_rate='deep', value=1034972.5)])" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine import Simulation\n", + "\n", + "sim = Simulation({\n", + " \"scope\": \"macro\", # Required for this\n", + " \"country\": \"uk\", # or \"us\"\n", + " \"time_period\": 2025,\n", + "})\n", + "\n", + "sim.calculate()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Output schema\n", + "\n", + "`calculate_single_economy` or `calculate` (when `scope=macro` and `reform=None`) return the following schema." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$defs': {'FiscalSummary': {'properties': {'tax_revenue': {'title': 'Tax Revenue',\n", + " 'type': 'number'},\n", + " 'federal_tax': {'title': 'Federal Tax', 'type': 'number'},\n", + " 'federal_balance': {'title': 'Federal Balance', 'type': 'number'},\n", + " 'state_tax': {'title': 'State Tax', 'type': 'number'},\n", + " 'government_spending': {'title': 'Government Spending', 'type': 'number'},\n", + " 'tax_benefit_programs': {'additionalProperties': {'type': 'number'},\n", + " 'title': 'Tax Benefit Programs',\n", + " 'type': 'object'},\n", + " 'household_net_income': {'title': 'Household Net Income',\n", + " 'type': 'number'}},\n", + " 'required': ['tax_revenue',\n", + " 'federal_tax',\n", + " 'federal_balance',\n", + " 'state_tax',\n", + " 'government_spending',\n", + " 'tax_benefit_programs',\n", + " 'household_net_income'],\n", + " 'title': 'FiscalSummary',\n", + " 'type': 'object'},\n", + " 'InequalitySummary': {'properties': {'gini': {'title': 'Gini',\n", + " 'type': 'number'},\n", + " 'top_10_share': {'title': 'Top 10 Share', 'type': 'number'},\n", + " 'top_1_share': {'title': 'Top 1 Share', 'type': 'number'}},\n", + " 'required': ['gini', 'top_10_share', 'top_1_share'],\n", + " 'title': 'InequalitySummary',\n", + " 'type': 'object'},\n", + " 'PovertyRateMetric': {'properties': {'age_group': {'enum': ['child',\n", + " 'working_age',\n", + " 'senior',\n", + " 'all'],\n", + " 'title': 'Age Group',\n", + " 'type': 'string'},\n", + " 'racial_group': {'enum': ['white', 'black', 'hispanic', 'other', 'all'],\n", + " 'title': 'Racial Group',\n", + " 'type': 'string'},\n", + " 'gender': {'enum': ['male', 'female', 'all'],\n", + " 'title': 'Gender',\n", + " 'type': 'string'},\n", + " 'relative': {'title': 'Relative', 'type': 'boolean'},\n", + " 'poverty_rate': {'enum': ['regular',\n", + " 'deep',\n", + " 'uk_hbai_bhc',\n", + " 'uk_hbai_bhc_half',\n", + " 'us_spm',\n", + " 'us_spm_half'],\n", + " 'title': 'Poverty Rate',\n", + " 'type': 'string'},\n", + " 'value': {'title': 'Value', 'type': 'number'}},\n", + " 'required': ['age_group',\n", + " 'racial_group',\n", + " 'gender',\n", + " 'relative',\n", + " 'poverty_rate',\n", + " 'value'],\n", + " 'title': 'PovertyRateMetric',\n", + " 'type': 'object'}},\n", + " 'properties': {'fiscal': {'$ref': '#/$defs/FiscalSummary'},\n", + " 'inequality': {'$ref': '#/$defs/InequalitySummary'},\n", + " 'poverty': {'items': {'$ref': '#/$defs/PovertyRateMetric'},\n", + " 'title': 'Poverty',\n", + " 'type': 'array'}},\n", + " 'required': ['fiscal', 'inequality', 'poverty'],\n", + " 'title': 'SingleEconomy',\n", + " 'type': 'object'}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine.outputs.macro.single.calculate_single_economy import SingleEconomy\n", + "\n", + "SingleEconomy.model_json_schema()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/reference/calculate_single_household.ipynb b/docs/reference/calculate_single_household.ipynb new file mode 100644 index 00000000..7801a627 --- /dev/null +++ b/docs/reference/calculate_single_household.ipynb @@ -0,0 +1,123 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Simulate outcomes for specific households\n", + "\n", + "Use `Simulation.calculate_single_household()` to use PolicyEngine's tax-benefit model to compute taxes, benefits and other downstream properties of individual households that you specify. This notebook demonstrates how to use this function to simulate outcomes for specific households." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "SingleHousehold(full_household={'people': {'person': {'age': {'2025': 30.0}, 'employment_income': {'2025': 30000.0}, 'employment_income_before_lsr': {'2025': 30000.0}, 'private_pension_income': {'2025': 0.0}, 'pension_income': {'2025': 0.0}, 'state_pension': {'2025': 0.0}, 'self_employment_income': {'2025': 0.0}, 'property_income': {'2025': 0.0}, 'savings_interest_income': {'2025': 0.0}, 'dividend_income': {'2025': 0.0}, 'sublet_income': {'2025': 0.0}, 'miscellaneous_income': {'2025': 0.0}, 'private_transfer_income': {'2025': 0.0}, 'lump_sum_income': {'2025': 0.0}, 'maintenance_income': {'2025': 0.0}, 'other_investment_income': {'2025': 0.0}, 'dla_sc_category': {'2025': 'NONE'}, 'dla_m_category': {'2025': 'NONE'}, 'pip_m_category': {'2025': 'NONE'}, 'pip_dl_category': {'2025': 'NONE'}, 'receives_carers_allowance': {'2025': False}, 'childcare_expenses': {'2025': 0.0}, 'employer_pension_contributions': {'2025': 0.0}, 'employee_pension_contributions': {'2025': 0.0}, 'personal_pension_contributions': {'2025': 0.0}, 'maintenance_expenses': {'2025': 0.0}, 'bi_individual_phaseout': {'2025': 0.0}, 'bi_household_phaseout': {'2025': 0.0}, 'bi_phaseout': {'2025': 0.0}, 'basic_income': {'2025': 0.0}, 'bi_maximum': {'2025': 0.0}, 'attends_private_school': {'2025': False}, 'employer_cost': {'2025': 32884.2}, 'baseline_employer_cost': {'2025': 32884.2}, 'adjusted_employer_cost': {'2025': 32884.2}, 'employer_ni_response_consumer_incidence': {'2025': 0.0}, 'employer_ni_response_capital_incidence': {'2025': 0.0}, 'employer_ni_fixed_employer_cost_change': {'2025': 0.0}, 'marginal_tax_rate': {'2025': 0.27999997}, 'cliff_evaluated': {'2025': True}, 'cliff_gap': {'2025': 0.0}, 'is_on_cliff': {'2025': False}, 'person_id': {'2025': 0.0}, 'people': {'2025': 1.0}, 'raw_person_weight': {'2025': 1.0}, 'person_weight': {'2025': 1.0}, 'adult_index': {'2025': 1.0}, 'birth_year': {'2025': 1995.0}, 'over_16': {'2025': True}, 'is_adult': {'2025': True}, 'is_child': {'2025': False}, 'child_index': {'2025': -1.0}, 'is_eldest_child': {'2025': True}, 'is_benunit_eldest_child': {'2025': False}, 'marital_status': {'2025': 'SINGLE'}, 'current_education': {'2025': 'NOT_IN_EDUCATION'}, 'highest_education': {'2025': 'UPPER_SECONDARY'}, 'in_FE': {'2025': False}, 'in_HE': {'2025': False}, 'gender': {'2025': 'MALE'}, 'is_male': {'2025': True}, 'is_female': {'2025': False}, 'is_household_head': {'2025': True}, 'is_benunit_head': {'2025': True}, 'in_social_housing': {'2025': False}, 'is_WA_adult': {'2025': True}, 'is_young_child': {'2025': False}, 'age_under_18': {'2025': False}, 'age_18_64': {'2025': True}, 'age_over_64': {'2025': False}, 'is_older_child': {'2025': False}, 'is_higher_earner': {'2025': True}, 'person_benunit_id': {'2025': 0.0}, 'person_household_id': {'2025': 0.0}, 'role': {'2025': ''}, 'person_benunit_role': {'2025': ''}, 'person_household_role': {'2025': ''}, 'is_disabled_for_benefits': {'2025': False}, 'is_enhanced_disabled_for_benefits': {'2025': False}, 'is_severely_disabled_for_benefits': {'2025': False}, 'person_state_id': {'2025': 0.0}, 'person_state_role': {'2025': ''}, 'is_blind': {'2025': False}, 'is_carer_for_benefits': {'2025': False}, 'personal_rent': {'2025': 0.0}, 'weekly_childcare_expenses': {'2025': 0.0}, 'earned_income': {'2025': 30000.0}, 'market_income': {'2025': 30000.0}, 'hours_worked': {'2025': 0.0}, 'in_work': {'2025': True}, 'weekly_hours': {'2025': 0.0}, 'employment_status': {'2025': 'UNEMPLOYED'}, 'capital_income': {'2025': 0.0}, 'base_net_income': {'2025': 0.0}, 'is_apprentice': {'2025': False}, 'minimum_wage_category': {'2025': 'OVER_24'}, 'minimum_wage': {'2025': 9.5}, 'income_decile': {'2025': 10.0}, 'household_statutory_maternity_pay': {'2025': 0.0}, 'household_statutory_paternity_pay': {'2025': 0.0}, 'household_statutory_sick_pay': {'2025': 0.0}, 'capital_gains': {'2025': 0.0}, 'is_QYP': {'2025': False}, 'is_child_or_QYP': {'2025': False}, 'employment_benefits': {'2025': 0.0}, 'relative_income_change': {'2025': 0.0}, 'relative_wage_change': {'2025': 0.0}, 'income_elasticity_lsr': {'2025': 0.0}, 'substitution_elasticity_lsr': {'2025': 0.0}, 'employment_income_behavioral_response': {'2025': 0.0}, 'income_support_reported': {'2025': 0.0}, 'attendance_allowance': {'2025': 0.0}, 'attendance_allowance_reported': {'2025': 0.0}, 'aa_category': {'2025': 'NONE'}, 'esa_income_reported': {'2025': 0.0}, 'iidb': {'2025': 0.0}, 'iidb_reported': {'2025': 0.0}, 'jsa_contrib': {'2025': 0.0}, 'jsa_contrib_reported': {'2025': 0.0}, 'maternity_allowance_reported': {'2025': 0.0}, 'maternity_allowance': {'2025': 0.0}, 'ssmg_reported': {'2025': 0.0}, 'ssmg': {'2025': 0.0}, 'council_tax_benefit_reported': {'2025': 0.0}, 'working_tax_credit_reported': {'2025': 0.0}, 'child_tax_credit_reported': {'2025': 0.0}, 'is_CTC_child_limit_exempt': {'2025': True}, 'is_child_for_CTC': {'2025': False}, 'jsa_income_reported': {'2025': 0.0}, 'bsp': {'2025': 0.0}, 'bsp_reported': {'2025': 0.0}, 'incapacity_benefit': {'2025': 0.0}, 'incapacity_benefit_reported': {'2025': 0.0}, 'sda': {'2025': 0.0}, 'sda_reported': {'2025': 0.0}, 'carers_allowance': {'2025': 0.0}, 'care_hours': {'2025': 0.0}, 'carers_allowance_reported': {'2025': 0.0}, 'armed_forces_independence_payment': {'2025': 0.0}, 'esa_contrib': {'2025': 0.0}, 'esa_contrib_reported': {'2025': 0.0}, 'esa': {'2025': 0.0}, 'afcs': {'2025': 0.0}, 'afcs_reported': {'2025': 0.0}, 'student_loans': {'2025': 0.0}, 'adult_ema': {'2025': 0.0}, 'child_ema': {'2025': 0.0}, 'access_fund': {'2025': 0.0}, 'education_grants': {'2025': 0.0}, 'student_payments': {'2025': 0.0}, 'state_pension_age': {'2025': 66.0}, 'is_SP_age': {'2025': False}, 'state_pension_type': {'2025': 'NONE'}, 'basic_state_pension': {'2025': 0.0}, 'additional_state_pension': {'2025': 0.0}, 'new_state_pension': {'2025': 0.0}, 'state_pension_reported': {'2025': 0.0}, 'winter_fuel_allowance_reported': {'2025': 0.0}, 'dla_m_reported': {'2025': 0.0}, 'dla_m': {'2025': 0.0}, 'dla_sc_reported': {'2025': 0.0}, 'dla_sc': {'2025': 0.0}, 'dla_sc_middle_plus': {'2025': False}, 'receives_highest_dla_sc': {'2025': False}, 'dla': {'2025': 0.0}, 'housing_benefit_reported': {'2025': 0.0}, 'household_benefits_individual_non_dep_deduction': {'2025': 6476.6}, 'housing_benefit_individual_non_dep_deduction_eligible': {'2025': True}, 'universal_credit_reported': {'2025': 0.0}, 'uc_is_child_born_before_child_limit': {'2025': False}, 'uc_individual_child_element': {'2025': 0.0}, 'uc_individual_disabled_child_element': {'2025': 0.0}, 'uc_individual_severely_disabled_child_element': {'2025': 0.0}, 'uc_is_in_startup_period': {'2025': False}, 'uc_minimum_income_floor': {'2025': 17290.0}, 'uc_mif_applies': {'2025': False}, 'uc_mif_capped_earned_income': {'2025': 30000.0}, 'uc_limited_capability_for_WRA': {'2025': False}, 'uc_individual_non_dep_deduction_eligible': {'2025': True}, 'uc_individual_non_dep_deduction': {'2025': 1125.3458}, 'uc_non_dep_deduction_exempt': {'2025': False}, 'pip_m_reported': {'2025': 0.0}, 'pip_m': {'2025': 0.0}, 'pip_dl_reported': {'2025': 0.0}, 'pip_dl': {'2025': 0.0}, 'receives_enhanced_pip_dl': {'2025': False}, 'pip': {'2025': 0.0}, 'pension_credit_reported': {'2025': 0.0}, 'tax': {'2025': 4880.4033}, 'tax_reported': {'2025': 0.0}, 'tax_modelling': {'2025': 4880.4033}, 'child_benefit_reported': {'2025': 0.0}, 'child_benefit_respective_amount': {'2025': 0.0}, 'relative_capital_gains_mtr_change': {'2025': 0.0}, 'capital_gains_elasticity': {'2025': 0.0}, 'capital_gains_behavioural_response': {'2025': 0.0}, 'capital_gains_before_response': {'2025': 0.0}, 'adult_index_cg': {'2025': 1.0}, 'marginal_tax_rate_on_capital_gains': {'2025': 0.0}, 'capital_gains_tax': {'2025': 0.0}, 'ni_employee': {'2025': 1394.4033}, 'national_insurance': {'2025': 1394.4033}, 'ni_employer': {'2025': 2884.2}, 'ni_self_employed': {'2025': 0.0}, 'total_national_insurance': {'2025': 4278.6035}, 'ni_class_4_maximum': {'2025': 917.73663}, 'ni_class_4': {'2025': 0.0}, 'ni_class_4_main': {'2025': 0.0}, 'ni_class_3': {'2025': 0.0}, 'ni_class_2': {'2025': 0.0}, 'ni_class_1_employee_primary': {'2025': 1394.4033}, 'ni_class_1_income': {'2025': 30000.0}, 'ni_class_1_employee': {'2025': 1394.4033}, 'ni_class_1_employee_additional': {'2025': 0.0}, 'ni_liable': {'2025': True}, 'ni_class_1_employer': {'2025': 2884.2}, 'other_tax_credits': {'2025': 0.0}, 'earned_income_tax': {'2025': 3486.0}, 'total_income': {'2025': 30000.0}, 'income_tax': {'2025': 3486.0}, 'taxed_income': {'2025': 17430.0}, 'adjusted_net_income': {'2025': 30000.0}, 'total_pension_income': {'2025': 0.0}, 'social_security_income': {'2025': 0.0}, 'income_tax_pre_charges': {'2025': 3486.0}, 'personal_allowance': {'2025': 12570.0}, 'blind_persons_allowance': {'2025': 0.0}, 'married_couples_allowance': {'2025': 0.0}, 'married_couples_allowance_deduction': {'2025': 0.0}, 'capped_mcad': {'2025': 0.0}, 'pension_annual_allowance': {'2025': 40000.0}, 'trading_allowance': {'2025': 1000.0}, 'trading_allowance_deduction': {'2025': 0.0}, 'property_allowance': {'2025': 1000.0}, 'property_allowance_deduction': {'2025': 0.0}, 'savings_allowance': {'2025': 1000.0}, 'dividend_allowance': {'2025': 500.0}, 'gift_aid': {'2025': 0.0}, 'covenanted_payments': {'2025': 0.0}, 'charitable_investment_gifts': {'2025': 0.0}, 'other_deductions': {'2025': 0.0}, 'allowances': {'2025': 12570.0}, 'unused_personal_allowance': {'2025': 0.0}, 'meets_marriage_allowance_income_conditions': {'2025': True}, 'partners_unused_personal_allowance': {'2025': 0.0}, 'marriage_allowance': {'2025': 0.0}, 'received_allowances': {'2025': 12570.0}, 'received_allowances_savings_income': {'2025': 0.0}, 'received_allowances_dividend_income': {'2025': 0.0}, 'received_allowances_earned_income': {'2025': 12570.0}, 'savings_income_tax': {'2025': 0.0}, 'dividend_income_tax': {'2025': 0.0}, 'loss_relief': {'2025': 0.0}, 'capital_allowances': {'2025': 0.0}, 'deficiency_relief': {'2025': 0.0}, 'employment_deductions': {'2025': 0.0}, 'employment_expenses': {'2025': 0.0}, 'CB_HITC': {'2025': 0.0}, 'higher_rate_earned_income': {'2025': 0.0}, 'add_rate_earned_income': {'2025': 0.0}, 'earned_taxable_income': {'2025': 17430.0}, 'basic_rate_earned_income': {'2025': 17430.0}, 'taxable_pension_income': {'2025': 0.0}, 'taxable_miscellaneous_income': {'2025': 0.0}, 'taxed_dividend_income': {'2025': 0.0}, 'taxable_dividend_income': {'2025': 0.0}, 'taxable_social_security_income': {'2025': 0.0}, 'taxable_employment_income': {'2025': 30000.0}, 'trading_loss': {'2025': 0.0}, 'taxable_self_employment_income': {'2025': 0.0}, 'taxable_property_income': {'2025': 0.0}, 'individual_savings_account_interest_income': {'2025': 0.0}, 'taxable_savings_interest_income': {'2025': 0.0}, 'tax_free_savings_income': {'2025': 0.0}, 'higher_rate_earned_income_tax': {'2025': 0.0}, 'add_rate_earned_income_tax': {'2025': 0.0}, 'tax_band': {'2025': 'BASIC'}, 'basic_rate_earned_income_tax': {'2025': 3486.0}, 'basic_rate_savings_income_pre_starter': {'2025': 0.0}, 'taxed_savings_income': {'2025': 0.0}, 'higher_rate_savings_income': {'2025': 0.0}, 'savings_starter_rate_income': {'2025': 0.0}, 'add_rate_savings_income': {'2025': 0.0}, 'basic_rate_savings_income': {'2025': 0.0}, 'personal_pension_contributions_tax': {'2025': 0.0}, 'pension_contributions_relief': {'2025': 0.0}, 'pension_contributions': {'2025': 0.0}, 'statutory_maternity_pay': {'2025': 0.0}, 'statutory_sick_pay': {'2025': 0.0}, 'pays_scottish_income_tax': {'2025': False}}}})" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine import Simulation\n", + "\n", + "sim = Simulation({\n", + " \"scope\": \"household\", # Required for this\n", + " \"country\": \"uk\", # or \"us\"\n", + " \"time_period\": 2025,\n", + " \"data\": { # Required for this\n", + " \"people\": {\n", + " \"person\": {\n", + " \"age\": {\n", + " \"2025\": 30,\n", + " },\n", + " \"employment_income\": {\n", + " \"2025\": 30_000,\n", + " },\n", + " }\n", + " }\n", + " }\n", + "})\n", + "\n", + "sim.calculate()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Output schema\n", + "\n", + "`calculate_single_household` or `calculate` (when `scope=household` and `reform=None`) return the following schema." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'description': 'Statistics for a single household scenario.',\n", + " 'properties': {'full_household': {'additionalProperties': {'anyOf': [{'additionalProperties': {'additionalProperties': {'additionalProperties': {'anyOf': [{'type': 'number'},\n", + " {'type': 'string'},\n", + " {'type': 'boolean'},\n", + " {'items': {}, 'type': 'array'},\n", + " {'type': 'null'}]},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " 'type': 'object'},\n", + " {'items': {'items': {'additionalProperties': {'anyOf': [{'type': 'string'},\n", + " {'type': 'integer'}]},\n", + " 'type': 'object'},\n", + " 'type': 'array'},\n", + " 'type': 'array'}]},\n", + " 'title': 'Full Household',\n", + " 'type': 'object'}},\n", + " 'required': ['full_household'],\n", + " 'title': 'SingleHousehold',\n", + " 'type': 'object'}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from policyengine.outputs.household.single.calculate_single_household import SingleHousehold\n", + "\n", + "SingleHousehold.model_json_schema()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/reference/simulation.ipynb b/docs/reference/simulation.ipynb index ee3c846a..57608985 100644 --- a/docs/reference/simulation.ipynb +++ b/docs/reference/simulation.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Simulation\n", + "# Simulation interface\n", "\n", "The `Simulation` class is the core interface of this package. You can initialise it by passing in a dictionary that matches the `SimulationOptions` schema, and then use its `calculate` methods to ask it questions." ] From 1c08b54228633d534ccf3e897bde847df2d4b397 Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 13:32:01 +1100 Subject: [PATCH 16/17] Add charts to docs --- Makefile | 1 + docs/_toc.yml | 1 + docs/add_plotly_to_book.py | 27 + docs/reference/create_charts.ipynb | 7175 +++++++++++++++++ docs/reference/simulation.ipynb | 68 +- .../macro/comparison/charts/__init__.py | 8 + .../outputs/macro/comparison/charts/decile.py | 2 +- 7 files changed, 7280 insertions(+), 2 deletions(-) create mode 100644 docs/add_plotly_to_book.py create mode 100644 docs/reference/create_charts.ipynb create mode 100644 policyengine/outputs/macro/comparison/charts/__init__.py diff --git a/Makefile b/Makefile index c1b795b7..26de991e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ all: build documentation: + python docs/add_plotly_to_book.py docs/ jb clean docs jb build docs diff --git a/docs/_toc.yml b/docs/_toc.yml index 02f70105..e901296a 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -8,4 +8,5 @@ parts: - file: reference/calculate_household_comparison - file: reference/calculate_single_economy - file: reference/calculate_economy_comparison + - file: reference/create_charts \ No newline at end of file diff --git a/docs/add_plotly_to_book.py b/docs/add_plotly_to_book.py new file mode 100644 index 00000000..822e77ab --- /dev/null +++ b/docs/add_plotly_to_book.py @@ -0,0 +1,27 @@ +import argparse +from pathlib import Path + +# This command-line tools enables Plotly charts to show in the HTML files for the Jupyter Book documentation. + +parser = argparse.ArgumentParser() +parser.add_argument("book_path", help="Path to the Jupyter Book.") + +args = parser.parse_args() + +# Find every HTML file in the Jupyter Book. Then, add a script tag to the start of the tag in each file, with the contents: +# + +book_folder = Path(args.book_path) + +for html_file in book_folder.glob("**/*.html"): + with open(html_file, "r") as f: + html = f.read() + + # Add the script tag to the start of the tag. + html = html.replace( + "", + '', + ) + + with open(html_file, "w") as f: + f.write(html) diff --git a/docs/reference/create_charts.ipynb b/docs/reference/create_charts.ipynb new file mode 100644 index 00000000..16c0c188 --- /dev/null +++ b/docs/reference/create_charts.ipynb @@ -0,0 +1,7175 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create charts for an economic comparison\n", + "\n", + "This package also comes with utilities that allow you to create charts from the economy comparison operation. In this notebook, we'll walk through them." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# First, set up the simulation\n", + "\n", + "from policyengine import Simulation\n", + "from policyengine.outputs.macro.comparison.charts import *\n", + "\n", + "sim = Simulation({\n", + " \"country\": \"uk\",\n", + " \"scope\": \"macro\",\n", + " \"reform\": {\n", + " \"gov.hmrc.income_tax.allowances.personal_allowance.amount\": 10_000,\n", + " },\n", + " \"title\": \"Lowering the personal allowance to £10,000\" # Required for charts\n", + "})\n", + "\n", + "from policyengine.utils.charts import add_fonts\n", + "\n", + "add_fonts() # Load the right font (Roboto Serif) in this notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Budget\n", + "\n", + "The budget chart shows high-level budget changes under the reform." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "decreasing": { + "marker": { + "color": "#616161" + } + }, + "increasing": { + "marker": { + "color": "#2C6496" + } + }, + "measure": [ + "relative", + "relative", + "total" + ], + "text": [ + "£24.2bn", + "£0.7bn", + "£23.5bn" + ], + "textposition": "inside", + "totals": { + "marker": { + "color": "#2C6496" + } + }, + "type": "waterfall", + "x": [ + "Tax revenues", + "Government spending", + "Public sector net worth" + ], + "y": [ + 24.18759785404004, + 0.7363357242798462, + 23.45126212976019 + ] + } + ], + "layout": { + "annotations": [ + { + "showarrow": false, + "text": "Source: PolicyEngine UK tax-benefit microsimulation model (version 2.18.0)", + "x": 0, + "xanchor": "left", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "margin": { + "b": 120, + "l": 120, + "r": 120, + "t": 120 + }, + "modebar": { + "activecolor": "#F4F4F4", + "bgcolor": "#F4F4F4", + "color": "#F4F4F4" + }, + "paper_bgcolor": "#F4F4F4", + "plot_bgcolor": "#F4F4F4", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Lowering the personal allowance to £10,000 would raise £23.5bn" + }, + "uniformtext": { + "minsize": 12, + "mode": "hide" + }, + "width": 800, + "xaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "title": { + "text": "" + }, + "zerolinecolor": "#F4F4F4" + }, + "yaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "title": { + "text": "Budgetary impact (£bn)" + }, + "zerolinecolor": "#616161" + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "create_budget_comparison_chart(sim)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is also the budget by program chart, which splits out the budgetary impact by program." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "decreasing": { + "marker": { + "color": "#616161" + } + }, + "increasing": { + "marker": { + "color": "#2C6496" + } + }, + "measure": [ + "relative", + "relative", + "relative", + "relative", + "total" + ], + "text": [ + "£24.2bn", + "-£0.4bn", + "-£0.2bn", + "-£1.0bn", + "£23.0bn" + ], + "textposition": "inside", + "totals": { + "marker": { + "color": "#2C6496" + } + }, + "type": "waterfall", + "x": [ + "Income Tax", + "Universal Credit", + "Pension Credit", + "Other", + "Combined" + ], + "y": [ + 24.2, + -0.4, + -0.2, + -1, + 23 + ] + } + ], + "layout": { + "annotations": [ + { + "showarrow": false, + "text": "Source: PolicyEngine UK tax-benefit microsimulation model (version 2.18.0)", + "x": 0, + "xanchor": "left", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "margin": { + "b": 120, + "l": 120, + "r": 120, + "t": 120 + }, + "modebar": { + "activecolor": "#F4F4F4", + "bgcolor": "#F4F4F4", + "color": "#F4F4F4" + }, + "paper_bgcolor": "#F4F4F4", + "plot_bgcolor": "#F4F4F4", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Lowering the personal allowance to £10,000 would raise £23bn" + }, + "uniformtext": { + "minsize": 12, + "mode": "hide" + }, + "width": 800, + "xaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "title": { + "text": "" + }, + "zerolinecolor": "#F4F4F4" + }, + "yaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "title": { + "text": "Budgetary impact (bn)" + }, + "zerolinecolor": "#616161" + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "create_budget_program_comparison_chart(sim)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Decile\n", + "\n", + "The decile chart shows the impact of the reform on each decile of the income distribution. You can specify whether this is over income deciles, or wealth deciles, in the UK." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "marker": { + "color": [ + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161" + ] + }, + "text": [ + "-1.1%", + "-1.5%", + "-1.7%", + "-1.8%", + "-1.5%", + "-1.9%", + "-2.0%", + "-1.9%", + "-1.8%", + "-0.8%" + ], + "type": "bar", + "x": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "y": [ + -0.010774457133013601, + -0.01452619007920295, + -0.017437944033923014, + -0.018396321126487758, + -0.015306613953115554, + -0.019266000449108846, + -0.019665841659677847, + -0.018543594207703178, + -0.018167225868890278, + -0.007966273529744838 + ] + } + ], + "layout": { + "annotations": [ + { + "showarrow": false, + "text": "Source: PolicyEngine UK tax-benefit microsimulation model (version 2.18.0)", + "x": 0, + "xanchor": "left", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "margin": { + "b": 120, + "l": 120, + "r": 120, + "t": 120 + }, + "modebar": { + "activecolor": "#F4F4F4", + "bgcolor": "#F4F4F4", + "color": "#F4F4F4" + }, + "paper_bgcolor": "#F4F4F4", + "plot_bgcolor": "#F4F4F4", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Lowering the personal allowance to £10,000 would decrease the net income of
households by 1.6% " + }, + "uniformtext": { + "minsize": 12, + "mode": "hide" + }, + "width": 800, + "xaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "tickvals": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "title": { + "text": "Income decile" + }, + "zerolinecolor": "#F4F4F4" + }, + "yaxis": { + "gridcolor": "#F4F4F4", + "tickformat": ".0%", + "ticksuffix": "", + "title": { + "text": "Average change to net income (%)" + }, + "zerolinecolor": "#616161" + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "create_decile_chart(sim, decile_variable=\"income\", relative=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Winners and losers\n", + "\n", + "The winners and losers chart shows in each decile (and overall) the share of people who come out ahead or behind under the reform. Again, you can specify whether the deciles are income or wealth in the UK." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "marker": { + "color": [ + "#2C6496", + "#D8E6F3", + "#F2F2F2", + "#BDBDBD", + "#616161" + ] + }, + "name": "All deciles", + "orientation": "h", + "showlegend": false, + "text": [ + "0.0%", + "0.1%", + "13.1%", + "74.3%", + "12.5%" + ], + "type": "bar", + "x": [ + 0, + 0.0012506888662903927, + 0.13111063617035676, + 0.7427831391111167, + 0.12485553585223622 + ], + "xaxis": "x", + "y": [ + "All", + "All", + "All", + "All", + "All" + ], + "yaxis": "y" + }, + { + "customdata": [ + "Gain more than 5%, 1: 0.0%", + "Gain more than 5%, 2: 0.0%", + "Gain more than 5%, 3: 0.0%", + "Gain more than 5%, 4: 0.0%", + "Gain more than 5%, 5: 0.0%", + "Gain more than 5%, 6: 0.0%", + "Gain more than 5%, 7: 0.0%", + "Gain more than 5%, 8: 0.0%", + "Gain more than 5%, 9: 0.0%", + "Gain more than 5%, 10: 0.0%", + "Gain less than 5%, 1: 0.0%", + "Gain less than 5%, 2: 0.8%", + "Gain less than 5%, 3: 0.4%", + "Gain less than 5%, 4: 0.0%", + "Gain less than 5%, 5: 0.0%", + "Gain less than 5%, 6: 0.0%", + "Gain less than 5%, 7: 0.0%", + "Gain less than 5%, 8: 0.0%", + "Gain less than 5%, 9: 0.0%", + "Gain less than 5%, 10: 0.0%", + "No change, 1: 62.5%", + "No change, 2: 27.6%", + "No change, 3: 17.2%", + "No change, 4: 10.3%", + "No change, 5: 2.6%", + "No change, 6: 2.9%", + "No change, 7: 1.0%", + "No change, 8: 0.8%", + "No change, 9: 1.5%", + "No change, 10: 9.0%", + "Lose less than 5%, 1: 20.3%", + "Lose less than 5%, 2: 54.3%", + "Lose less than 5%, 3: 45.3%", + "Lose less than 5%, 4: 74.6%", + "Lose less than 5%, 5: 95.7%", + "Lose less than 5%, 6: 78.5%", + "Lose less than 5%, 7: 80.9%", + "Lose less than 5%, 8: 94.4%", + "Lose less than 5%, 9: 97.6%", + "Lose less than 5%, 10: 91.0%", + "Lose more than 5%, 1: 17.1%", + "Lose more than 5%, 2: 17.2%", + "Lose more than 5%, 3: 37.0%", + "Lose more than 5%, 4: 15.1%", + "Lose more than 5%, 5: 1.7%", + "Lose more than 5%, 6: 18.6%", + "Lose more than 5%, 7: 18.1%", + "Lose more than 5%, 8: 4.8%", + "Lose more than 5%, 9: 1.0%", + "Lose more than 5%, 10: 0.0%" + ], + "hovertemplate": "%{customdata}", + "marker": { + "color": [ + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#D8E6F3", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#F2F2F2", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#BDBDBD", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161" + ] + }, + "name": "Deciles", + "orientation": "h", + "text": [ + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.8%", + "0.4%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "0.0%", + "62.5%", + "27.6%", + "17.2%", + "10.3%", + "2.6%", + "2.9%", + "1.0%", + "0.8%", + "1.5%", + "9.0%", + "20.3%", + "54.3%", + "45.3%", + "74.6%", + "95.7%", + "78.5%", + "80.9%", + "94.4%", + "97.6%", + "91.0%", + "17.1%", + "17.2%", + "37.0%", + "15.1%", + "1.7%", + "18.6%", + "18.1%", + "4.8%", + "1.0%", + "0.0%" + ], + "textposition": "inside", + "type": "bar", + "x": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0.00003002075376702071, + 0.008309730310565712, + 0.0042514917572317205, + 0, + 0, + 0, + 5.104037721866914e-7, + 1.224587452954663e-7, + 0, + 0, + 0.6251904617648583, + 0.2761051112600827, + 0.17241658338662263, + 0.10326230200712576, + 0.02634261147112231, + 0.029121268727080388, + 0.009779479442778611, + 0.007915780728303014, + 0.014550917092224268, + 0.09022786691788508, + 0.20347253259186693, + 0.5434876317349786, + 0.4532449619985432, + 0.7461001383808286, + 0.9570849978504702, + 0.7850737490397103, + 0.8087431337210494, + 0.9444749389980199, + 0.9758083400649616, + 0.9097721330821149, + 0.17130698488950774, + 0.17209752669437295, + 0.37008696285760245, + 0.15063755961204564, + 0.01657239067840755, + 0.18580498223320932, + 0.1814768764323998, + 0.047609157814931725, + 0.009640742842814045, + 0 + ], + "xaxis": "x2", + "y": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "yaxis": "y2" + } + ], + "layout": { + "annotations": [ + { + "showarrow": false, + "text": "Source: PolicyEngine UK tax-benefit microsimulation model (version 2.18.0)", + "x": 0, + "xanchor": "left", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "barmode": "stack", + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "grid": { + "columns": 1, + "rows": 2 + }, + "height": 600, + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "margin": { + "b": 120, + "l": 120, + "r": 120, + "t": 120 + }, + "modebar": { + "activecolor": "#F4F4F4", + "bgcolor": "#F4F4F4", + "color": "#F4F4F4" + }, + "paper_bgcolor": "#F4F4F4", + "plot_bgcolor": "#F4F4F4", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Lowering the personal allowance to £10,000 would raise the net income of 0.1% of
people " + }, + "uniformtext": { + "minsize": 12, + "mode": "hide" + }, + "width": 800, + "xaxis": { + "anchor": "y", + "fixedrange": true, + "gridcolor": "#F4F4F4", + "matches": "x2", + "showgrid": false, + "showticklabels": false, + "tickformat": ".0%", + "ticksuffix": "", + "title": { + "text": "" + }, + "zerolinecolor": "#F4F4F4" + }, + "xaxis2": { + "anchor": "y2", + "fixedrange": true, + "tickformat": ".0%", + "title": { + "text": "Population share" + } + }, + "yaxis": { + "domain": [ + 0.91, + 1 + ], + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "tickvals": [ + "All" + ], + "title": { + "text": "" + }, + "zerolinecolor": "#F4F4F4" + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0, + 0.85 + ], + "tickvals": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "title": { + "text": "Population share" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "create_winners_losers_chart(sim, decile_variable=\"income\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Poverty\n", + "\n", + "The poverty chart shows the impact of the reform on various poverty rates, split out by demographic groups you can specify. You can show the change to poverty rate by age group, gender or racial group (US-only), and you can choose between regular or deep poverty." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "marker": { + "color": [ + "#2C6496", + "#2C6496", + "#2C6496", + "#2C6496" + ] + }, + "text": [ + "33,440", + "52,656", + "23,791", + "109,889" + ], + "type": "bar", + "x": [ + "child", + "working_age", + "senior", + "all" + ], + "y": [ + 33440.125, + 52656.5, + 23791.21875, + 109889 + ] + } + ], + "layout": { + "annotations": [ + { + "showarrow": false, + "text": "Source: PolicyEngine UK tax-benefit microsimulation model (version 2.18.0)", + "x": 0, + "xanchor": "left", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "margin": { + "b": 120, + "l": 120, + "r": 120, + "t": 120 + }, + "modebar": { + "activecolor": "#F4F4F4", + "bgcolor": "#F4F4F4", + "color": "#F4F4F4" + }, + "paper_bgcolor": "#F4F4F4", + "plot_bgcolor": "#F4F4F4", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Lowering the personal allowance to £10,000 would raise the poverty rate by 1.9%" + }, + "uniformtext": { + "minsize": 12, + "mode": "hide" + }, + "width": 800, + "xaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "ticktext": [ + "Child", + "Working-age", + "Senior", + "All" + ], + "tickvals": [ + "child", + "working_age", + "senior", + "all" + ], + "title": { + "text": "Group" + }, + "zerolinecolor": "#F4F4F4" + }, + "yaxis": { + "gridcolor": "#F4F4F4", + "tickformat": ",.0f", + "ticksuffix": "", + "title": { + "text": "Poverty rate change" + }, + "zerolinecolor": "#616161" + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "create_poverty_chart(\n", + " sim,\n", + " age_group=None, # Specify None to show all age groups\n", + " gender=\"all\", # The rest are filters\n", + " racial_group=\"all\",\n", + " rate_relative=False, # Compare how many people are in poverty rather than the rate\n", + " change_relative=False, # Compare the absolute headcount rather than % change\n", + " poverty_rate=\"regular\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Inequality\n", + "\n", + "The inequality chart shows the impact of the reform on various inequality measures: the Gini coefficient, the share of income held by the top 10% of households (by income) and the top 1% share." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "marker": { + "color": [ + "#616161", + "#616161", + "#616161" + ] + }, + "text": [ + "0.2%", + "0.6%", + "1.4%" + ], + "type": "bar", + "x": [ + "Gini index", + "Top 10% share", + "Top 1% share" + ], + "y": [ + 0.002379218659187675, + 0.006010878001505188, + 0.014167378755875062 + ] + } + ], + "layout": { + "annotations": [ + { + "showarrow": false, + "text": "Source: PolicyEngine UK tax-benefit microsimulation model (version 2.18.0)", + "x": 0, + "xanchor": "left", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "margin": { + "b": 120, + "l": 120, + "r": 120, + "t": 120 + }, + "modebar": { + "activecolor": "#F4F4F4", + "bgcolor": "#F4F4F4", + "color": "#F4F4F4" + }, + "paper_bgcolor": "#F4F4F4", + "plot_bgcolor": "#F4F4F4", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Lowering the personal allowance to £10,000 would raise inequality" + }, + "uniformtext": { + "minsize": 12, + "mode": "hide" + }, + "width": 800, + "xaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "title": { + "text": "" + }, + "zerolinecolor": "#F4F4F4" + }, + "yaxis": { + "gridcolor": "#F4F4F4", + "tickformat": ".0%", + "ticksuffix": "", + "title": { + "text": "Change (%)" + }, + "zerolinecolor": "#616161" + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "create_inequality_chart(sim, relative=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Labor supply\n", + "\n", + "The labor supply chart shows the impact of the reform on labor supply if labor supply response assumptions have been set in the `reform`, split out by groups you can specify. You can show the change to labor supply by decile, the mechanism (or elasticity- e.g. filtering to only substitution effect-powered responses), and also by the unit (hours or earnings, though only earnings in the UK)." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "marker": { + "color": [ + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#616161", + "#2C6496", + "#616161" + ] + }, + "text": [ + "-1.3%", + "-0.1%", + "-0.7%", + "-0.2%", + "-0.1%", + "-0.4%", + "-0.5%", + "-0.5%", + "-0.1%", + "0.4%", + "-0.1%" + ], + "type": "bar", + "x": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + "all" + ], + "y": [ + -0.013388361268997362, + -0.0009001679620899952, + -0.006940002445757689, + -0.002096522479093097, + -0.000590021861392062, + -0.003602100638872149, + -0.005081699001099658, + -0.004559364671289845, + -0.001499923178195422, + 0.0038960671090264246, + -0.0007300784012860403 + ] + } + ], + "layout": { + "annotations": [ + { + "showarrow": false, + "text": "Source: PolicyEngine UK tax-benefit microsimulation model (version 2.18.0)", + "x": 0, + "xanchor": "left", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.2, + "yanchor": "bottom", + "yref": "paper" + } + ], + "margin": { + "b": 120, + "l": 120, + "r": 120, + "t": 120 + }, + "modebar": { + "activecolor": "#F4F4F4", + "bgcolor": "#F4F4F4", + "color": "#F4F4F4" + }, + "paper_bgcolor": "#F4F4F4", + "plot_bgcolor": "#F4F4F4", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Lowering the personal allowance to £10,000 would lower labor supply by 0.1%" + }, + "uniformtext": { + "minsize": 12, + "mode": "hide" + }, + "width": 800, + "xaxis": { + "gridcolor": "#F4F4F4", + "ticksuffix": "", + "ticktext": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "tickvals": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + "all" + ], + "title": { + "text": "Group" + }, + "zerolinecolor": "#F4F4F4" + }, + "yaxis": { + "gridcolor": "#F4F4F4", + "tickformat": ".0%", + "ticksuffix": "", + "title": { + "text": "Labor supply change (£bn)" + }, + "zerolinecolor": "#616161" + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sim = Simulation({\n", + " \"country\": \"uk\",\n", + " \"scope\": \"macro\",\n", + " \"reform\": {\n", + " \"gov.hmrc.income_tax.allowances.personal_allowance.amount\": 10_000,\n", + " \"gov.simulation.labor_supply_responses.substitution_elasticity\": 0.2,\n", + " },\n", + " \"title\": \"Lowering the personal allowance to £10,000\" # Required for charts\n", + "})\n", + "\n", + "create_labor_supply_chart(\n", + " sim,\n", + " decile=None,\n", + " elasticity=\"all\",\n", + " unit=\"earnings\",\n", + " change_relative=True,\n", + " change_average=False,\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/reference/simulation.ipynb b/docs/reference/simulation.ipynb index 57608985..99d2e5ee 100644 --- a/docs/reference/simulation.ipynb +++ b/docs/reference/simulation.ipynb @@ -6,7 +6,18 @@ "source": [ "# Simulation interface\n", "\n", - "The `Simulation` class is the core interface of this package. You can initialise it by passing in a dictionary that matches the `SimulationOptions` schema, and then use its `calculate` methods to ask it questions." + "The `Simulation` class is the core interface of this package. You can initialise it by passing in a dictionary that matches the `SimulationOptions` schema, and then use its `calculate` methods to ask it questions.\n", + "\n", + "Some of the options are straightforward and some are more complex. The straightforward ones are:\n", + "\n", + "* `country`: `uk` or `us`.\n", + "* `scope`: `macro` (simulating over large data to represent e.g. a country) or `household` (simulating over specific households you describe).\n", + "* `time_period`: the year to simulate.\n", + "\n", + "The next important features are:\n", + "* `reform`: the policy to use in the reform scenario if we are comparing against a different scenario.\n", + "* `baseline`: the policy to use in the baseline scenario if we are comparing against a different baseline scenario.\n", + "* `data`: either a household (if `scope` is `household`) or a large dataset name (if `scope` is `macro`)." ] }, { @@ -42,6 +53,61 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Providing `baseline` and `reform` policies\n", + "\n", + "The `baseline` and `reform` policies are dictionaries that represent the policy to simulate. You don't have to provide a reform policy (if you don't, the simulation will just simulate the baseline policy). You also don't have to provide a baseline policy (if you don't, the simulation will just compare your reform scenario against current law).\n", + "\n", + "If you do, they should each follow this syntax:\n", + "\n", + "```json\n", + "{\n", + " \"gov.hmrc.income_tax.rate\": { // Parameter address, in the country model's `parameters/` folder\n", + " \"2025\": 0.2 // Value to set the parameter to in the year 2025\n", + " }\n", + "}\n", + "```\n", + "\n", + "You can also use this shorthand to set parameters for all years:\n", + "\n", + "```json\n", + "{\n", + " \"gov.hmrc.income_tax.rate\": 0.2\n", + "}\n", + "```\n", + "\n", + "## Providing `data`\n", + "\n", + "If you set `scope` to `macro`, you should provide either:\n", + "\n", + "* A Hugging Face `.h5` dataset address in this format: `\"hf://policyengine/policyengine-us-data/cps_2023.h5\"` (`hf://owner/dataset-name/path.h5`).\n", + "* An instance of `policyengine_core.data.Dataset` (advanced).\n", + "\n", + "See `policyengine.constants` for the available datasets.\n", + "\n", + "If you set `scope` to `household`, you should provide a dictionary that represents a household. This should look like:\n", + "\n", + "```json\n", + "{\n", + " \"people\": { // Entity group\n", + " \"person\": { // Entity name\n", + " \"age\": { // Variable (in the country model's `variables/` folder)\n", + " \"2025\": 30, // Time period and value\n", + " }\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"person\"], // Group entities need a `members` field\n", + " \"region\": {\n", + " \"2025\": \"LONDON\",\n", + " }\n", + " }\n", + " }\n", + "}\n", + "```\n", + "\n", + "See the country model's repository for more information on what entity types are available.\n", + "\n", "## Module documentation\n", "\n", "```{eval-rst}\n", diff --git a/policyengine/outputs/macro/comparison/charts/__init__.py b/policyengine/outputs/macro/comparison/charts/__init__.py new file mode 100644 index 00000000..2089605e --- /dev/null +++ b/policyengine/outputs/macro/comparison/charts/__init__.py @@ -0,0 +1,8 @@ +from .budget import create_budget_comparison_chart +from .budget_by_program import create_budget_program_comparison_chart +from .decile import create_decile_chart +from .winners_losers import create_winners_losers_chart +from .poverty import create_poverty_chart +from .inequality import create_inequality_chart +from .labor_supply import create_labor_supply_chart +from .create_all_charts import create_all_charts diff --git a/policyengine/outputs/macro/comparison/charts/decile.py b/policyengine/outputs/macro/comparison/charts/decile.py index d9592477..d17c5bf2 100644 --- a/policyengine/outputs/macro/comparison/charts/decile.py +++ b/policyengine/outputs/macro/comparison/charts/decile.py @@ -52,7 +52,7 @@ def create_decile_chart( ) elif avg_change < 0: description = ( - f"decrease the net income of households by {-avg_change_str}" + f"decrease the net income of households by {avg_change_str}" ) else: description = "have no effect on household net income" From fa7c575bbcf3d848f539a7556bf9e63c823210ae Mon Sep 17 00:00:00 2001 From: Nikhil Woodruff Date: Fri, 31 Jan 2025 13:34:30 +1100 Subject: [PATCH 17/17] Fix typo --- Makefile | 2 +- docs/reference/calculate_economy_comparison.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 26de991e..49972175 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ all: build documentation: - python docs/add_plotly_to_book.py docs/ jb clean docs jb build docs + python docs/add_plotly_to_book.py docs/ install: pip install -e .[dev] diff --git a/docs/reference/calculate_economy_comparison.ipynb b/docs/reference/calculate_economy_comparison.ipynb index 81819fdb..2d6fcfd4 100644 --- a/docs/reference/calculate_economy_comparison.ipynb +++ b/docs/reference/calculate_economy_comparison.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Compare outcomes large population scenarios\n", + "# Compare outcomes for large population scenarios\n", "\n", "Use `Simulation.calculate_economy_comparison()` to use PolicyEngine's tax-benefit model to compare how taxes, benefits and other household properties change under a reform scenario. This notebook demonstrates how to use this function." ]