Recipe 2: Estimating Behavioral Response to Reform

Recipe 2: Estimating Behavioral Response to Reform#

This is an advanced recipe that should be followed only after mastering the basic recipe. This recipe shows how to analyze the behavioral responses to a tax reform using the Behavioral-Responses behresp package.

import sys
if 'google.colab' in sys.modules:
    !pip install -q condacolab &> /dev/null # dev/null suppresses output
    import condacolab
    condacolab.install()
import taxcalc as tc
import behresp

# use publicly-available CPS input file
recs = tc.Records.cps_constructor()

# specify baseline Calculator object representing current-law policy
pol = tc.Policy()
calc1 = tc.Calculator(policy=pol, records=recs)

CYR = 2020

# calculate aggregate current-law income tax liabilities for cyr
calc1.advance_to_year(CYR)
calc1.calc_all()
itax_rev1 = calc1.weighted_total('iitax')

# specify Calculator object for static analysis of reform policy
pol.implement_reform(tc.Policy.read_json_reform('github://PSLmodels:Tax-Calculator@master/docs/recipes/_static/reformA.json'))
calc2 = tc.Calculator(policy=pol, records=recs)

# calculate reform income tax liabilities for cyr under static assumptions
calc2.advance_to_year(CYR)
calc2.calc_all()
itax_rev2sa = calc2.weighted_total('iitax')

# specify assumed non-zero response-function substitution elasticity
response_elasticities = {'sub': 0.25}

# specify Calculator object for analysis of reform with behavioral responses
calc2 = tc.Calculator(policy=pol, records=recs)
calc2.advance_to_year(CYR)
_, df2br = behresp.response(calc1, calc2, response_elasticities)

# calculate reform income tax liabilities for CYR with behavioral response
itax_rev2br = (df2br['iitax'] * df2br['s006']).sum()

# print total income tax revenue estimates for CYR
# (estimates in billons of dollars)
print('{}_CURRENT_LAW_P__itax_rev($B)= {:.3f}'.format(CYR, itax_rev1 * 1e-9))
print('{}_REFORM_STATIC__itax_rev($B)= {:.3f}'.format(CYR, itax_rev2sa * 1e-9))
print('{}_REFORM_DYNAMIC_itax_rev($B)= {:.3f}'.format(CYR, itax_rev2br * 1e-9))
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
Cell In[3], line 19
     16 itax_rev1 = calc1.weighted_total('iitax')
     18 # specify Calculator object for static analysis of reform policy
---> 19 pol.implement_reform(tc.Policy.read_json_reform('github://PSLmodels:Tax-Calculator@master/docs/recipes/_static/reformA.json'))
     20 calc2 = tc.Calculator(policy=pol, records=recs)
     22 # calculate reform income tax liabilities for cyr under static assumptions

File ~/work/Tax-Calculator/Tax-Calculator/taxcalc/policy.py:168, in Policy.read_json_reform(obj)
    160 @staticmethod
    161 def read_json_reform(obj):
    162     """
    163     Return a reform dictionary suitable for use with implement_reform
    164     method generated from the specified JSON object, which can be None or
    165     a string containing a local filename, a URL beginning with 'http'
    166     pointing to a valid JSON file hosted online, or a valid JSON text.
    167     """
--> 168     return Parameters._read_json_revision(obj, 'policy')

File ~/work/Tax-Calculator/Tax-Calculator/taxcalc/parameters.py:759, in Parameters._read_json_revision(obj, topkey)
    756 if obj is None:
    757     return {}
--> 759 full_dict = paramtools.read_json(obj)
    761 # check top-level key contents of dictionary
    762 if topkey in full_dict.keys():

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/paramtools/utils.py:105, in read_json(params_or_path, storage_options)
     89 def read_json(
     90     params_or_path: FileDictStringLike,
     91     storage_options: Optional[Dict[str, Any]] = None,
     92 ):
     93     """
     94     Read JSON data of the form:
     95     - Dict.
   (...)    103 
    104     """
--> 105     res = _read(params_or_path, storage_options)
    106     if isinstance(res, str):
    107         try:

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/paramtools/utils.py:50, in _read(params_or_path, storage_options)
     47         return f.read()
     49 if isinstance(params_or_path, str) and _is_url(params_or_path):
---> 50     with fsspec.open(params_or_path, "r", **(storage_options or {})) as f:
     51         return f.read()
     53 if isinstance(params_or_path, str):

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/fsspec/core.py:491, in open(urlpath, mode, compression, encoding, errors, protocol, newline, expand, **kwargs)
    433 """Given a path or paths, return one ``OpenFile`` object.
    434 
    435 Parameters
   (...)    488   https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations
    489 """
    490 expand = DEFAULT_EXPAND if expand is None else expand
