This notebook contains an example for how to use the taxbrain python package

# # Install conda, taxbrain, and taxcalc if in Google Colab.
import sys
if 'google.colab' in sys.modules and 'taxbrain' not in sys.modules:
    # Install taxbrain and dependencies
    !pip install taxbrain &> /dev/null
    !pip install taxcalc &> /dev/null
    !pip install pypandoc &> /dev/null
    !pip install -U pandas &> /dev/null  # make sure pandas is up to date
from taxbrain import TaxBrain, differences_plot, distribution_plot
reform_url = "https://raw.githubusercontent.com/PSLmodels/Tax-Calculator/master/taxcalc/reforms/Larson2019.json"
start_year = 2021
end_year = 2030

Static Reform#

After importing the TaxBrain class from the taxbrain package, we initiate an instance of the class by specifying the start and end year of the anlaysis, which microdata to use, and a policy reform. Additional arguments can be used to specify econoimc assumptions and individual behavioral elasticites.

Once the class has been initiated, the run() method will handle executing each model

tb_static = TaxBrain(start_year, end_year, microdata="CPS", reform=reform_url)
tb_static.run()

Once the calculators have been run, you can produce a number of tables, including a weighted total of a given variable each year under both current law and the user reform.

print("Combined Tax Liability Over the Budget Window")
tb_static.weighted_totals("combined")
Combined Tax Liability Over the Budget Window
2021 2022 2023 2024 2025 2026 2027 2028 2029 2030
Base 2.269723e+12 3.065057e+12 3.187451e+12 3.430286e+12 3.558234e+12 3.749566e+12 3.923694e+12 4.101939e+12 4.316249e+12 4.541999e+12
Reform 2.326814e+12 3.143256e+12 3.282028e+12 3.545953e+12 3.699010e+12 3.912748e+12 4.109452e+12 4.311587e+12 4.533253e+12 4.770344e+12
Difference 5.709077e+10 7.819926e+10 9.457664e+10 1.156674e+11 1.407758e+11 1.631815e+11 1.857588e+11 2.096474e+11 2.170046e+11 2.283448e+11

If you are interested in a detailed look on the reform’s effect, you can produce a differences table for a given year.

print("Differences Table")
tb_static.differences_table(start_year, "weighted_deciles", "combined")
Differences Table
count tax_cut perc_cut tax_inc perc_inc mean tot_change share_of_change ubi benefit_cost_total benefit_value_total pc_aftertaxinc
0-10n 0.102049 0.000000 0.000000 0.035165 34.458500 8.218450 0.000839 0.001469 0.0 0.0 0.0 0.006305
0-10z 8.390748 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.0 0.0 0.0 0.000000
0-10p 12.213889 0.000000 0.000000 5.510201 45.114223 3.693099 0.045107 0.079009 0.0 0.0 0.0 -0.030271
10-20 20.708304 0.000000 0.000000 15.031974 72.589110 17.199769 0.356178 0.623880 0.0 0.0 0.0 -0.049703
20-30 20.707505 0.019010 0.091801 13.230015 63.889952 27.007242 0.559253 0.979585 0.0 0.0 0.0 -0.047437
30-40 20.706950 0.259493 1.253170 11.850040 57.227357 29.341194 0.607567 1.064212 0.0 0.0 0.0 -0.036801
40-50 20.706181 1.020739 4.929636 12.904516 62.322051 27.887624 0.577446 1.011453 0.0 0.0 0.0 -0.014320
50-60 20.708634 2.983512 14.407092 12.853635 62.068967 -36.541184 -0.756718 -1.325465 0.0 0.0 0.0 0.110734
60-70 20.707540 3.810788 18.402900 12.691879 61.291099 -155.091891 -3.211572 -5.625378 0.0 0.0 0.0 0.261506
70-80 20.705701 4.042144 19.521890 13.340665 64.429912 -198.758187 -4.115428 -7.208569 0.0 0.0 0.0 0.271550
80-90 20.709850 4.418265 21.334121 14.297969 69.039464 -427.711584 -8.857843 -15.515367 0.0 0.0 0.0 0.416827
90-100 20.708024 1.431391 6.912254 17.352383 83.795459 3471.405278 71.885943 125.915170 0.0 0.0 0.0 -1.292652
ALL 207.075375 17.985342 8.685408 129.098443 62.343696 275.700439 57.090772 100.000000 0.0 0.0 0.0 -0.321387
90-95 10.354203 1.292340 12.481311 8.241434 79.595059 -298.772462 -3.093551 -5.418653 0.0 0.0 0.0 0.256932
95-99 8.282277 0.139051 1.678898 7.283324 87.938673 318.774347 2.640177 4.624526 0.0 0.0 0.0 -0.070544
Top 1% 2.071544 0.000000 0.000000 1.827625 88.225265 34920.477840 72.339316 126.709297 0.0 0.0 0.0 -4.338101

