Basic Recipe: Static Analysis of a Simple Reform

Basic Recipe: Static Analysis of a Simple Reform#

This is the recipe you should follow first. Mastering this recipe is a prerequisite for all the other recipes in this cookbook.


import taxcalc as tc
import pandas as pd
from import show, output_notebook


Use publicly-available CPS input file.

NOTE: if you have access to the restricted-use IRS-SOI PUF-based input file and you have that file (named ‘puf.csv’) located in the directory where this script is located, then you can substitute the following statement for the prior statement:

recs = tc.Records()

recs = tc.Records.cps_constructor()

Specify Calculator object for static analysis of current-law policy.

pol = tc.Policy()
calc1 = tc.Calculator(policy=pol, records=recs)

NOTE: calc1 now contains a PRIVATE COPY of pol and a PRIVATE COPY of recs, so we can continue to use pol and recs in this script without any concern about side effects from Calculator method calls on calc1.

CYR = 2020

Calculate aggregate current-law income tax liabilities for CYR.

itax_rev1 = calc1.weighted_total('iitax')

Read JSON reform file and use (the default) static analysis assumptions.

reform_filename = 'github://PSLmodels:Tax-Calculator@master/docs/recipes/_static/reformA.json'
params = tc.Calculator.read_json_param_objects(reform_filename, None)

Specify Calculator object for static analysis of reform policy.

calc2 = tc.Calculator(policy=pol, records=recs)


Calculate reform income tax liabilities for CYR.

itax_rev2 = calc2.weighted_total('iitax')


Print total revenue estimates for 2018.

Estimates in billons of dollars rounded to nearest hundredth of a billion.

print('{}_CLP_itax_rev($B)= {:.3f}'.format(CYR, itax_rev1 * 1e-9))
print('{}_REF_itax_rev($B)= {:.3f}'.format(CYR, itax_rev2 * 1e-9))
2020_CLP_itax_rev($B)= 764.231
2020_REF_itax_rev($B)= 766.915

Generate several other standard results tables.

# Aggregate diagnostic tables for CYR.
clp_diagnostic_table = calc1.diagnostic_table(1)
ref_diagnostic_table = calc2.diagnostic_table(1)

# Income-tax distribution for CYR with CLP and REF results side-by-side.
dist_table1, dist_table2 = calc1.distribution_tables(calc2, 'weighted_deciles')
assert isinstance(dist_table1, pd.DataFrame)
assert isinstance(dist_table2, pd.DataFrame)
dist_extract = pd.DataFrame()
dist_extract['funits(#m)'] = dist_table1['count']
dist_extract['itax1($b)'] = dist_table1['iitax']
dist_extract['itax2($b)'] = dist_table2['iitax']
dist_extract['aftertax_inc1($b)'] = dist_table1['aftertax_income']
dist_extract['aftertax_inc2($b)'] = dist_table2['aftertax_income']

# Income-tax difference table by expanded-income decile for CYR.
diff_table = calc1.difference_table(calc2, 'weighted_deciles', 'iitax')
assert isinstance(diff_table, pd.DataFrame)
diff_extract = pd.DataFrame()
dif_colnames = ['count', 'tot_change', 'mean', 'pc_aftertaxinc']
ext_colnames = ['funits(#m)', 'agg_diff($b)', 'mean_diff($)', 'aftertaxinc_diff(%)']
for dname, ename in zip(dif_colnames, ext_colnames):
    diff_extract[ename] = diff_table[dname]


Generate a decile graph and display it using Bokeh (will render in Jupyter, not in webpage).

fig = calc1.pch_graph(calc2)
Loading BokehJS ...
ValueError                                Traceback (most recent call last)
Cell In[12], line 3
      1 fig = calc1.pch_graph(calc2)
      2 output_notebook()
----> 3 show(fig)

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/bokeh/io/, in show(obj, browser, new, notebook_handle, notebook_url, **kwargs)
    155     assert state.notebook_type is not None
    156     return run_notebook_hook(state.notebook_type, 'app', obj, state, notebook_url, **kwargs)
--> 158 raise ValueError(_BAD_SHOW_MSG)

ValueError: Invalid object to show. The object to passed to show must be one of:

* a UIElement (e.g. a plot, figure, widget or layout)
* a Bokeh Application
* a callable suitable to an application FunctionHandler