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
baseline,
reform excluding behavioral responses, and
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 |