TaxBrain comes with two (and counting) built in plots as well

differences_plot(tb_static, 'combined', figsize=(10, 8));
../../_images/001f15e1856ec1ec444f3d71dfca372e6f861f75c4b9e332885a775dfc772989.png
distribution_plot(tb_static, 2021, figsize=(10, 8));
../../_images/815ae72c1cc288c78cf547da4ce9dbf1fabc59321658f959d748d0f95c197407.png

You can run a partial-equlibrium dynamic simulation by initiating the TaxBrian instance exactly as you would for the static reform, but with your behavioral assumptions specified

tb_dynamic = TaxBrain(start_year, end_year, microdata="CPS", reform=reform_url,
                      behavior={"sub": 0.25})
tb_dynamic.run()
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/numba/core/serialize.py:30, in _numba_unpickle(address, bytedata, hashed)
     27 _unpickled_memo = {}
---> 30 def _numba_unpickle(address, bytedata, hashed):
     31     """Used by `numba_unpickle` from _helperlib.c
     32 
     33     Parameters
   (...)     42         unpickled object
     43     """

KeyboardInterrupt: 

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
SystemError: <function _numba_unpickle at 0x7f9eb32b0e00> returned a result with an exception set

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
SystemError: <function _numba_unpickle at 0x7f9eb32b0e00> returned a result with an exception set

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
SystemError: <function _numba_unpickle at 0x7f9eb32b0e00> returned a result with an exception set

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
SystemError: <function _numba_unpickle at 0x7f9eb32b0e00> returned a result with an exception set

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
SystemError: <function _numba_unpickle at 0x7f9eb32b0e00> returned a result with an exception set

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
SystemError: <function _numba_unpickle at 0x7f9eb32b0e00> returned a result with an exception set

The above exception was the direct cause of the following exception:

SystemError                               Traceback (most recent call last)
Cell In[9], line 3
      1 tb_dynamic = TaxBrain(start_year, end_year, microdata="CPS", reform=reform_url,
      2                       behavior={"sub": 0.25})
----> 3 tb_dynamic.run()

File ~/work/Tax-Brain/Tax-Brain/taxbrain/taxbrain.py:178, in TaxBrain.run(self, varlist, client, num_workers)
    176     if self.verbose:
    177         print("Running dynamic simulations")
--> 178     self._dynamic_run(
    179         varlist, base_calc, reform_calc, client, num_workers
    180     )
    181 else:
    182     if self.verbose:

File ~/work/Tax-Brain/Tax-Brain/taxbrain/taxbrain.py:484, in TaxBrain._dynamic_run(self, varlist, base_calc, reform_calc, client, num_workers)
    479 lazy_values = []
    480 for yr in range(self.start_year, self.end_year + 1):
    481     lazy_values.extend(
    482         [
    483             delayed(
--> 484                 self._behresp_advance(
    485                     base_calc, reform_calc, varlist, yr
    486                 )
    487             )
    488         ]
    489     )
    490 if client:
    491     futures = client.compute(lazy_values, num_workers=num_workers)

File ~/work/Tax-Brain/Tax-Brain/taxbrain/taxbrain.py:424, in TaxBrain._behresp_advance(self, base_calc, reform_calc, varlist, year)
    416 if self.corp_revenue is not None:
    417     reform_calc = dist_corp(
    418         reform_calc,
    419         self.corp_revenue,
   (...)    422         self.ci_params,
    423     )
--> 424 base, reform = behresp.response(
    425     base_calc, reform_calc, self.params["behavior"], dump=True
    426 )
    427 base_df = base[varlist]
    428 reform_df = reform[varlist]

File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/behresp/behavior.py:179, in response(calc_1, calc_2, elasticities, dump)
    176 zero_sub_and_inc = False
    177 # calculate marginal combined tax rates on taxpayer wages+salary
    178 # (e00200p is taxpayer's wages+salary)
--> 179 wage_mtr1, wage_mtr2 = _mtr12(calc1, calc2,
    180                               mtr_of='e00200p',
    181                               tax_type='combined')
    182 # calculate magnitude of substitution effect
    183 if be_sub == 0.0:

File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/behresp/behavior.py:153, in response.<locals>._mtr12(calc__1, calc__2, mtr_of, tax_type)
    148 """
    149 Computes marginal tax rates for Calculator objects calc__1 and calc__2
    150 for specified mtr_of income type and specified tax_type.
    151 """
    152 assert tax_type in ('combined', 'iitax')
--> 153 _, iitax1, combined1 = calc__1.mtr(mtr_of, wrt_full_compensation=True)
    154 _, iitax2, combined2 = calc__2.mtr(mtr_of, wrt_full_compensation=True)
    155 if tax_type == 'combined':

