"
]
},
- "execution_count": 22,
+ "execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "from policyengine_uk import Microsimulation\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "import plotly.express as px\n",
- "import plotly.graph_objects as go\n",
- "from scipy.optimize import bisect\n",
- "from policyengine_core.charts import format_fig\n",
- "from policyengine_core.model_api import *\n",
- "import pandas as pd\n",
- "# Silence warnings\n",
- "import warnings\n",
- "warnings.filterwarnings(\"ignore\")\n",
- "import plotly.io as pio\n",
- "pio.renderers.default = \"notebook\"\n",
- "\n",
- "def filter_to_ni(sim: Microsimulation):\n",
- " weights = sim.calculate(\"household_weight\")\n",
- " country = sim.calculate(\"country\")\n",
- " sim.set_input(\"household_weight\", 2023, np.where(country == \"NORTHERN_IRELAND\", weights, 0))\n",
- "\n",
- "baseline = Microsimulation()\n",
- "filter_to_ni(baseline)\n",
- "\n",
- "\n",
- "def get_ubi_cost(\n",
- " adult_amount: float = 400 * 12,\n",
- " child_amount: float = 200 * 12,\n",
- " senior_amount: float = 100 * 12,\n",
- "):\n",
- " in_ni = baseline.calculate(\"country\", map_to=\"person\") == \"NORTHERN_IRELAND\"\n",
- " age = baseline.calculate(\"age\")\n",
- " retired = baseline.calculate(\"is_SP_age\")\n",
- " num_adults = ((age >= 16) * (~retired))[in_ni].sum()\n",
- " num_children = (age < 16)[in_ni].sum()\n",
- " num_seniors = (retired)[in_ni].sum()\n",
- " return adult_amount * num_adults + child_amount * num_children + senior_amount * num_seniors\n",
- "\n",
- "from policyengine_uk.model_api import *\n",
- "\n",
- "def ubi_ni_reform(\n",
- " personal_allowance: float = 12_570,\n",
- " higher_rate_threshold: float = 50_270,\n",
- " basic_rate: float = 0.2,\n",
- " higher_rate: float = 0.4,\n",
- " additional_rate: float = 0.45,\n",
- " child_bi: float = 0,\n",
- " adult_bi: float = 0,\n",
- " senior_bi: float = 0,\n",
- "\n",
- ") -> Reform:\n",
- " TIME_PERIOD = \"year:2023:10\"\n",
- " return Reform.from_dict({\n",
- " \"gov.hmrc.income_tax.rates.uk[0].rate\": { TIME_PERIOD: basic_rate },\n",
- " \"gov.hmrc.income_tax.rates.uk[1].rate\": { TIME_PERIOD: higher_rate },\n",
- " \"gov.hmrc.income_tax.rates.uk[2].rate\": { TIME_PERIOD: additional_rate },\n",
- " \"gov.hmrc.income_tax.rates.uk[1].threshold\": { TIME_PERIOD: float(higher_rate_threshold) },\n",
- " \"gov.hmrc.income_tax.allowances.personal_allowance.amount\": { TIME_PERIOD: float(personal_allowance) },\n",
- " \"gov.contrib.ubi_center.basic_income.amount.by_age.child\": { TIME_PERIOD: child_bi / 52 },\n",
- " # \"gov.contrib.ubi_center.basic_income.amount.child_min_age\": { TIME_PERIOD: 16 },\n",
- " \"gov.contrib.ubi_center.basic_income.amount.by_age.working_age\": { TIME_PERIOD: adult_bi / 52 },\n",
- " \"gov.contrib.ubi_center.basic_income.amount.by_age.senior\": { TIME_PERIOD: senior_bi / 52 },\n",
- " }, country_id=\"uk\")\n",
- "\n",
- "def get_revenue_slow_estimate(\n",
- " personal_allowance: float,\n",
- " basic_rate_addition: float,\n",
- " higher_rate_addition: float,\n",
- " additional_rate_addition: float,\n",
- ") -> float:\n",
- " baseline = Microsimulation()\n",
- " reformed = Microsimulation(\n",
- " reform=ubi_ni_reform(\n",
- " personal_allowance=personal_allowance,\n",
- " higher_rate_threshold = 50_270 + 12_570 - personal_allowance,\n",
- " basic_rate=0.2 + basic_rate_addition,\n",
- " higher_rate=0.4 + higher_rate_addition,\n",
- " additional_rate=0.45 + additional_rate_addition,\n",
- " child_bi=0,\n",
- " adult_bi=0,\n",
- " senior_bi=0,\n",
- " )\n",
- " )\n",
- " filter_to_ni(baseline)\n",
- " filter_to_ni(reformed)\n",
- " return baseline.calculate(\"household_net_income\").sum() - reformed.calculate(\"household_net_income\").sum()\n",
- "\n",
- "def get_revenue_quick_estimate(\n",
- " personal_allowance: float,\n",
- " basic_rate_addition: float,\n",
- " higher_rate_addition: float,\n",
- " additional_rate_addition: float,\n",
- ") -> float:\n",
- " income = baseline.calc(\"adjusted_net_income\")\n",
- " weight = income.weights\n",
- " add_rate_income = np.maximum(0, income - 125_140)\n",
- " higher_rate_income = np.maximum(0, income - 50_270) - add_rate_income\n",
- " basic_rate_income_b = np.maximum(0, income - higher_rate_income - add_rate_income - 12_570)\n",
- " basic_rate_income_r = np.maximum(0, income - higher_rate_income - add_rate_income - personal_allowance)\n",
- " basic_rate_increased_tax = (\n",
- " basic_rate_income_r * (0.2 + basic_rate_addition)\n",
- " - basic_rate_income_b * 0.2\n",
- " )\n",
- " higher_rate_increased_tax = baseline.calculate(\"higher_rate_earned_income\").values * higher_rate_addition\n",
- " additional_rate_increased_tax = baseline.calculate(\"add_rate_earned_income\").values * additional_rate_addition\n",
- " increased_tax = basic_rate_increased_tax + higher_rate_increased_tax + additional_rate_increased_tax\n",
- " return (increased_tax * weight).sum() * 0.91 # Rough benefit response ratio\n",
- "\n",
- "def solve_rates(\n",
- " adult_ubi: float = 0,\n",
- " personal_allowance: float = 12_570,\n",
- " higher_rate_ratio: float = 1,\n",
- " additional_rate_ratio: float = 1,\n",
- "):\n",
- " def adjusted_reform_cost(\n",
- " basic_rate_addition: float = 0,\n",
- " ) -> float:\n",
- " adjusted_revenue = get_revenue_quick_estimate(personal_allowance, basic_rate_addition, basic_rate_addition * higher_rate_ratio, basic_rate_addition * additional_rate_ratio)\n",
- " ubi_cost = get_ubi_cost(\n",
- " adult_ubi, \n",
- " adult_ubi * 0.5,\n",
- " adult_ubi\n",
- " )\n",
- " return ubi_cost * 0.75 - adjusted_revenue # Assume 25% of the UBI is paid for by Westminster\n",
- " \n",
- " return bisect(adjusted_reform_cost, -1, 1, xtol=1e-3)\n",
- "\n",
+ "combined_table = pd.concat([\n",
+ " summary_table,\n",
+ " child_summary_table,\n",
+ "]).T\n",
"\n",
- "headline_ubi_rates = []\n",
- "personal_allowances = []\n",
- "basic_rate_additions = []\n",
- "higher_add_addition_ratios = []\n",
+ "# Intersperse the columns (1, 4, 2, 5, 3, 6)\n",
"\n",
- "for pa in [12_570] + list(range(12_000, -1000, -1000)):\n",
- " for ubi_rate in list([200 * 12, 300 * 12, 400 * 12]):\n",
- " for higher_add_ratio in list((0, 1, 2)):\n",
- " headline_ubi_rates.append(ubi_rate)\n",
- " personal_allowances.append(pa)\n",
- " basic_rate_additions.append(solve_rates(adult_ubi=ubi_rate, personal_allowance=pa, higher_rate_ratio=higher_add_ratio, additional_rate_ratio=higher_add_ratio))\n",
- " higher_add_addition_ratios.append(higher_add_ratio)\n",
+ "combined_table = combined_table.iloc[:, [0, 3, 1, 4, 2, 5]]\n",
"\n",
+ "# Rename index (Model 1, Model 1(C), Model 2, Model 2(C), Model 3, Model 3(C))\n",
"\n",
- "df = pd.DataFrame({\n",
- " \"Adult UBI\": headline_ubi_rates,\n",
- " \"Personal Allowance\": personal_allowances,\n",
- " \"Basic rate addition\": basic_rate_additions,\n",
- " \"Higher/additional rate increase ratio\": higher_add_addition_ratios,\n",
- "})\n",
- "df[\"Personal Allowance reduction\"] = 12_570 - df[\"Personal Allowance\"]\n",
- "df[\"Higher rate addition\"] = df[\"Basic rate addition\"] * df[\"Higher/additional rate increase ratio\"]\n",
- "df[\"Basic rate addition (x100)\"] = df[\"Basic rate addition\"] * 100\n",
- "df[\"Additional rate addition\"] = df[\"Basic rate addition\"] * df[\"Higher/additional rate increase ratio\"]\n",
- "df[\"UBI/Increase ratio combination\"] = [\n",
- " f\"{higher_add_ratio:.0%}\" + \" \" * (ubi // 100)\n",
- " for ubi, higher_add_ratio in zip(\n",
- " df[\"Adult UBI\"],\n",
- " df[\"Higher/additional rate increase ratio\"],\n",
- " )\n",
+ "combined_table.columns = [\n",
+ " \"Model 1\",\n",
+ " \"Model 1c\",\n",
+ " \"Model 2\",\n",
+ " \"Model 2c\",\n",
+ " \"Model 3\",\n",
+ " \"Model 3c\",\n",
"]\n",
- "df[\"Label\"] = [\n",
- " f\"A £{ubi:,.0f} UBI and £{pa:,.0f} Personal Allowance can be made
75% budget neutral by setting the basic, higher and additional rates
to {0.2 + basic_rate_addition:.1%}, {0.4 + higher_rate_addition:.1%} and {0.45 + additional_rate_addition:.1%} respectively.\"\n",
- " for ubi, pa, basic_rate_addition, higher_rate_addition, additional_rate_addition in zip(\n",
- " df[\"Adult UBI\"],\n",
- " df[\"Personal Allowance\"],\n",
- " df[\"Basic rate addition\"],\n",
- " df[\"Higher rate addition\"],\n",
- " df[\"Additional rate addition\"],\n",
- " )\n",
- "]\n",
- "\n",
- "df = df.sort_values([\"Adult UBI\", \"Higher/additional rate increase ratio\"])\n",
- "\n",
- "df[\"Basic rate\"] = df[\"Basic rate addition\"] + 0.2\n",
- "df[\"Higher rate\"] = df[\"Higher rate addition\"] + 0.4\n",
- "df[\"Add. rate\"] = df[\"Additional rate addition\"] + 0.45\n",
- "\n",
- "df[[\n",
- " \"Adult UBI\",\n",
- " \"Personal Allowance\",\n",
- " \"Basic rate addition\",\n",
- " \"Higher/additional rate increase ratio\",\n",
- "]].to_csv(\"table.csv\", index=False)\n",
- "df[\"Feasible MTR\"] = np.where(\n",
- " # £150k+ MTR under 100%\n",
- " (df[\"Add. rate\"] + 0.0325 < 1)\n",
- " # £100k-150k MTR under 100%\n",
- " & (df[\"Higher rate\"] * (\n",
- " np.where(df[\"Personal Allowance\"] > 0, 1.5, 1)\n",
- " ) + 0.0325 < 1),\n",
- " \"Yes\",\n",
- " \"No\",\n",
- " )\n",
- "\n",
- "df.to_csv(\"experiment_results.csv\")\n",
- "\n",
- "pa = df[\"Personal Allowance\"]\n",
- "ubi = df[\"Adult UBI\"]\n",
- "ratio = df[\"Higher/additional rate increase ratio\"]\n",
- "model_1_row = df[(pa == 2000) & (ubi == 200 * 12) & (ratio == 2)].iloc[0]\n",
- "model_2_row = df[(pa == 0) & (ubi == 300 * 12) & (ratio == 1)].iloc[0]\n",
- "model_3_row = df[(pa == 0) & (ubi == 400 * 12) & (ratio == 0)].iloc[0]\n",
- "\n",
- "model_1_policy = dict(\n",
- " basic_rate=model_1_row[\"Basic rate\"],\n",
- " higher_rate=model_1_row[\"Higher rate\"],\n",
- " additional_rate=model_1_row[\"Add. rate\"],\n",
- " personal_allowance=model_1_row[\"Personal Allowance\"],\n",
- " adult_bi=model_1_row[\"Adult UBI\"],\n",
- " higher_rate_threshold=50_270. - model_1_row[\"Personal Allowance\"],\n",
- " child_bi=model_1_row[\"Adult UBI\"] * 0.5,\n",
- " senior_bi=model_1_row[\"Adult UBI\"],\n",
- ")\n",
- "\n",
- "model_2_policy = dict(\n",
- " basic_rate=model_2_row[\"Basic rate\"],\n",
- " higher_rate=model_2_row[\"Higher rate\"],\n",
- " additional_rate=model_2_row[\"Add. rate\"],\n",
- " personal_allowance=model_2_row[\"Personal Allowance\"],\n",
- " adult_bi=model_2_row[\"Adult UBI\"],\n",
- " higher_rate_threshold=50_270. - model_2_row[\"Personal Allowance\"],\n",
- " child_bi=model_2_row[\"Adult UBI\"] * 0.5,\n",
- " senior_bi=model_2_row[\"Adult UBI\"],\n",
- ")\n",
- "\n",
- "model_3_policy = dict(\n",
- " basic_rate=model_3_row[\"Basic rate\"],\n",
- " higher_rate=model_3_row[\"Higher rate\"],\n",
- " additional_rate=model_3_row[\"Add. rate\"],\n",
- " personal_allowance=model_3_row[\"Personal Allowance\"],\n",
- " adult_bi=model_3_row[\"Adult UBI\"],\n",
- " higher_rate_threshold=50_270. - model_3_row[\"Personal Allowance\"],\n",
- " child_bi=model_3_row[\"Adult UBI\"] * 0.5,\n",
- " senior_bi=model_3_row[\"Adult UBI\"],\n",
- ")\n",
"\n",
- "model_1_reform = ubi_ni_reform(**model_1_policy)\n",
- "model_2_reform = ubi_ni_reform(**model_2_policy)\n",
- "model_3_reform = ubi_ni_reform(**model_3_policy)\n",
- "\n",
- "baseline = Microsimulation()\n",
- "model_1_sim = Microsimulation(reform=model_1_reform)\n",
- "model_2_sim = Microsimulation(reform=model_2_reform)\n",
- "model_3_sim = Microsimulation(reform=model_3_reform)\n",
+ "for i in range(1, 4):\n",
+ " ubi_amount = {\n",
+ " 1: \"£100\",\n",
+ " 2: \"£150\",\n",
+ " 3: \"£200\",\n",
+ " }[i]\n",
+ " combined_table[f\"Model {i}c\"].iloc[2] = ubi_amount\n",
"\n",
- "for simulation in [baseline, model_1_sim, model_2_sim, model_3_sim]:\n",
- " filter_to_ni(simulation)\n",
- "\n",
- "summary_table_original = summary_table.copy()\n",
- "\n",
- "summary_table = pd.DataFrame()\n",
- "summary_table[\"Monthly UBI amount for adults\"] = [model_1_row[\"Adult UBI\"] / 12, model_2_row[\"Adult UBI\"] / 12, model_3_row[\"Adult UBI\"] / 12]\n",
- "summary_table[\"Monthly UBI amount for children\"] = [model_1_row[\"Adult UBI\"] / 12 * 0.5, model_2_row[\"Adult UBI\"] / 12 * 0.5, model_3_row[\"Adult UBI\"] / 12 * 0.5]\n",
- "summary_table[\"Personal Allowance\"] = [model_1_row[\"Personal Allowance\"], model_2_row[\"Personal Allowance\"], model_3_row[\"Personal Allowance\"]]\n",
- "summary_table[\"Basic rate increase\"] = [model_1_row[\"Basic rate addition\"], model_2_row[\"Basic rate addition\"], model_3_row[\"Basic rate addition\"]]\n",
- "summary_table[\"Higher rate increase\"] = [model_1_row[\"Higher rate addition\"], model_2_row[\"Higher rate addition\"], model_3_row[\"Higher rate addition\"]]\n",
- "summary_table[\"Additional rate increase\"] = [model_1_row[\"Additional rate addition\"], model_2_row[\"Additional rate addition\"], model_3_row[\"Additional rate addition\"]]\n",
- "summary_table[\"Net cost\"] = [\n",
- " sim.calculate(\"household_net_income\").sum() - baseline.calculate(\"household_net_income\").sum()\n",
- " for sim in [model_1_sim, model_2_sim, model_3_sim]\n",
- "]\n",
- "summary_table[\"Revenue raised\"] = [\n",
- " sim.calculate(\"income_tax\").sum() - baseline.calculate(\"income_tax\").sum()\n",
- " for sim in [model_1_sim, model_2_sim, model_3_sim]\n",
- "]\n",
- "summary_table[\"Benefit outlay increase\"] = [\n",
- " sim.calculate(\"household_benefits\").sum() - baseline.calculate(\"household_benefits\").sum()\n",
- " for sim in [model_1_sim, model_2_sim, model_3_sim]\n",
- "]\n",
- "summary_table[\"Poverty impact\"] = [\n",
- " sim.calculate(\"in_poverty\", map_to=\"person\").sum() / baseline.calculate(\"in_poverty\", map_to=\"person\").sum() - 1\n",
- " for sim in [model_1_sim, model_2_sim, model_3_sim]\n",
- "]\n",
- "summary_table[\"Inequality impact\"] = [\n",
- " sim.calculate(\"equiv_household_net_income\", map_to=\"person\").gini() / baseline.calculate(\"equiv_household_net_income\", map_to=\"person\").gini() - 1\n",
- " for sim in [model_1_sim, model_2_sim, model_3_sim]\n",
- "]\n",
- "summary_table[\"Percent of population gaining\"] = [\n",
- " (sim.calculate(\"household_net_income\", map_to=\"person\") > baseline.calculate(\"household_net_income\", map_to=\"person\")).mean()\n",
- " for sim in [model_1_sim, model_2_sim, model_3_sim]\n",
- "]\n",
- "\n",
- "summary_table\n",
- "\n",
- "# Formatting\n",
- "\n",
- "summary_table[\"Monthly UBI amount for adults\"] = summary_table[\"Monthly UBI amount for adults\"].map(lambda x: f\"£{x:,.0f}\")\n",
- "summary_table[\"Monthly UBI amount for children\"] = summary_table[\"Monthly UBI amount for children\"].map(lambda x: f\"£{x:,.0f}\")\n",
- "summary_table[\"Personal Allowance\"] = summary_table[\"Personal Allowance\"].map(lambda x: f\"£{x:,.0f}\")\n",
- "summary_table[\"Basic rate increase\"] = summary_table[\"Basic rate increase\"].map(lambda x: f\"{x * 100:.1f}p\")\n",
- "summary_table[\"Higher rate increase\"] = summary_table[\"Higher rate increase\"].map(lambda x: f\"{x * 100:.1f}p\")\n",
- "summary_table[\"Additional rate increase\"] = summary_table[\"Additional rate increase\"].map(lambda x: f\"{x * 100:.1f}p\")\n",
- "summary_table[\"Westminster contribution\"] = summary_table[\"Net cost\"] / summary_table[\"Benefit outlay increase\"]\n",
- "summary_table[\"Westminster contribution\"] = summary_table[\"Westminster contribution\"].map(lambda x: f\"{x:.1%}\")\n",
- "summary_table[\"Net cost\"] = summary_table[\"Net cost\"].map(lambda x: f\"£{x/1e9:,.1f}bn\")\n",
- "summary_table[\"Revenue raised\"] = summary_table[\"Revenue raised\"].map(lambda x: f\"£{x/1e9:,.1f}bn\")\n",
- "summary_table[\"Benefit outlay increase\"] = summary_table[\"Benefit outlay increase\"].map(lambda x: f\"£{x/1e9:,.1f}bn\")\n",
- "summary_table[\"Poverty impact\"] = summary_table[\"Poverty impact\"].map(lambda x: f\"{x * 100:+.1f}%\")\n",
- "summary_table[\"Inequality impact\"] = summary_table[\"Inequality impact\"].map(lambda x: f\"{x * 100:+.1f}%\")\n",
- "summary_table[\"Percent of population gaining\"] = summary_table[\"Percent of population gaining\"].map(lambda x: f\"{x * 100:.0f}%\")\n",
- "\n",
- "policy_ids = [\n",
- " reform.api_id\n",
- " for reform in [model_1_reform, model_2_reform, model_3_reform]\n",
- "]\n",
- "summary_table[\"PolicyEngine link\"] = [\n",
- " f\"#{policy_id}\" for policy_id in policy_ids\n",
- "]\n",
- "summary_table.index = [\"Model 1 (+ children)\", \"Model 2 (+ children)\", \"Model 3 (+ children)\"]\n",
- "\n",
- "from IPython.display import Markdown\n",
- "\n",
- "Markdown(summary_table.T.to_markdown())"
+ "Markdown(combined_table.to_markdown())"
]
}
],
diff --git a/src/redesign/components/BlogPage.jsx b/src/redesign/components/BlogPage.jsx
index 4bd645801..a12ea0e6d 100644
--- a/src/redesign/components/BlogPage.jsx
+++ b/src/redesign/components/BlogPage.jsx
@@ -199,7 +199,7 @@ function NotebookOutputPlotly({ data }) {
const displayCategory = useDisplayCategory();
return (
- <>
+
);
}
@@ -784,7 +784,8 @@ function BlogContent({ markdown, backgroundColor }) {
(a.date < b.date ? 1 : -1));
for (let post of posts) {
|