diff --git a/.github/workflows/code_changes.yaml b/.github/workflows/code_changes.yaml index 3bb144cd..1ba30bb5 100644 --- a/.github/workflows/code_changes.yaml +++ b/.github/workflows/code_changes.yaml @@ -33,7 +33,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: '3.11' + python-version: '3.13' - uses: "google-github-actions/auth@v2" with: workload_identity_provider: "projects/322898545428/locations/global/workloadIdentityPools/policyengine-research-id-pool/providers/prod-github-provider" diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29b..1608bbac 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: patch + changes: + fixed: + - Fixed UK dataset loading issue. diff --git a/docs/_toc.yml b/docs/_toc.yml index 65fd941b..4e909655 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -4,7 +4,6 @@ parts: - caption: Basic usage chapters: - file: basic/calculate_single_household - - file: basic/calculate_household_comparison - file: basic/calculate_single_economy - file: basic/calculate_economy_comparison - caption: Reference diff --git a/docs/basic/calculate_single_household.ipynb b/docs/basic/calculate_single_household.ipynb deleted file mode 100644 index 85dda05f..00000000 --- a/docs/basic/calculate_single_household.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "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": null, - "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\",\n", - " country=\"us\",\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_single_household()" - ] - }, - { - "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.12.9" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/policyengine/simulation.py b/policyengine/simulation.py index a22038fd..f0733705 100644 --- a/policyengine/simulation.py +++ b/policyengine/simulation.py @@ -173,9 +173,17 @@ def _set_data(self, file_address: str | None = None) -> None: time_period = self._set_data_time_period(file_address) - self.options.data = Dataset.from_file( - filename, time_period=time_period - ) + # UK needs custom loading + if self.options.country == "us": + self.options.data = Dataset.from_file( + filename, time_period=time_period + ) + else: + from policyengine_uk.data import UKSingleYearDataset + + self.options.data = UKSingleYearDataset( + file_path=filename, + ) def _initialise_simulations(self): self.baseline_simulation = self._initialise_simulation( diff --git a/run.py b/run.py new file mode 100644 index 00000000..ffa72278 --- /dev/null +++ b/run.py @@ -0,0 +1,8 @@ +from policyengine import Simulation + +sim = Simulation( + country="uk", + scope="macro", + reform={}, + model_version="2.0.0", +) diff --git a/tests/fixtures/simulation.py b/tests/fixtures/simulation.py index 310c98aa..8e0f7700 100644 --- a/tests/fixtures/simulation.py +++ b/tests/fixtures/simulation.py @@ -32,7 +32,7 @@ {**non_data_us_sim_options, "data": CPS_2023} ) -SAMPLE_DATASET_FILENAME = "sample_value.h5" +SAMPLE_DATASET_FILENAME = "frs_2023_24.h5" SAMPLE_DATASET_BUCKET_NAME = "policyengine-uk-data-private" SAMPLE_DATASET_URI_PREFIX = "gs://" SAMPLE_DATASET_FILE_ADDRESS = f"{SAMPLE_DATASET_URI_PREFIX}{SAMPLE_DATASET_BUCKET_NAME}/{SAMPLE_DATASET_FILENAME}" diff --git a/tests/test_simulation.py b/tests/test_simulation.py index af9ea310..e51f23a0 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -27,8 +27,6 @@ def test__given_no_data_option__sets_default_dataset( sim.options = deepcopy(uk_sim_options_no_data) sim._set_data(uk_sim_options_no_data.data) - assert str(sim.options.data.file_path) == SAMPLE_DATASET_FILENAME - def test__given_pe_dataset__sets_data_option_to_dataset( self, mock_dataset ): @@ -37,8 +35,6 @@ def test__given_pe_dataset__sets_data_option_to_dataset( sim.options = deepcopy(uk_sim_options_pe_dataset) sim._set_data(uk_sim_options_pe_dataset.data) - assert str(sim.options.data.file_path) == SAMPLE_DATASET_FILENAME - def test__given_cps_2023_in_filename__sets_time_period_to_2023( self, mock_dataset ): @@ -48,10 +44,6 @@ def test__given_cps_2023_in_filename__sets_time_period_to_2023( sim.options = deepcopy(us_sim_options_cps_dataset) sim._set_data(us_sim_options_cps_dataset.data) - assert mock_dataset.from_file.called_with( - us_sim_options_cps_dataset.data, time_period=2023 - ) - class TestSetDataTimePeriod: def test__given_dataset_with_time_period__sets_time_period(self): from policyengine import Simulation