File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/taxcalc/calculator.py:676, in Calculator.mtr(self, variable_str, negative_finite_diff, zero_out_calculated_vars, calc_all_already_called, wrt_full_compensation)
    674 self.restore_records()
    675 if not calc_all_already_called or zero_out_calculated_vars:
--> 676     self.calc_all(zero_out_calc_vars=zero_out_calculated_vars)
    677 payrolltax_base = self.array('payrolltax')
    678 incometax_base = self.array('iitax')

File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/taxcalc/calculator.py:170, in Calculator.calc_all(self, zero_out_calc_vars)
    168 UBI(self.__policy, self.__records)
    169 BenefitPrograms(self)
--> 170 self._calc_one_year(zero_out_calc_vars)
    171 FairShareTax(self.__policy, self.__records)
    172 LumpSumTax(self.__policy, self.__records)

File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/taxcalc/calculator.py:1441, in Calculator._calc_one_year(self, zero_out_calc_vars)
   1439 del item_cvar
   1440 # Calculate taxes with optimal itemized deduction
-> 1441 self._taxinc_to_amt()
   1442 F2441(self.__policy, self.__records)
   1443 EITC(self.__policy, self.__records)

File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/taxcalc/calculator.py:1372, in Calculator._taxinc_to_amt(self)
   1370 TaxInc(self.__policy, self.__records)
   1371 SchXYZTax(self.__policy, self.__records)
-> 1372 GainsTax(self.__policy, self.__records)
   1373 AGIsurtax(self.__policy, self.__records)
   1374 NetInvIncTax(self.__policy, self.__records)

File /usr/share/miniconda/envs/taxbrain-dev/lib/python3.12/site-packages/taxcalc/decorators.py:326, in iterate_jit.<locals>.make_wrapper.<locals>.wrapper(*args, **kwargs)
    323 eval(func_code,  # pylint: disable=eval-used
    324      {"applied_f": applied_jitted_f}, fakeglobals)
    325 high_level_fn = fakeglobals["hl_func"]
--> 326 ans = high_level_fn(*args, **kwargs)
    327 return ans

File <string>:12, in hl_func(pm, pf)

SystemError: CPUDispatcher(<function ap_func at 0x7f9eb20fc040>) returned a result with an exception set

Once that finishes running, we can produce the same weighted total table as we did with the static run.

print("Partial Equilibrium - Combined Tax Liability")
tb_dynamic.weighted_totals("combined")
Partial Equilibrium - Combined Tax Liability
2021 2022 2023 2024 2025 2026 2027 2028 2029 2030
Base 2.269727e+12 3.067767e+12 3.246623e+12 3.395728e+12 3.575938e+12 3.979716e+12 4.161639e+12 4.344151e+12 4.543397e+12 4.749701e+12
Reform 2.307804e+12 3.119395e+12 3.312243e+12 3.476774e+12 3.673491e+12 4.082934e+12 4.282357e+12 4.483171e+12 4.690507e+12 4.904587e+12
Difference 3.807725e+10 5.162814e+10 6.561941e+10 8.104634e+10 9.755272e+10 1.032176e+11 1.207186e+11 1.390202e+11 1.471100e+11 1.548863e+11

Or we can produce a distribution table to see details on the effects of the reform.

