diff --git a/src/posts/articles/winter_fuel_allowance.ipynb b/src/posts/articles/winter_fuel_allowance.ipynb new file mode 100644 index 000000000..a34284825 --- /dev/null +++ b/src/posts/articles/winter_fuel_allowance.ipynb @@ -0,0 +1,518 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "61336949", + "metadata": {}, + "source": [ + "Proposed reforms include:\n", + "\n", + "1. Introduce a transitional period before disenrolling PIP recipients.\n", + "2. Increase the threshold for Pension Credit.\n", + "3. Repeal the two-child limit.\n", + "\n", + "| Policy reform | 2025 | 2026 | 2027 | 2028 | 2029 | 2025-29 |\n", + "|:-----------------------------|-------:|-------:|-------:|-------:|-------:|----------:|\n", + "| PIP reform with 6 months TP | -3 | 0 | 0 | 0 | 0 | -3 |\n", + "| PIP reform with 12 months TP | -5.5 | 0 | 0 | 0 | 0 | -5.5 |\n", + "| PIP reform with 18 months TP | -5.5 | -3 | 0 | 0 | 0 | -8.6 |\n", + "| Raise PC threshold by £1,000 | -1.6 | -1.6 | -1.6 | -1.5 | -1.4 | -7.8 |\n", + "| Raise PC threshold by £2,000 | -3.2 | -3.3 | -3.2 | -3.2 | -3.1 | -16 |\n", + "| Raise PC threshold by £3,000 | -4.9 | -4.9 | -4.9 | -4.8 | -4.7 | -24.1 |\n", + "| Repeal two-child limit | -1.5 | -1.6 | -1.7 | -2 | -2.1 | -8.9 |" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3f17dec4", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/nikhilwoodruff/policyengine/policyengine-app/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "from policyengine import Simulation\n", + "import pandas as pd\n", + "import plotly.express as px\n", + "from policyengine.utils.charts import *\n", + "\n", + "\n", + "def get_threshold_change_reform(\n", + " threshold_change: float,\n", + "):\n", + " return {\n", + " \"gov.dwp.pension_credit.guarantee_credit.minimum_guarantee.SINGLE\": 221.85 + threshold_change / 52,\n", + " \"gov.dwp.pension_credit.guarantee_credit.minimum_guarantee.COUPLE\": 338.61 + threshold_change / 52,\n", + " }\n", + "\n", + "raise_pc_threshold_1k = get_threshold_change_reform(1000)\n", + "raise_pc_threshold_2k = get_threshold_change_reform(2000)\n", + "raise_pc_threshold_3k = get_threshold_change_reform(3000)\n", + "\n", + "two_child_limit_repeal = {\n", + " \"gov.dwp.universal_credit.elements.child.limit.child_count\": 99,\n", + " \"gov.dwp.tax_credits.child_tax_credit.limit.child_count\": 99,\n", + "}\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "da3378ef", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.simplified_google_storage_client:Downloading policyengine-uk-data-private, enhanced_frs_2022_23.h5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n", + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Cache exists and crc is unchanged for policyengine-uk-data-private, enhanced_frs_2022_23.h5.\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n", + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Cache exists and crc is unchanged for policyengine-uk-data-private, enhanced_frs_2022_23.h5.\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n", + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Cache exists and crc is unchanged for policyengine-uk-data-private, enhanced_frs_2022_23.h5.\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n", + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Cache exists and crc is unchanged for policyengine-uk-data-private, enhanced_frs_2022_23.h5.\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n", + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Cache exists and crc is unchanged for policyengine-uk-data-private, enhanced_frs_2022_23.h5.\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n", + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Cache exists and crc is unchanged for policyengine-uk-data-private, enhanced_frs_2022_23.h5.\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n", + "INFO:root:Using Google Cloud Storage for download.\n", + "WARNING:root:No version specified for policyengine-uk-data-private, enhanced_frs_2022_23.h5. Using latest version: 1.11.5\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Syncing policyengine-uk-data-private, enhanced_frs_2022_23.h5, 1.11.5 to cache\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Cache exists and crc is unchanged for policyengine-uk-data-private, enhanced_frs_2022_23.h5.\n", + "INFO:policyengine.utils.data.caching_google_storage_client:Copying downloaded data for policyengine-uk-data-private, enhanced_frs_2022_23.h5 to enhanced_frs_2022_23.h5\n" + ] + } + ], + "source": [ + "def pip_reform(\n", + " transitional_protection_months: int = 0,\n", + ") -> Simulation:\n", + " sim = Simulation(\n", + " country=\"uk\",\n", + " scope=\"macro\",\n", + " )\n", + " baseline = sim.baseline_simulation\n", + " \n", + " has_enhanced_level = (baseline.calculate(\"pip_dl_category\").values == \"ENHANCED\")\n", + "\n", + " data = baseline.to_input_dataframe()\n", + "\n", + " SPLIT = 0.7 # SPLIT% of claimants are in the treatment group\n", + "\n", + " import pandas as pd\n", + "\n", + " data_copy_b = data.copy()\n", + " data_copy_r = data.copy()\n", + "\n", + " for col in data_copy_r.columns:\n", + " if \"_id\" in col:\n", + " data_copy_r[col] = data_copy_r[col].apply(lambda x: 1e9 + x)\n", + " if \"_weight\" in col:\n", + " data_copy_r[col] = data_copy_r[col] * SPLIT\n", + "\n", + " for col in data_copy_b.columns:\n", + " if \"_weight\" in col:\n", + " data_copy_b[col] = data_copy_b[col] * (1 - SPLIT)\n", + "\n", + " combined_data = pd.concat([data_copy_b, data_copy_r])\n", + "\n", + " sim.baseline_simulation = Simulation(\n", + " country=\"uk\",\n", + " scope=\"macro\",\n", + " data=combined_data,\n", + " ).baseline_simulation\n", + " sim.reform_simulation = Simulation(\n", + " country=\"uk\",\n", + " scope=\"macro\",\n", + " data=combined_data,\n", + " ).baseline_simulation\n", + "\n", + " import numpy as np\n", + "\n", + " for year in range(2025, 2030):\n", + " sim.reform_simulation.set_input(\n", + " \"pip_dl\",\n", + " year,\n", + " list(baseline.calculate(\"pip_dl\", year).values) + list(baseline.calculate(\"pip_dl\", year).values * np.where(\n", + " has_enhanced_level, 1, max(min((transitional_protection_months / 12) - (year - 2025), 1), 0)\n", + " ))\n", + " )\n", + " return sim\n", + "\n", + "simulations = [\n", + " Simulation(\n", + " country=\"uk\",\n", + " scope=\"macro\",\n", + " reform=reform\n", + " ) for reform in [\n", + " raise_pc_threshold_1k,\n", + " raise_pc_threshold_2k,\n", + " raise_pc_threshold_3k,\n", + " two_child_limit_repeal,\n", + " ]\n", + "]\n", + "\n", + "for transitional_protection_months in [0, 6, 12, 18]:\n", + " simulations.append(\n", + " pip_reform(transitional_protection_months)\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b50f1b36", + "metadata": {}, + "outputs": [], + "source": [ + "reform_labels = [\n", + " \"Raise PC threshold by £1,000\",\n", + " \"Raise PC threshold by £2,000\",\n", + " \"Raise PC threshold by £3,000\",\n", + " \"Repeal two-child limit\",\n", + " \"PIP reform with no TP\",\n", + " \"PIP reform with 6 months TP\",\n", + " \"PIP reform with 12 months TP\",\n", + " \"PIP reform with 18 months TP\",\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "25e3cfce", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025\n", + "2026\n", + "2027\n", + "2028\n", + "2029\n" + ] + } + ], + "source": [ + "budgetary_impacts = []\n", + "years = []\n", + "\n", + "for year in range(2025, 2030):\n", + " print(year)\n", + " for sim in simulations:\n", + " budgetary_impacts.append(sim.reform_simulation.calculate(\"gov_balance\", year).sum()/1e9 - sim.baseline_simulation.calculate(\"gov_balance\", year).sum()/1e9)\n", + " years.append(year)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "48bcde5b", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.DataFrame({\n", + " \"reform\": reform_labels * 5,\n", + " \"year\": years,\n", + " \"budgetary_impact\": budgetary_impacts,\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "907952d9", + "metadata": {}, + "outputs": [], + "source": [ + "# cols = year, rows=reform, values=budgetary_impact\n", + "df_pivot = df.pivot_table(\n", + " index=\"reform\",\n", + " columns=\"year\",\n", + " values=\"budgetary_impact\",\n", + " aggfunc=\"sum\"\n", + ").reset_index()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "0cb41873", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
| year | \n", + "reform | \n", + "2025 | \n", + "2026 | \n", + "2027 | \n", + "2028 | \n", + "2029 | \n", + "
|---|---|---|---|---|---|---|
| 0 | \n", + "PIP reform with 12 months TP | \n", + "0.0 | \n", + "5.6 | \n", + "5.7 | \n", + "5.9 | \n", + "6.0 | \n", + "
| 1 | \n", + "PIP reform with 18 months TP | \n", + "0.0 | \n", + "2.6 | \n", + "5.7 | \n", + "5.9 | \n", + "6.0 | \n", + "
| 2 | \n", + "PIP reform with 6 months TP | \n", + "2.6 | \n", + "5.6 | \n", + "5.7 | \n", + "5.9 | \n", + "6.0 | \n", + "
| 3 | \n", + "PIP reform with no TP | \n", + "5.5 | \n", + "5.6 | \n", + "5.7 | \n", + "5.9 | \n", + "6.0 | \n", + "
| 4 | \n", + "Raise PC threshold by £1,000 | \n", + "-1.6 | \n", + "-1.5 | \n", + "-1.4 | \n", + "-1.4 | \n", + "-1.3 | \n", + "
| 5 | \n", + "Raise PC threshold by £2,000 | \n", + "-3.1 | \n", + "-3.1 | \n", + "-3.0 | \n", + "-2.9 | \n", + "-2.8 | \n", + "
| 6 | \n", + "Raise PC threshold by £3,000 | \n", + "-4.7 | \n", + "-4.7 | \n", + "-4.6 | \n", + "-4.5 | \n", + "-4.4 | \n", + "
| 7 | \n", + "Repeal two-child limit | \n", + "-1.6 | \n", + "-1.7 | \n", + "-1.8 | \n", + "-2.1 | \n", + "-2.2 | \n", + "