--> 491 out = open_files(
    492     urlpath=[urlpath],
    493     mode=mode,
    494     compression=compression,
    495     encoding=encoding,
    496     errors=errors,
    497     protocol=protocol,
    498     newline=newline,
    499     expand=expand,
    500     **kwargs,
    501 )
    502 if not out:
    503     raise FileNotFoundError(urlpath)

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/fsspec/core.py:295, in open_files(urlpath, mode, compression, encoding, errors, name_function, num, protocol, newline, auto_mkdir, expand, **kwargs)
    216 def open_files(
    217     urlpath,
    218     mode="rb",
   (...)    228     **kwargs,
    229 ):
    230     """Given a path or paths, return a list of ``OpenFile`` objects.
    231 
    232     For writing, a str path must contain the "*" character, which will be filled
   (...)    293       https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations
    294     """
--> 295     fs, fs_token, paths = get_fs_token_paths(
    296         urlpath,
    297         mode,
    298         num=num,
    299         name_function=name_function,
    300         storage_options=kwargs,
    301         protocol=protocol,
    302         expand=expand,
    303     )
    304     if fs.protocol == "file":
    305         fs.auto_mkdir = auto_mkdir

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/fsspec/core.py:667, in get_fs_token_paths(urlpath, mode, num, name_function, storage_options, protocol, expand)
    665     inkwargs["fo"] = urls
    666 paths, protocol, _ = chain[0]
--> 667 fs = filesystem(protocol, **inkwargs)
    668 if isinstance(urlpath, (list, tuple, set)):
    669     pchains = [
    670         _un_chain(stringify_path(u), storage_options or {})[0] for u in urlpath
    671     ]

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/fsspec/registry.py:322, in filesystem(protocol, **storage_options)
    315     warnings.warn(
    316         "The 'arrow_hdfs' protocol has been deprecated and will be "
    317         "removed in the future. Specify it as 'hdfs'.",
    318         DeprecationWarning,
    319     )
    321 cls = get_filesystem_class(protocol)
--> 322 return cls(**storage_options)

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/fsspec/spec.py:81, in _Cached.__call__(cls, *args, **kwargs)
     79     return cls._cache[token]
     80 else:
---> 81     obj = super().__call__(*args, **kwargs)
     82     # Setting _fs_token here causes some static linters to complain.
     83     obj._fs_token_ = token

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/fsspec/implementations/github.py:66, in GithubFileSystem.__init__(self, org, repo, sha, username, token, timeout, **kwargs)
     63     sha = r.json()["default_branch"]
     65 self.root = sha
---> 66 self.ls("")
     67 try:
     68     from .http import HTTPFileSystem

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/fsspec/implementations/github.py:175, in GithubFileSystem.ls(self, path, detail, sha, _sha, **kwargs)
    173 if r.status_code == 404:
    174     raise FileNotFoundError(path)