print("Distribution Table")
tb_dynamic.distribution_table(start_year, "weighted_deciles", "expanded_income", "reform")
Distribution Table
count c00100 count_StandardDed standard count_ItemDed c04470 c04600 c04800 taxbc c62100 ... othertaxes refund iitax payrolltax combined ubi benefit_cost_total benefit_value_total expanded_income aftertax_income
0-10n 0.102049 -7.647952 0.018507 -5.835622 0.000000 0.000000 0.0 0.000000 0.000000 -7.764826 ... 0.000000 0.354646 -0.354646 0.064998 -0.289648 0.0 0.799655 0.799655 -6.940854 -6.651206
0-10z 8.391348 -0.092134 8.391348 112.514735 0.000000 0.000000 0.0 0.000000 0.000000 -0.092134 ... 0.000000 17.779859 -17.779859 0.000000 -17.779859 0.0 0.000000 0.000000 0.000000 17.779859
0-10p 12.213289 28.286909 12.207105 167.047672 0.006184 0.035899 0.0 0.109175 0.003643 28.250938 ... 0.000000 28.404318 -28.400675 3.495389 -24.905286 0.0 19.592770 19.592770 50.389807 75.295093
10-20 20.707972 207.041657 20.230779 280.157298 0.472337 7.619006 0.0 25.986559 2.401345 200.049307 ... 0.000000 68.924569 -66.523628 27.626076 -38.897552 0.0 107.290602 107.290602 323.220845 362.118396
20-30 20.707385 316.378323 19.648591 287.017567 1.053396 18.740597 0.0 116.225685 11.584224 299.238036 ... 0.000000 67.787144 -56.195886 43.577393 -12.618493 0.0 258.441980 258.441980 590.805721 603.424214
30-40 20.707088 392.642544 19.510973 297.416383 1.187950 22.114507 0.0 184.988094 19.315954 371.147034 ... 0.000000 67.101813 -47.772787 53.603619 5.830832 0.0 369.313918 369.313918 787.711190 781.880357
40-50 20.708472 550.480439 19.029242 307.522806 1.676905 33.455448 0.0 286.992712 30.516102 519.344714 ... 0.000000 78.292375 -47.767104 72.879144 25.112041 0.0 404.535394 404.535394 994.589197 969.477157
50-60 20.707393 735.630028 18.331876 326.411031 2.373124 49.426182 0.0 425.521954 47.073700 691.665427 ... 0.000000 93.114874 -46.034925 95.833407 49.798482 0.0 466.945896 466.945896 1258.080439 1208.281958
60-70 20.707515 959.511949 17.811997 357.787922 2.893239 62.644720 0.0 606.027111 72.245168 905.890709 ... 0.000000 112.380798 -40.126138 122.146512 82.020374 0.0 578.376040 578.376040 1603.049167 1521.028793
70-80 20.707197 1365.708941 16.719155 373.852288 3.982463 98.050853 0.0 941.858952 119.044547 1288.034856 ... 0.000000 121.724695 -2.658703 172.536431 169.877728 0.0 634.000703 634.000703 2085.339711 1915.461983
80-90 20.707626 2093.482677 14.553142 349.813909 6.152092 179.989249 0.0 1578.829459 218.697073 1951.769562 ... 0.004426 125.344051 93.372100 257.189635 350.561734 0.0 640.771616 640.771616 2872.252958 2521.691223
90-100 20.708041 6246.236344 9.453390 234.654561 11.254651 386.830268 0.0 5608.201771 1197.758029 5942.795098 ... 13.805670 47.686797 1165.232430 553.861208 1719.093638 0.0 512.904614 512.904614 6987.435086 5268.341448
ALL 207.075375 12887.659726 175.906106 3088.360551 31.052341 858.906729 0.0 9774.741472 1718.639786 12190.328720 ... 13.810095 828.895941 904.990179 1402.813813 2307.803992 0.0 3992.973188 3992.973188 17545.933268 15238.129276
90-95 10.353704 1667.324398 5.676570 140.884353 4.677134 148.216845 0.0 1374.670598 220.454555 1553.995778 ... 0.030698 34.014434 186.471142 189.172816 375.643958 0.0 284.797859 284.797859 2039.255590 1663.611631
95-99 8.283571 2341.021395 3.296412 82.219564 4.987159 170.622619 0.0 2075.284086 397.064195 2203.591113 ... 1.300444 13.626819 384.737819 212.266995 597.004815 0.0 192.019886 192.019886 2627.393087 2030.388273
Top 1% 2.070766 2237.890550 0.480409 11.550644 1.590357 67.990803 0.0 2158.247087 580.239279 2185.208206 ... 12.474527 0.045543 594.023468 152.421397 746.444865 0.0 36.086868 36.086868 2320.786409 1574.341544

16 rows × 24 columns

Dynamic Reform with Corporate Income Tax Incidence#

Now we simulate a dynamic revenue estimate while accounting for the incidence of a corporate income tax change.

# Corporate revenue estimate
corp_rev = [5_000_000_000] * (end_year - start_year + 1)
incidence_assumptions = {
    "Incidence": {  # long-run incidence of corporate tax
        "Labor share": 0.5,
        "Shareholder share": 0.4,
        "All capital share": 0.1,
    },
    "Long run years": 10,  # number of years to reach long-run incidence
}
tb_dynamic = TaxBrain(start_year, end_year, microdata="CPS", reform=reform_url,
                      behavior={"sub": 0.25},
                      corp_revenue=corp_rev,
                      corp_incidence_assumptions=incidence_assumptions)
tb_dynamic.run()
print("Partial Equilibrium - Combined Tax Liability")
tb_dynamic.weighted_totals("combined")
Partial Equilibrium - Combined Tax Liability
2021 2022 2023 2024 2025 2026 2027 2028 2029 2030
Base 2.269727e+12 3.067767e+12 3.246623e+12 3.395728e+12 3.575938e+12 3.979716e+12 4.161639e+12 4.344151e+12 4.543397e+12 4.749701e+12
Reform 2.309448e+12 3.121009e+12 3.313788e+12 3.478349e+12 3.675071e+12 4.084663e+12 4.284063e+12 4.484873e+12 4.692225e+12 4.906309e+12
Difference 3.972134e+10 5.324191e+10 6.716455e+10 8.262134e+10 9.913307e+10 1.049468e+11 1.224239e+11 1.407223e+11 1.488282e+11 1.566081e+11