diff --git a/environment.yml b/environment.yml index 58600bd..c72202d 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,7 @@ channels: - conda-forge dependencies: - python -- "taxcalc>=3.2.0" +- "taxcalc>=4.0.0" - pandas - numpy - scipy diff --git a/examples/Simulate_all_policies.ipynb b/examples/Simulate_all_policies.ipynb new file mode 100644 index 0000000..4c9690a --- /dev/null +++ b/examples/Simulate_all_policies.ipynb @@ -0,0 +1,726 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Simulate all policies\n", + "This notebook simulated all candidates policies from 2012-2024." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# imports\n", + "import iot\n", + "from iot import iot_user\n", + "import numpy as np\n", + "import pandas as pd\n", + "import json\n", + "import plotly.express as px\n", + "import os\n", + "from plotly.colors import n_colors\n", + "from scipy.interpolate import UnivariateSpline\n", + "import plotly.graph_objects as go\n", + "\n", + "# set plotly template\n", + "template = \"plotly_white\"\n", + "COLOR_SEQUENCE = [\"red\", \"blue\"] # Republican, Democrat\n", + "dash_sequence = ['dash', 'dot', 'dashdot', \"solid\"] # 2012, 2016, 2020\n", + "\n", + "# Create path for plots to be saved to\n", + "CUR_DIR = os.getcwd()\n", + "path = os.path.join(CUR_DIR, \"plots\")\n", + "if not os.path.exists(path):\n", + " os.makedirs(path)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Read in candidate platform JSON files\n", + "obama2015_path = \"https://raw.githubusercontent.com/jdebacker/examples/pres_proposals/psl_examples/taxcalc/Obama2015.json\"\n", + "romney2012_path = \"https://raw.githubusercontent.com/jdebacker/examples/pres_proposals/psl_examples/taxcalc/Romney2012.json\"\n", + "clinton2016_path = \"https://raw.githubusercontent.com/jdebacker/examples/pres_proposals/psl_examples/taxcalc/Clinton2016.json\"\n", + "trump2016_path = \"https://raw.githubusercontent.com/PSLmodels/examples/main/psl_examples/taxcalc/Trump2016.json\"\n", + "biden2020_path = \"https://raw.githubusercontent.com/PSLmodels/examples/main/psl_examples/taxcalc/Biden2020.json\"\n", + "trump2020_path = \"https://raw.githubusercontent.com/PSLmodels/examples/main/psl_examples/taxcalc/TCJA.json\"\n", + "harris2024_path = \"https://raw.githubusercontent.com/PSLmodels/examples/main/psl_examples/taxcalc/Harris2024.json\"\n", + "trump2024_path = \"https://raw.githubusercontent.com/PSLmodels/examples/main/psl_examples/taxcalc/Trump2024.json\"\n", + "\n", + "pre_2020_baseline = \"https://raw.githubusercontent.com/PSLmodels/examples/main/psl_examples/taxcalc/2017_law.json\"\n", + "baseline_2020 = \"https://raw.githubusercontent.com/PSLmodels/examples/main/psl_examples/taxcalc/TCJA.json\"\n", + "\n", + "\n", + "candidate_dict = {\n", + " \"Obama 2015\": {\"policy_path\": obama2015_path, \"baseline_path\": [pre_2020_baseline], \"start_year\": 2016},\n", + " \"Romney 2012\": {\"policy_path\": romney2012_path, \"baseline_path\": [pre_2020_baseline], \"start_year\": 2014}, #wanted to do 13, but taxcalc with CPS only goes to 13\n", + " \"Clinton 2016\": {\"policy_path\": clinton2016_path, \"baseline_path\": [pre_2020_baseline], \"start_year\": 2017},\n", + " \"Trump 2016\": {\"policy_path\": trump2016_path, \"baseline_path\": [pre_2020_baseline], \"start_year\": 2017},\n", + " \"Biden 2020\": {\"policy_path\": biden2020_path, \"baseline_path\": [pre_2020_baseline, baseline_2020], \"start_year\": 2021},\n", + " \"Trump 2020\": {\"policy_path\": trump2020_path, \"baseline_path\": [pre_2020_baseline], \"start_year\": 2021},\n", + " \"Harris 2024\": {\"policy_path\": trump2020_path, \"baseline_path\": [pre_2020_baseline], \"start_year\": 2025},\n", + " \"Trump 2024\": {\"policy_path\": trump2020_path, \"baseline_path\": [pre_2020_baseline], \"start_year\": 2025}}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create IOT objects for each candidate platform\n", + "policies = []\n", + "baseline_policies = []\n", + "labels = list(candidate_dict.keys())\n", + "# get years from start_year in candidate_dict\n", + "years = [v[\"start_year\"] for v in candidate_dict.values()]\n", + "for k, v in candidate_dict.items():\n", + " # with open(v[\"policy_path\"], \"r\") as file:\n", + " # json1 = file.read()\n", + " json1 = v[\"policy_path\"]#json.load(open(v[\"policy_path\"]))\n", + " print(json1)\n", + " policies.append(json1)\n", + " if v[\"baseline_path\"] is None:\n", + " json2 = {}\n", + " else:\n", + " for ii, vv in enumerate(v[\"baseline_path\"]):\n", + " list_json = []\n", + " # with open(vv, \"r\") as file:\n", + " # json2 = file.read()\n", + " # list_json.append(json2)\n", + " json2 = v[\"baseline_path\"]#open(v[\"baseline_path\"])\n", + " print(json2)\n", + " baseline_policies.append(list_json)\n", + "\n", + "iot_all = iot_user.iot_comparison(\n", + " policies=policies,\n", + " baseline_policies=baseline_policies,\n", + " mtr_smoother=\"kreg\", # this is kreg or HSV\n", + " labels=labels,\n", + " years=years,\n", + " data=\"CPS\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Plots of f(z) for each year/candidate\n", + "fplot = iot_all.plot(var=\"f\")\n", + "fplot.update_layout(\n", + " template=template,\n", + ")\n", + "fplot.write_image(\n", + " os.path.join(path, \"income_dist.png\")\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Plots of theta(z) for each year/candidate\n", + "theta_plot = iot_all.plot(var=\"theta_z\")\n", + "theta_plot.update_layout(\n", + " template=template,\n", + ")\n", + "theta_plot.write_image(\n", + " os.path.join(path, \"theta.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Plots of MTRs for each year/candidate\n", + "mtr_plot = iot_all.plot(var=\"mtr\")\n", + "mtr_plot.update_layout(\n", + " template=template,\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"blue\"),\n", + " selector=dict(name=\"Obama 2015\")\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"red\"),\n", + " selector=dict(name=\"Romney 2012\")\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"blue\"),\n", + " selector=dict(name=\"Clinton 2016\")\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2016\")\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"blue\"),\n", + " selector=dict(name=\"Biden 2020\")\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2020\")\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"blue\"),\n", + " selector=dict(name=\"Harris 2024\")\n", + ")\n", + "mtr_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2024\")\n", + ")\n", + "mtr_plot.write_image(\n", + " os.path.join(path, \"mtrs_all.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# plots of g(z) for each year/candidate\n", + "gz_plot = iot_all.plot(var=\"g_z\")\n", + "gz_plot.update_layout(\n", + " template=template,\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"blue\"),\n", + " selector=dict(name=\"Obama 2015\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"red\"),\n", + " selector=dict(name=\"Romney 2012\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"blue\"),\n", + " selector=dict(name=\"Clinton 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"blue\"),\n", + " selector=dict(name=\"Biden 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"blue\"),\n", + " selector=dict(name=\"Harris 2024\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2024\")\n", + ")\n", + "# gz_plot.update_xaxes(range=[0, 850000])\n", + "\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_all.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# plots of g(z) for each year/candidate, numerical approach\n", + "gz_plot = iot_all.plot(var=\"g_z_numerical\")\n", + "gz_plot.update_layout(\n", + " template=template,\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"blue\"),\n", + " selector=dict(name=\"Obama 2015\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"red\"),\n", + " selector=dict(name=\"Romney 2012\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"blue\"),\n", + " selector=dict(name=\"Clinton 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"blue\"),\n", + " selector=dict(name=\"Biden 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"blue\"),\n", + " selector=dict(name=\"Harris 2024\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2024\")\n", + ")\n", + "# gz_plot.update_xaxes(range=[0, 850000])\n", + "\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_all.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# plots of g(z) for each year/candidate, numerical approach,\n", + "# HIGHlighting the Democrats\n", + "gz_plot = iot_all.plot(var=\"g_z_numerical\")\n", + "gz_plot.update_layout(\n", + " template=template,\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"blue\"),\n", + " selector=dict(name=\"Obama 2015\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"gray\"),\n", + " selector=dict(name=\"Romney 2012\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"blue\"),\n", + " selector=dict(name=\"Clinton 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"gray\"),\n", + " selector=dict(name=\"Trump 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"blue\"),\n", + " selector=dict(name=\"Biden 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"gray\"),\n", + " selector=dict(name=\"Trump 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"blue\"),\n", + " selector=dict(name=\"Harris 2024\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"gray\"),\n", + " selector=dict(name=\"Trump 2024\")\n", + ")\n", + "# gz_plot.update_xaxes(range=[0, 850000])\n", + "\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_democrats.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# plots of g(z) for each year/candidate, numerical approach\n", + "# HIGHlighting the Republicans\n", + "gz_plot = iot_all.plot(var=\"g_z_numerical\")\n", + "gz_plot.update_layout(\n", + " template=template,\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"gray\"),\n", + " selector=dict(name=\"Obama 2015\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"red\"),\n", + " selector=dict(name=\"Romney 2012\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"gray\"),\n", + " selector=dict(name=\"Clinton 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"gray\"),\n", + " selector=dict(name=\"Biden 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"gray\"),\n", + " selector=dict(name=\"Harris 2024\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2024\")\n", + ")\n", + "# gz_plot.update_xaxes(range=[0, 850000])\n", + "\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_republicans.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# Show how MTRs vs tax base elasticity affecting g(z) for 2 candidates (separate plots,\n", + "# which will be put in a 2 panel figure)\n", + "fig = iot_all.JJZFig4(policy='Biden 2020')\n", + "fig.update_layout(\n", + " template=template,\n", + ")\n", + "# fig.update_xaxes(range=[0, 850000])\n", + "\n", + "fig.write_image(\n", + " os.path.join(path, \"composition_Biden2020_gz.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Loop over values of epsilon and plot Biden under these alternative values\"\n", + "eps_values = [0.2, 0.3, 0.4, 0.5, 0.6]\n", + "biden_eps_dict = {}\n", + "biden_eps_dict_numerical = {}\n", + "label_list = []\n", + "for i, v in enumerate(eps_values):\n", + " label = r\"$\\varepsilon$ = \" + str(v)\n", + " iot_b = iot_user.iot_comparison(\n", + " policies=[candidate_dict[\"Biden 2020\"][\"policy_path\"]],\n", + " baseline_policies=[candidate_dict[\"Biden 2020\"][\"baseline_path\"]],\n", + " labels=[label],\n", + " years=[candidate_dict[\"Biden 2020\"][\"start_year\"]],\n", + " eti=v,\n", + " data=\"CPS\"\n", + " )\n", + " label_list.append(label)\n", + " biden_eps_dict[v] = iot_b.iot[0].df().g_z\n", + " biden_eps_dict_numerical[v] = iot_b.iot[0].df().g_z_numerical" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# plot each g_z\n", + "label_dict = {}\n", + "redVSblue = n_colors('rgb(0, 0, 255)', 'rgb(255, 0, 0)', len(label_list), colortype = 'rgb')\n", + "for i, v in enumerate(label_list):\n", + " label_dict[\"wide_variable_\" + str(i)] = str(eps_values[i])#v\n", + "fig = px.line(\n", + " x=iot_b.iot[0].df().z,\n", + " y=[\n", + " biden_eps_dict[0.2],\n", + " biden_eps_dict[0.3],\n", + " biden_eps_dict[0.4],\n", + " biden_eps_dict[0.5],\n", + " biden_eps_dict[0.6]\n", + " ],\n", + " color_discrete_sequence=redVSblue,\n", + " labels=label_dict)\n", + "fig.for_each_trace(lambda t: t.update(name = label_dict[t.name], legendgroup = label_dict[t.name],\n", + " hovertemplate = t.hovertemplate.replace(t.name, label_dict[t.name])))\n", + "fig.update_layout(\n", + " template=template,\n", + " xaxis_title=\"Wages and Salaries\",\n", + " yaxis_title=r\"$g_z$\",\n", + " legend=dict(\n", + " title=\"ETI value:\",\n", + " ),\n", + ")\n", + "\n", + "# fig.update_xaxes(range=[0, 850000])\n", + "fig.write_image(\n", + " os.path.join(path, \"vary_ETI_Biden2020_gz.png\"),\n", + " scale=4\n", + " )\n", + "\n", + "# plot each g_z_numerical\n", + "label_dict = {}\n", + "redVSblue = n_colors('rgb(0, 0, 255)', 'rgb(255, 0, 0)', len(label_list), colortype = 'rgb')\n", + "for i, v in enumerate(label_list):\n", + " label_dict[\"wide_variable_\" + str(i)] = str(eps_values[i])#v\n", + "fig = px.line(\n", + " x=iot_b.iot[0].df().z[50:],\n", + " y=[\n", + " biden_eps_dict_numerical[0.2][50:],\n", + " biden_eps_dict_numerical[0.3][50:],\n", + " biden_eps_dict_numerical[0.4][50:],\n", + " biden_eps_dict_numerical[0.5][50:],\n", + " biden_eps_dict_numerical[0.6][50:]\n", + " ],\n", + " color_discrete_sequence=redVSblue,\n", + " labels=label_dict)\n", + "fig.for_each_trace(lambda t: t.update(name = label_dict[t.name], legendgroup = label_dict[t.name],\n", + " hovertemplate = t.hovertemplate.replace(t.name, label_dict[t.name])))\n", + "fig.update_layout(\n", + " template=template,\n", + " xaxis_title=\"Wages and Salaries\",\n", + " yaxis_title=r\"$g_z$\",\n", + " legend=dict(\n", + " title=\"ETI value:\",\n", + " ),\n", + ")\n", + "\n", + "# fig.update_xaxes(range=[0, 850000])\n", + "fig.write_image(\n", + " os.path.join(path, \"vary_ETI_Biden2020_gz_numerical.png\"),\n", + " scale=4\n", + " )\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# Redo above with varying epsilon(z) according to some empirical studies\n", + "# Required modification of model\n", + "eti_dict = {\n", + " \"eti_values\": [0.18, 0.106, 0.567, 1.83, 1.9],\n", + " \"knot_points\": [30000, 75000, 250000, 2000000, 10000000]\n", + "}\n", + "iot_all_vary = iot_user.iot_comparison(\n", + " policies=policies,\n", + " baseline_policies=baseline_policies,\n", + " labels=labels,\n", + " years=years,\n", + " eti=eti_dict,\n", + " data=\"CPS\"\n", + ")\n", + "# plots of g(z) for each year/candidate, numerical approach\n", + "gz_plot = iot_all_vary.plot(var=\"g_z_numerical\")\n", + "gz_plot.update_layout(\n", + " template=template,\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"blue\"),\n", + " selector=dict(name=\"Obama 2015\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dot\", color=\"red\"),\n", + " selector=dict(name=\"Romney 2012\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"blue\"),\n", + " selector=dict(name=\"Clinton 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dash\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2016\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"blue\"),\n", + " selector=dict(name=\"Biden 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"dashdot\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2020\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"blue\"),\n", + " selector=dict(name=\"Harris 2024\")\n", + ")\n", + "gz_plot.update_traces(\n", + " line=dict(dash=\"solid\", color=\"red\"),\n", + " selector=dict(name=\"Trump 2024\")\n", + ")\n", + "# gz_plot.update_xaxes(range=[0, 850000])\n", + "\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_all_vary_eti.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "# Plot how ETI varies with income\n", + "z_line = np.linspace(1, 1000000, 100000)\n", + "eti_dict = {\n", + " \"eti_values\": [0.18, 0.106, 0.567, 1.83, 1.9],\n", + " \"knot_points\": [30000, 75000, 250000, 2000000, 10000000]\n", + "}\n", + "eti_spl = UnivariateSpline(\n", + " eti_dict[\"knot_points\"], eti_dict[\"eti_values\"], k=3, s=0\n", + ")\n", + "eti = eti_spl(z_line)\n", + "fig = px.line(x=z_line, y=eti, labels={\"x\": \"Wages and Salaries\", \"y\": r\"$\\varepsilon$\"})\n", + "# add special markers without hoverinfo\n", + "fig.add_traces(\n", + " go.Scatter(\n", + " x=eti_dict[\"knot_points\"][:-2], y=eti_dict[\"eti_values\"][:-2], mode=\"markers\", name=\"Gruber and Saez (2022)\", hoverinfo=\"skip\"\n", + " )\n", + ")\n", + "# put legend at bottom\n", + "fig.update_layout(legend=dict(yanchor=\"bottom\", y=0.7, xanchor=\"left\", x=0.1))\n", + "fig.update_layout(\n", + " template=template,\n", + ")\n", + "fig.write_image(\n", + " os.path.join(path, \"ETI_spline.png\"),\n", + " scale=4,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# Do experiment where hold constant g(z) - pick a candidate as baseline - then plot epsilon(z)\n", + "# that would recover those g(z) given the tax rates of each candidate\n", + "# one plot with epsilon(z) for each candidate\n", + "# Will pick Trump and Clinton (2016) for example\n", + "\n", + "# First, plot just their g(z)\n", + "fig = px.line(\n", + " x=iot_all.iot[2].df().z[10:],\n", + " y=[iot_all.iot[2].df().g_z_numerical[10:], iot_all.iot[3].df().g_z_numerical[10:]],\n", + " labels={\"x\": \"Wages and Salaries\", \"y\": r\"$g_z$\"},\n", + " )\n", + "fig.update_layout(\n", + " template=template,\n", + " legend=dict(\n", + " title=\"Candidate:\",\n", + " ),\n", + ")\n", + "candidate_name = [\"Clinton 2016\", \"Trump 2016\"]\n", + "label_dict = {}\n", + "for i, v in enumerate(candidate_name):\n", + " label_dict[\"wide_variable_\" + str(i)] = str(candidate_name[i])\n", + "fig.for_each_trace(lambda t: t.update(name = label_dict[t.name], legendgroup = label_dict[t.name],\n", + " hovertemplate = t.hovertemplate.replace(t.name, label_dict[t.name])))\n", + "\n", + "fig.write_image(\n", + " os.path.join(path, \"trump_clinton_g_z_numerical.png\"),\n", + " scale=4\n", + " )\n", + "# Now find the epsilon(z) that would give Trump's policies the same g(z) as Clinton\n", + "eti_beliefs_lw, eti_beliefs_jjz = iot.inverse_optimal_tax.find_eti(iot_all.iot[2], iot_all.iot[3], g_z_type=\"g_z\")\n", + "idx = np.where(np.absolute(eti_beliefs_jjz[1:]) < 10)[0]\n", + "fig2 = px.line(\n", + " x=iot_all.iot[2].df().z[idx],\n", + " y=eti_beliefs_jjz[idx],\n", + " labels={\"x\": \"Wages and Salaries\", \"y\": r\"$\\text{Implied } \\varepsilon$\"},\n", + " )\n", + "fig2.update_layout(\n", + " template=template,\n", + ")\n", + "fig2.write_image(\n", + " os.path.join(path, \"trump_eti.png\"),\n", + " scale=4\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "eti_dict = {\n", + " \"eti_values\": [0.18, 0.106, 0.567, 1.83, 1.9],\n", + " \"knot_points\": [30000, 75000, 250000, 2000000, 10000000]\n", + "}\n", + "# ETI values from Gruber and Saez (2002) (Table 3) and Saez (2004) (Tables 2, 4, 5)\n", + "# Compute MTR schedule under current law\n", + "iot_2023 = iot_user.iot_comparison(\n", + " policies=[{}],\n", + " baseline_policies=[None],\n", + " labels=[\"2023 Law\"],\n", + " years=[2023],\n", + " data=\"CPS\",\n", + " eti=eti_dict\n", + " )\n", + "fig = px.line(\n", + " x=iot_2023.iot[0].df().z,\n", + " y=iot_2023.iot[0].df().mtr\n", + " )\n", + "fig.update_layout(\n", + " template=template,\n", + " xaxis_title=\"Wages and Salaries\",\n", + " yaxis_title=r\"$T'(z)$\",\n", + ")\n", + "fig.write_image(\n", + " os.path.join(path, \"MTR_2023.png\"),\n", + " scale=4\n", + " )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "iot-dev", + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/iot/inverse_optimal_tax.py b/iot/inverse_optimal_tax.py index 6d7d43e..6584180 100644 --- a/iot/inverse_optimal_tax.py +++ b/iot/inverse_optimal_tax.py @@ -150,27 +150,40 @@ def compute_mtr_dist( binned_data[income_measure].dropna(), var_type="c", reg_type="ll", + bw=[mtr_smooth_param * 40_000], ) mtr, _ = mtr_function.fit(self.z) mtr_prime = np.gradient(mtr, self.z, edge_order=2) elif mtr_smoother == "HSV": # estimate the HSV function on mtrs via weighted least squares + # DATA CLEANING + # drop rows with missing or inf mtr + data = data[~data["mtr"].isna()] + data = data[~data["mtr"].isin([np.inf, -np.inf])] + # drop if MTR > 100% + data = data[data["mtr"] < 1] + # drop rows with missing, inf, or zero income + data = data[data[income_measure] > 0] + # drop rows with missing, inf or negative weights + data = data[~data[weight_var].isna()] + data = data[~data[weight_var].isin([np.inf, -np.inf])] + data = data[data[weight_var] > 0] + # ESTIMATION X = np.log(data[income_measure].values) X = np.column_stack((np.ones(len(X)), X)) w = np.array(data[weight_var].values) w_sqrt = np.sqrt(w) - y = np.log(1-data["mtr"].values) + y = np.log(1 - data["mtr"].values) X_weighted = X * w_sqrt[:, np.newaxis] y_weighted = y * w_sqrt coef, _, _, _ = lstsq(X_weighted, y_weighted) tau = -coef[1] - lambda_param = np.exp(coef[0]) / (1-tau) - mtr = 1 - lambda_param * (1-tau) * self.z ** (-tau) - mtr_prime = lambda_param * tau * (1-tau) * self.z ** (-tau - 1) + lambda_param = np.exp(coef[0]) / (1 - tau) + mtr = 1 - lambda_param * (1 - tau) * self.z ** (-tau) + mtr_prime = lambda_param * tau * (1 - tau) * self.z ** (-tau - 1) else: print("Please enter a value mtr_smoother method") assert False - return mtr, mtr_prime @@ -368,7 +381,7 @@ def sw_weights(self): g_z_numerical = -(1 / self.f) * d_dz_bracket integral = np.trapz(g_z_numerical * self.f, self.z) g_z_numerical = g_z_numerical / integral - + return g_z, g_z_numerical