--> 175 r.raise_for_status()
    176 types = {"blob": "file", "tree": "directory"}
    177 out = [
    178     {
    179         "name": path + "/" + f["path"] if path else f["path"],
   (...)    186     if f["type"] in types
    187 ]

File /usr/share/miniconda/envs/taxcalc-dev/lib/python3.11/site-packages/requests/models.py:1026, in Response.raise_for_status(self)
   1021     http_error_msg = (
   1022         f"{self.status_code} Server Error: {reason} for url: {self.url}"
   1023     )
   1025 if http_error_msg:
-> 1026     raise HTTPError(http_error_msg, response=self)

HTTPError: 403 Client Error: rate limit exceeded for url: https://api.github.com/repos/PSLmodels/Tax-Calculator/git/trees/master

Create multi-year diagnostic tables for

  1. baseline,

  2. reform excluding behavioral responses, and

  3. reform including behavioral responses

NUM_YEARS = 3  # number of diagnostic table years beginning with CYR
dtable1 = calc1.diagnostic_table(NUM_YEARS)
dtable2 = calc2.diagnostic_table(NUM_YEARS)
dvar_list3 = list()
year_list3 = list()
for year in range(CYR, CYR + NUM_YEARS):
    calc1.advance_to_year(year)
    calc2.advance_to_year(year)
    _, df2br = behresp.response(calc1, calc2, response_elasticities)
    dvar_list3.append(df2br)
    year_list3.append(year)
dtable3 = tc.create_diagnostic_table(dvar_list3, year_list3)

Diagnostic table for baseline:

dtable1
2020 2021 2022
Returns (#m) 204.480 207.080 209.660
AGI ($b) 11573.238 13156.205 13723.185
Itemizers (#m) 27.730 32.030 31.220
Itemized Deduction ($b) 752.189 880.903 882.969
Standard Deduction Filers (#m) 176.750 175.050 178.440
Standard Deduction ($b) 3075.468 3066.702 3229.758
Personal Exemption ($b) 0.000 0.000 0.000
Taxable Income ($b) 8617.397 10040.159 10505.738
Regular Tax ($b) 1505.274 1760.905 1864.719
AMT Income ($b) 11012.668 12497.322 13059.342
AMT Liability ($b) 0.568 1.811 1.322
AMT Filers (#m) 0.090 0.330 0.250
Tax before Credits ($b) 1505.841 1762.716 1866.041
Refundable Credits ($b) 654.861 827.256 98.250
Nonrefundable Credits ($b) 99.385 0.005 107.651
Reform Surtaxes ($b) 0.000 0.000 0.000
Other Taxes ($b) 12.575 13.844 14.446
Ind Income Tax ($b) 764.170 949.299 1674.587
Payroll Taxes ($b) 1217.941 1318.728 1421.579
Combined Liability ($b) 1982.111 2268.028 3096.166
With Income Tax <= 0 (#m) 135.170 127.680 95.970
With Combined Tax <= 0 (#m) 98.150 97.700 67.730
UBI Benefits ($b) 0.000 0.000 0.000
Total Benefits, Consumption Value ($b) 3617.042 3992.973 4069.559
Total Benefits Cost ($b) 3617.042 3992.973 4069.559

Diagnostic table for reform, excluding behavioral responses:

dtable2
2020 2021 2022
Returns (#m) 204.480 207.080 209.660
AGI ($b) 11573.238 13156.205 13723.185
Itemizers (#m) 27.660 31.890 31.110
Itemized Deduction ($b) 749.755 877.489 879.822
Standard Deduction Filers (#m) 176.820 175.180 178.560
Standard Deduction ($b) 3076.756 3068.785 3231.586
Personal Exemption ($b) 375.177 383.210 404.808
Taxable Income ($b) 8369.678 9776.270 10230.157
Regular Tax ($b) 1505.595 1764.964 1871.117
AMT Income ($b) 11014.938 12500.520 13062.305
AMT Liability ($b) 0.585 1.927 1.414
AMT Filers (#m) 0.090 0.350 0.270
Tax before Credits ($b) 1506.180 1766.891 1872.531
Refundable Credits ($b) 658.196 827.256 101.933
Nonrefundable Credits ($b) 93.705 0.002 102.012
Reform Surtaxes ($b) 0.000 0.000 0.000
Other Taxes ($b) 12.575 13.844 14.446
Ind Income Tax ($b) 766.855 953.478 1683.031
Payroll Taxes ($b) 1217.941 1318.728 1421.579
Combined Liability ($b) 1984.796 2272.206 3104.610
With Income Tax <= 0 (#m) 137.340 129.960 98.600
With Combined Tax <= 0 (#m) 98.910 98.600 68.450
UBI Benefits ($b) 0.000 0.000 0.000
Total Benefits, Consumption Value ($b) 3617.042 3992.973 4069.559
Total Benefits Cost ($b) 3617.042 3992.973 4069.559

Diagnostic table for reform, including behavioral responses:

dtable3
2020 2021 2022
Returns (#m) 204.480 207.080 209.660
AGI ($b) 11547.479 13123.042 13686.894
Itemizers (#m) 27.630 31.870 31.080
Itemized Deduction ($b) 748.521 875.970 878.204
Standard Deduction Filers (#m) 176.850 175.200 178.580
Standard Deduction ($b) 3077.317 3069.335 3232.059
Personal Exemption ($b) 375.177 383.210 404.808
Taxable Income ($b) 8344.174 9743.507 10194.449
Regular Tax ($b) 1494.026 1750.669 1855.496
AMT Income ($b) 10990.180 12468.655 13027.447
AMT Liability ($b) 0.583 1.967 1.452
AMT Filers (#m) 0.090 0.350 0.280
Tax before Credits ($b) 1494.609 1752.636 1856.948
Refundable Credits ($b) 657.905 827.233 101.663
Nonrefundable Credits ($b) 93.933 0.002 102.266
Reform Surtaxes ($b) 0.000 0.000 0.000
Other Taxes ($b) 12.290 13.496 14.083
Ind Income Tax ($b) 755.060 938.897 1667.103
Payroll Taxes ($b) 1217.293 1317.688 1420.460
Combined Liability ($b) 1972.353 2256.585 3087.563
With Income Tax <= 0 (#m) 137.310 129.930 98.420
With Combined Tax <= 0 (#m) 98.880 98.570 68.270
UBI Benefits ($b) 0.000 0.000 0.000
Total Benefits, Consumption Value ($b) 3617.042 3992.973 4069.559
Total Benefits Cost ($b) 3617.042 3992.973 4069.559