diff --git a/examples/Simulate_all_policies.ipynb b/examples/Simulate_all_policies.ipynb index 4c9690a..7f868ec 100644 --- a/examples/Simulate_all_policies.ipynb +++ b/examples/Simulate_all_policies.ipynb @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -60,13 +60,16 @@ "\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", + " \"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 14\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}}" + " # \"Harris 2024\": {\"policy_path\": harris2024_path, \"baseline_path\": [pre_2020_baseline, baseline_2020], \"start_year\": 2025},\n", + " # \"Trump 2024\": {\"policy_path\": trump2024_path, \"baseline_path\": [pre_2020_baseline, baseline_2020], \"start_year\": 2025}\n", + " \"Harris 2024\": {\"policy_path\": harris2024_path, \"baseline_path\": None, \"start_year\": 2026}, # Use 2026 for these candidates since largest diff is how treat TCJA expirations\n", + " \"Trump 2024\": {\"policy_path\": trump2024_path, \"baseline_path\": None, \"start_year\": 2026}\n", + " }" ] }, { @@ -111,7 +114,34 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "iot_all.iot[-1].mtr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "iot_all.iot[-2].mtr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(iot_all.iot)" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -127,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -144,7 +174,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -193,7 +223,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -244,7 +274,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -290,12 +320,24 @@ "gz_plot.write_image(\n", " os.path.join(path, \"gz_numerical_all.png\"),\n", " scale=4\n", + " )\n", + "\n", + "# Make the same plot but stop at 200_000\n", + "gz_plot.update_xaxes(range=[0, 200_000])\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_all_200k.png\"),\n", + " scale=4\n", + " )\n", + "gz_plot.update_xaxes(range=[0, 100_000])\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_all_100k.png\"),\n", + " scale=4\n", " )" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -342,12 +384,22 @@ "gz_plot.write_image(\n", " os.path.join(path, \"gz_numerical_democrats.png\"),\n", " scale=4\n", + " )\n", + "gz_plot.update_xaxes(range=[0, 200_000])\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_democrats_200k.png\"),\n", + " scale=4\n", + " )\n", + "gz_plot.update_xaxes(range=[0, 100_000])\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_democrats_100k.png\"),\n", + " scale=4\n", " )" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -394,12 +446,22 @@ "gz_plot.write_image(\n", " os.path.join(path, \"gz_numerical_republicans.png\"),\n", " scale=4\n", + " )\n", + "gz_plot.update_xaxes(range=[0, 200_000])\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_republicans_200k.png\"),\n", + " scale=4\n", + " )\n", + "gz_plot.update_xaxes(range=[0, 100_000])\n", + "gz_plot.write_image(\n", + " os.path.join(path, \"gz_numerical_republicans_100k.png\"),\n", + " scale=4\n", " )" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -419,7 +481,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -445,7 +507,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -518,7 +580,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -583,7 +645,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -617,7 +679,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -638,6 +700,17 @@ " title=\"Candidate:\",\n", " ),\n", ")\n", + "candidate_name = [\"Obama 2012\", \"Romney 2012\"]\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, \"romney_obama_g_z_numerical.png\"),\n", + " scale=4\n", + " )\n", "candidate_name = [\"Clinton 2016\", \"Trump 2016\"]\n", "label_dict = {}\n", "for i, v in enumerate(candidate_name):\n", @@ -649,9 +722,32 @@ " os.path.join(path, \"trump_clinton_g_z_numerical.png\"),\n", " scale=4\n", " )\n", + "candidate_name = [\"Biden 2020\", \"Trump 2020\"]\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_biden_g_z_numerical.png\"),\n", + " scale=4\n", + " )\n", + "candidate_name = [\"Harris 2024\", \"Trump 2024\"]\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_harris_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", + "# idx = (np.where(np.absolute(eti_beliefs_jjz[1:]) < 1))[0]\n", + "idx = (np.where(((iot_all.iot[3].df().g_z.values) > 1.05) | ((iot_all.iot[3].df().g_z.values) < 0.95)))[0]\n", "fig2 = px.line(\n", " x=iot_all.iot[2].df().z[idx],\n", " y=eti_beliefs_jjz[idx],\n", @@ -668,7 +764,16 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(np.where(((iot_all.iot[2].df().z.values) > 1.01) | ((iot_all.iot[2].df().z.values) < 0.99)))[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [], "source": [ diff --git a/iot/inverse_optimal_tax.py b/iot/inverse_optimal_tax.py index a0b1275..dac7055 100644 --- a/iot/inverse_optimal_tax.py +++ b/iot/inverse_optimal_tax.py @@ -76,7 +76,12 @@ def __init__( self.eti = eti_spl(self.z) # compute marginal tax rate schedule self.mtr, self.mtr_prime = self.compute_mtr_dist( - data, weight_var, income_measure, mtr_smoother, mtr_smooth_param, kreg_bw + data, + weight_var, + income_measure, + mtr_smoother, + mtr_smooth_param, + kreg_bw, ) # compute theta_z, the elasticity of the tax base self.theta_z = 1 + ((self.z * self.f_prime) / self.f) @@ -109,7 +114,13 @@ def df(self): return df def compute_mtr_dist( - self, data, weight_var, income_measure, mtr_smoother, mtr_smooth_param, kreg_bw + self, + data, + weight_var, + income_measure, + mtr_smoother, + mtr_smooth_param, + kreg_bw, ): """ Compute marginal tax rates over the income distribution and diff --git a/iot/iot_user.py b/iot/iot_user.py index 1ffa3dc..6d1e836 100644 --- a/iot/iot_user.py +++ b/iot/iot_user.py @@ -63,7 +63,7 @@ def __init__( kde_bw=None, mtr_smoother="kreg", mtr_smooth_param=1000, - kreg_bw=[120_000] + kreg_bw=[120_000], ): self.income_measure = income_measure self.weight_var = weight_var @@ -262,35 +262,26 @@ def JJZFig4(self, policy="Current Law", var="g_z"): showlegend=False, ) ) - # fig.add_trace( - # go.Scatter( - # x=plot_df[self.income_measure], - # y=plot_df["Nonconstant MTRs"], - # fill="tonexty", # fill area between trace1 and trace2 - # # fill="tozeroy", - # mode="lines", - # name="Nonconstant MTRs", - # ) - # ) fig.add_trace( go.Scatter( x=plot_df[self.income_measure], - y=plot_df["Tax Base Elasticity"], - fill="tonexty", # fill area between trace0 and trace1 + y=plot_df["Nonconstant MTRs"], + fill="tonexty", # fill area from prior trace to this one + # fill="tozeroy", mode="lines", - name="Tax Base Elasticity", + name="Nonconstant MTRs", ) ) fig.add_trace( go.Scatter( x=plot_df[self.income_measure], y=plot_df["Overall weight"], - fill="tonexty", + fill="tonexty", # fill area between trace0 and trace1 mode="lines", - name="Nonconstant MTRs", + name="Tax Base Elasticity", ) ) - # add a line at y=0 + # Add black line for overall weight fig.add_trace( go.Scatter( x=plot_df[self.income_measure], @@ -301,18 +292,18 @@ def JJZFig4(self, policy="Current Law", var="g_z"): showlegend=False, ) ) - # add a line at y=0 - fig.add_trace( - go.Scatter( - x=[ - plot_df[self.income_measure].min(), - plot_df[self.income_measure].max(), - ], - y=[0, 0], - mode="lines", - line=dict(color="black", width=1, dash="dash"), - ) - ) + # # add a line at y=0 + # fig.add_trace( + # go.Scatter( + # x=[ + # plot_df[self.income_measure].min(), + # plot_df[self.income_measure].max(), + # ], + # y=[0, 0], + # mode="lines", + # line=dict(color="black", width=1, dash="dash"), + # ) + # ) fig.update_layout( xaxis_title=OUTPUT_LABELS[self.income_measure], yaxis_title=r"$g_z$",