taxcalc 4.3.5__py3-none-any.whl → 4.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- taxcalc/__init__.py +1 -1
- taxcalc/calcfunctions.py +326 -171
- taxcalc/calculator.py +35 -34
- taxcalc/cli/tc.py +6 -7
- taxcalc/consumption.py +14 -9
- taxcalc/data.py +8 -8
- taxcalc/decorators.py +3 -3
- taxcalc/growdiff.py +6 -6
- taxcalc/growfactors.py +1 -1
- taxcalc/parameters.py +91 -47
- taxcalc/policy.py +23 -7
- taxcalc/records.py +1 -0
- taxcalc/records_variables.json +6 -0
- taxcalc/reforms/ext.json +1 -1
- taxcalc/taxcalcio.py +88 -73
- taxcalc/tests/cmpi_cps_expect.txt +6 -6
- taxcalc/tests/cmpi_puf_expect.txt +6 -6
- taxcalc/tests/conftest.py +42 -41
- taxcalc/tests/test_4package.py +47 -3
- taxcalc/tests/test_benefits.py +9 -8
- taxcalc/tests/test_calcfunctions.py +55 -38
- taxcalc/tests/test_calculator.py +14 -10
- taxcalc/tests/test_compare.py +44 -50
- taxcalc/tests/test_compatible_data.py +9 -7
- taxcalc/tests/test_consumption.py +41 -22
- taxcalc/tests/test_cpscsv.py +81 -31
- taxcalc/tests/test_data.py +31 -24
- taxcalc/tests/test_decorators.py +84 -32
- taxcalc/tests/test_growdiff.py +20 -19
- taxcalc/tests/test_growfactors.py +8 -8
- taxcalc/tests/test_parameters.py +54 -58
- taxcalc/tests/test_policy.py +16 -14
- taxcalc/tests/test_puf_var_stats.py +14 -14
- taxcalc/tests/test_pufcsv.py +40 -40
- taxcalc/tests/test_records.py +73 -60
- taxcalc/tests/test_reforms.py +34 -31
- taxcalc/tests/test_responses.py +4 -4
- taxcalc/tests/test_taxcalcio.py +76 -62
- taxcalc/tests/test_utils.py +78 -46
- taxcalc/utils.py +49 -42
- taxcalc/validation/taxsim35/taxsim_emulation.json +1 -5
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/METADATA +23 -11
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/RECORD +47 -47
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/WHEEL +1 -1
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/LICENSE +0 -0
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/entry_points.txt +0 -0
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/top_level.txt +0 -0
taxcalc/tests/test_parameters.py
CHANGED
@@ -13,15 +13,11 @@ import tempfile
|
|
13
13
|
import numpy as np
|
14
14
|
import paramtools
|
15
15
|
import pytest
|
16
|
-
|
17
|
-
from taxcalc import
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
GrowDiff,
|
22
|
-
GrowFactors,
|
23
|
-
is_paramtools_format,
|
24
|
-
)
|
16
|
+
from taxcalc.parameters import Parameters, is_paramtools_format
|
17
|
+
from taxcalc.policy import Policy
|
18
|
+
from taxcalc.consumption import Consumption
|
19
|
+
from taxcalc.growdiff import GrowDiff
|
20
|
+
from taxcalc.growfactors import GrowFactors
|
25
21
|
|
26
22
|
|
27
23
|
# Test specification and use of simple Parameters-derived class that has
|
@@ -129,6 +125,7 @@ def test_params_class(revision, expect, params_json_file):
|
|
129
125
|
"""
|
130
126
|
The Params class is derived from the abstract base Parameter class.
|
131
127
|
"""
|
128
|
+
# pylint: disable=too-few-public-methods,abstract-method
|
132
129
|
DEFAULTS_FILE_NAME = params_json_file.name
|
133
130
|
DEFAULTS_FILE_PATH = ''
|
134
131
|
START_YEAR = 2001
|
@@ -157,8 +154,8 @@ def test_params_class(revision, expect, params_json_file):
|
|
157
154
|
assert prms.start_year == 2001
|
158
155
|
assert prms.current_year == 2001
|
159
156
|
assert prms.end_year == 2010
|
160
|
-
assert prms.inflation_rates() ==
|
161
|
-
assert prms.wage_growth_rates() ==
|
157
|
+
assert prms.inflation_rates() == []
|
158
|
+
assert prms.wage_growth_rates() == []
|
162
159
|
prms.set_year(2010)
|
163
160
|
assert prms.current_year == 2010
|
164
161
|
with pytest.raises(paramtools.ValidationError):
|
@@ -189,24 +186,17 @@ def test_json_file_contents(tests_path, fname):
|
|
189
186
|
"""
|
190
187
|
Check contents of JSON parameter files in Tax-Calculator/taxcalc directory.
|
191
188
|
"""
|
189
|
+
# pylint: disable=too-many-locals,too-many-branches
|
192
190
|
first_year = Policy.JSON_START_YEAR
|
193
191
|
last_known_year = Policy.LAST_KNOWN_YEAR # for indexed parameter values
|
194
192
|
known_years = set(range(first_year, last_known_year + 1))
|
195
|
-
long_params = ['II_brk1', 'II_brk2', 'II_brk3', 'II_brk4',
|
196
|
-
'II_brk5', 'II_brk6', 'II_brk7',
|
197
|
-
'PT_brk1', 'PT_brk2', 'PT_brk3', 'PT_brk4',
|
198
|
-
'PT_brk5', 'PT_brk6', 'PT_brk7',
|
199
|
-
'PT_qbid_taxinc_thd',
|
200
|
-
'ALD_BusinessLosses_c',
|
201
|
-
'STD', 'II_em', 'II_em_ps',
|
202
|
-
'AMT_em', 'AMT_em_ps', 'AMT_em_pe',
|
203
|
-
'ID_ps', 'ID_AllTaxes_c']
|
204
193
|
# for TCJA-reverting long_params
|
205
194
|
long_known_years = set(range(first_year, last_known_year + 1))
|
206
195
|
long_known_years.add(2026)
|
207
196
|
# check elements in each parameter sub-dictionary
|
208
197
|
failures = ''
|
209
|
-
|
198
|
+
path = os.path.join(tests_path, "..", fname)
|
199
|
+
with open(path, 'r', encoding='utf-8') as f:
|
210
200
|
allparams = json.loads(f.read())
|
211
201
|
for pname in allparams:
|
212
202
|
if pname == "schema":
|
@@ -219,24 +209,29 @@ def test_json_file_contents(tests_path, fname):
|
|
219
209
|
assert param.get('indexed', False) is False
|
220
210
|
# check that indexable is True when indexed is True
|
221
211
|
if param.get('indexed', False) and not param.get('indexable', False):
|
222
|
-
msg =
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
212
|
+
msg = (
|
213
|
+
f'param:<{pname}>; '
|
214
|
+
f'indexed={param.get("indexed", False)}; '
|
215
|
+
f'indexable={param.get("indexable", False)}\n'
|
216
|
+
)
|
217
|
+
failures += msg
|
227
218
|
# check that indexable param has value_type float
|
228
219
|
if param.get('indexable', False) and param['type'] != 'float':
|
229
|
-
msg =
|
230
|
-
|
231
|
-
|
232
|
-
|
220
|
+
msg = (
|
221
|
+
f'param:<{pname}>; '
|
222
|
+
f'type={param["type"]}; '
|
223
|
+
f'indexable={param.get("indexable", False)}\n'
|
224
|
+
)
|
225
|
+
failures += msg
|
233
226
|
# ensure that indexable is False when value_type is not real
|
234
227
|
if param.get('indexable', False) and param['type'] != 'float':
|
235
|
-
msg =
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
228
|
+
msg = (
|
229
|
+
f'param:<{pname}>; '
|
230
|
+
f'indexable={param.get("indexable", False)}; '
|
231
|
+
f'type={param["value_type"]}\n'
|
232
|
+
)
|
233
|
+
failures += msg
|
234
|
+
o = None
|
240
235
|
if fname == "consumption.json":
|
241
236
|
o = Consumption()
|
242
237
|
elif fname == "policy_current_law.json":
|
@@ -251,9 +246,8 @@ def test_json_file_contents(tests_path, fname):
|
|
251
246
|
for y in known_years:
|
252
247
|
o.set_year(y)
|
253
248
|
if np.isnan(getattr(o, param)).any():
|
254
|
-
msg = 'param:<{}>; not found in year={}'
|
255
|
-
|
256
|
-
failures += fail + '\n'
|
249
|
+
msg = f'param:<{param}>; not found in year={y}\n'
|
250
|
+
failures += msg
|
257
251
|
if failures:
|
258
252
|
raise ValueError(failures)
|
259
253
|
|
@@ -268,29 +262,29 @@ def test_parameters_mentioned(tests_path, jfname, pfname):
|
|
268
262
|
"""
|
269
263
|
# read JSON parameter file into a dictionary
|
270
264
|
path = os.path.join(tests_path, '..', jfname)
|
271
|
-
|
272
|
-
|
273
|
-
pfile.close()
|
265
|
+
with open(path, 'r', encoding='utf-8') as pfile:
|
266
|
+
allparams = json.load(pfile)
|
274
267
|
assert isinstance(allparams, dict)
|
275
268
|
# read PYTHON code file text
|
269
|
+
# pylint: disable=consider-using-join
|
276
270
|
if pfname == 'consumption.py':
|
277
271
|
# consumption.py does not explicitly name the parameters
|
278
272
|
code_text = ''
|
279
273
|
for var in Consumption.RESPONSE_VARS:
|
280
|
-
code_text += 'MPC_{}\n'
|
274
|
+
code_text += f'MPC_{var}\n'
|
281
275
|
for var in Consumption.BENEFIT_VARS:
|
282
|
-
code_text += 'BEN_{}_value\n'
|
276
|
+
code_text += f'BEN_{var}_value\n'
|
283
277
|
elif pfname == 'growdiff.py':
|
284
278
|
# growdiff.py does not explicitly name the parameters
|
285
279
|
code_text = ''
|
286
280
|
for var in GrowFactors.VALID_NAMES:
|
287
|
-
code_text += '{}\n'
|
281
|
+
code_text += f'{var}\n'
|
288
282
|
else:
|
289
283
|
# parameters are explicitly named in PYTHON file
|
290
284
|
path = os.path.join(tests_path, '..', pfname)
|
291
|
-
|
292
|
-
|
293
|
-
|
285
|
+
with open(path, 'r', encoding='utf-8') as pfile:
|
286
|
+
code_text = pfile.read()
|
287
|
+
# pylint: enable=consider-using-join
|
294
288
|
# check that each param (without leading _) is mentioned in code text
|
295
289
|
for pname in allparams:
|
296
290
|
if pname == "schema":
|
@@ -301,6 +295,7 @@ def test_parameters_mentioned(tests_path, jfname, pfname):
|
|
301
295
|
# following tests access private methods, so pylint: disable=protected-access
|
302
296
|
|
303
297
|
class ArrayParams(Parameters):
|
298
|
+
"""ArrayParams class"""
|
304
299
|
defaults = {
|
305
300
|
"schema": {
|
306
301
|
"labels": {
|
@@ -407,7 +402,7 @@ class ArrayParams(Parameters):
|
|
407
402
|
self._update(revision, print_warnings, raise_errors)
|
408
403
|
|
409
404
|
def set_rates(self):
|
410
|
-
|
405
|
+
"""Method docstring"""
|
411
406
|
|
412
407
|
|
413
408
|
def test_expand_xd_errors():
|
@@ -420,6 +415,7 @@ def test_expand_xd_errors():
|
|
420
415
|
|
421
416
|
|
422
417
|
def test_expand_empty():
|
418
|
+
"""Test docstring"""
|
423
419
|
params = ArrayParams(label_to_extend=None, array_first=False)
|
424
420
|
params.sort_values()
|
425
421
|
one_dim = copy.deepcopy(params.one_dim)
|
@@ -431,6 +427,7 @@ def test_expand_empty():
|
|
431
427
|
|
432
428
|
|
433
429
|
def test_expand_1d_scalar():
|
430
|
+
"""Test docstring"""
|
434
431
|
yrs = 12
|
435
432
|
val = 10.0
|
436
433
|
exp = np.array([val * math.pow(1.02, i) for i in range(0, yrs)])
|
@@ -453,7 +450,6 @@ def test_expand_2d_short_array():
|
|
453
450
|
"""
|
454
451
|
One of several _expand_?D tests.
|
455
452
|
"""
|
456
|
-
ary = np.array([[1., 2., 3.]])
|
457
453
|
val = np.array([1., 2., 3.])
|
458
454
|
exp2 = np.array([val * math.pow(1.02, i) for i in range(1, 5)])
|
459
455
|
exp1 = np.array([1., 2., 3.])
|
@@ -563,19 +559,19 @@ def test_expand_2d_partial_expand():
|
|
563
559
|
assert np.allclose(res, exp, atol=0.01, rtol=0.0)
|
564
560
|
|
565
561
|
|
566
|
-
|
562
|
+
TAXCALC_REVISION = """
|
567
563
|
{
|
568
564
|
"consumption": {"BEN_mcaid_value": {"2013": 0.9}}
|
569
565
|
}
|
570
566
|
"""
|
571
567
|
|
572
|
-
|
568
|
+
PARAMTOOLS_REVISION = """
|
573
569
|
{
|
574
570
|
"consumption": {"BEN_mcaid_value": [{"year": "2013", "value": 0.9}]}
|
575
571
|
}
|
576
572
|
"""
|
577
573
|
|
578
|
-
|
574
|
+
PARAMTOOLS_REVISION2 = """
|
579
575
|
{
|
580
576
|
"consumption": {"BEN_mcaid_value": 0.9}
|
581
577
|
}
|
@@ -583,8 +579,8 @@ paramtools_revision2 = """
|
|
583
579
|
|
584
580
|
|
585
581
|
@pytest.mark.parametrize("good_revision", [
|
586
|
-
|
587
|
-
|
582
|
+
TAXCALC_REVISION,
|
583
|
+
PARAMTOOLS_REVISION,
|
588
584
|
])
|
589
585
|
def test_read_json_revision(good_revision):
|
590
586
|
"""
|
@@ -593,7 +589,7 @@ def test_read_json_revision(good_revision):
|
|
593
589
|
# pllint: disable=private-method
|
594
590
|
with pytest.raises(TypeError):
|
595
591
|
# error because first obj argument is neither None nor a string
|
596
|
-
Parameters._read_json_revision(
|
592
|
+
Parameters._read_json_revision([], '')
|
597
593
|
with pytest.raises(ValueError):
|
598
594
|
# error because second topkey argument must be a string
|
599
595
|
Parameters._read_json_revision(good_revision, 999)
|
@@ -603,9 +599,9 @@ def test_read_json_revision(good_revision):
|
|
603
599
|
|
604
600
|
|
605
601
|
@pytest.mark.parametrize("params,is_paramtools", [
|
606
|
-
(
|
607
|
-
(
|
608
|
-
(
|
602
|
+
(TAXCALC_REVISION, False),
|
603
|
+
(PARAMTOOLS_REVISION, True),
|
604
|
+
(PARAMTOOLS_REVISION2, True),
|
609
605
|
])
|
610
606
|
def test_read_json_revision_foramts(params, is_paramtools):
|
611
607
|
"""
|
taxcalc/tests/test_policy.py
CHANGED
@@ -13,8 +13,7 @@ import json
|
|
13
13
|
import numpy as np
|
14
14
|
import pytest
|
15
15
|
import paramtools as pt
|
16
|
-
|
17
|
-
from taxcalc import Policy
|
16
|
+
from taxcalc.policy import Policy
|
18
17
|
|
19
18
|
|
20
19
|
def cmp_policy_objs(pol1, pol2, year_range=None, exclude=None):
|
@@ -30,7 +29,7 @@ def cmp_policy_objs(pol1, pol2, year_range=None, exclude=None):
|
|
30
29
|
else:
|
31
30
|
pol1.clear_state()
|
32
31
|
pol2.clear_state()
|
33
|
-
for param in pol1._data:
|
32
|
+
for param in pol1._data: # pylint: disable=protected-access
|
34
33
|
if exclude and param in exclude:
|
35
34
|
continue
|
36
35
|
v1 = getattr(pol1, param)
|
@@ -247,9 +246,9 @@ def test_multi_year_reform():
|
|
247
246
|
Test multi-year reform involving 1D and 2D parameters.
|
248
247
|
"""
|
249
248
|
# specify dimensions of policy Policy object
|
250
|
-
syr = Policy.JSON_START_YEAR
|
251
|
-
nyrs = Policy.DEFAULT_NUM_YEARS
|
252
249
|
pol = Policy()
|
250
|
+
syr = pol.start_year
|
251
|
+
nyrs = pol.num_years
|
253
252
|
iratelist = pol.inflation_rates()
|
254
253
|
ifactor = {}
|
255
254
|
for i in range(0, nyrs):
|
@@ -562,7 +561,7 @@ def test_order_of_indexing_and_level_reforms():
|
|
562
561
|
make no difference to the post-reform policy parameter values.
|
563
562
|
"""
|
564
563
|
# specify two reforms that raises the MTE and stops its indexing in 2015
|
565
|
-
|
564
|
+
reforms = [
|
566
565
|
{
|
567
566
|
'SS_Earnings_c': {2015: 500000},
|
568
567
|
'SS_Earnings_c-indexed': {2015: False}
|
@@ -577,17 +576,17 @@ def test_order_of_indexing_and_level_reforms():
|
|
577
576
|
ppo = [Policy(), Policy()]
|
578
577
|
# apply reforms to corresponding Policy object & check post-reform values
|
579
578
|
syr = Policy.JSON_START_YEAR
|
580
|
-
for
|
579
|
+
for idx, reform in enumerate(reforms):
|
581
580
|
# confirm pre-reform MTE values in 2014-2017
|
582
|
-
mte = ppo[
|
581
|
+
mte = ppo[idx]._SS_Earnings_c
|
583
582
|
assert mte[2014 - syr] == 117000
|
584
583
|
assert mte[2015 - syr] == 118500
|
585
584
|
assert mte[2016 - syr] == 118500
|
586
585
|
assert mte[2017 - syr] < 500000
|
587
586
|
# implement reform in 2015
|
588
|
-
ppo[
|
587
|
+
ppo[idx].implement_reform(reform)
|
589
588
|
# confirm post-reform MTE values in 2014-2017
|
590
|
-
mte = ppo[
|
589
|
+
mte = ppo[idx]._SS_Earnings_c
|
591
590
|
assert mte[2014 - syr] == 117000
|
592
591
|
assert mte[2015 - syr] == 500000
|
593
592
|
assert mte[2016 - syr] == 500000
|
@@ -765,7 +764,7 @@ def test_section_titles(tests_path):
|
|
765
764
|
md_text = md_file.read()
|
766
765
|
md_dict = generate_section_dictionary(md_text)
|
767
766
|
# ... make sure every md_dict section title is in valid_dict
|
768
|
-
for sec1title,secdict in md_dict.items():
|
767
|
+
for sec1title, secdict in md_dict.items():
|
769
768
|
assert isinstance(secdict, dict)
|
770
769
|
assert sec1title in valid_dict
|
771
770
|
for sec2title in secdict:
|
@@ -1332,7 +1331,7 @@ def test_multiple_cpi_swaps2():
|
|
1332
1331
|
{"year": 2018, "value": 500000},
|
1333
1332
|
],
|
1334
1333
|
"SS_Earnings_c-indexed": [
|
1335
|
-
|
1334
|
+
{"year": 2017, "value": False},
|
1336
1335
|
{"year": 2019, "value": True},
|
1337
1336
|
],
|
1338
1337
|
"AMT_em-indexed": [
|
@@ -1417,6 +1416,9 @@ def test_multiple_cpi_swaps2():
|
|
1417
1416
|
)
|
1418
1417
|
|
1419
1418
|
|
1419
|
+
# pylint: disable=invalid-name
|
1420
|
+
|
1421
|
+
|
1420
1422
|
def test_adj_CPI_offset_and_index_status():
|
1421
1423
|
"""
|
1422
1424
|
Test changing parameter_indexing_CPI_offset and another
|
@@ -1540,8 +1542,8 @@ def test_two_sets_of_tax_brackets():
|
|
1540
1542
|
Test that II_brk? and PT_brk? values are the same under current law.
|
1541
1543
|
"""
|
1542
1544
|
pol = Policy()
|
1543
|
-
brackets = range(1, 7+1)
|
1544
|
-
years = range(Policy.JSON_START_YEAR, Policy.LAST_KNOWN_YEAR+1)
|
1545
|
+
brackets = range(1, 7 + 1)
|
1546
|
+
years = range(Policy.JSON_START_YEAR, Policy.LAST_KNOWN_YEAR + 1)
|
1545
1547
|
emsg = ''
|
1546
1548
|
for year in years:
|
1547
1549
|
pol.set_year(year)
|
@@ -11,8 +11,9 @@ import copy
|
|
11
11
|
import numpy as np
|
12
12
|
import pandas as pd
|
13
13
|
import pytest
|
14
|
-
|
15
|
-
from taxcalc import
|
14
|
+
from taxcalc.policy import Policy
|
15
|
+
from taxcalc.records import Records
|
16
|
+
from taxcalc.calculator import Calculator
|
16
17
|
|
17
18
|
|
18
19
|
def create_base_table(test_path):
|
@@ -60,10 +61,10 @@ def create_base_table(test_path):
|
|
60
61
|
read_vars = list(records_varinfo.USABLE_READ_VARS - unused_var_set)
|
61
62
|
# get read variable information from JSON file
|
62
63
|
rec_vars_path = os.path.join(test_path, '..', 'records_variables.json')
|
63
|
-
with open(rec_vars_path) as rvfile:
|
64
|
+
with open(rec_vars_path, 'r', encoding='utf-8') as rvfile:
|
64
65
|
read_var_dict = json.load(rvfile)
|
65
66
|
# create table_dict with sorted read vars followed by sorted calc vars
|
66
|
-
table_dict =
|
67
|
+
table_dict = {}
|
67
68
|
for var in sorted(read_vars):
|
68
69
|
if "taxdata_puf" in read_var_dict['read'][var]['availability']:
|
69
70
|
table_dict[var] = read_var_dict['read'][var]['desc']
|
@@ -85,14 +86,14 @@ def calculate_corr_stats(calc, table):
|
|
85
86
|
errmsg = ''
|
86
87
|
for varname1 in table.index:
|
87
88
|
var1 = calc.array(varname1)
|
88
|
-
var1_cc =
|
89
|
+
var1_cc = []
|
89
90
|
for varname2 in table.index:
|
90
91
|
var2 = calc.array(varname2)
|
91
92
|
try:
|
92
93
|
cor = np.corrcoef(var1, var2)[0][1]
|
93
94
|
except FloatingPointError:
|
94
|
-
msg = 'corr-coef error for {} and {}\n'
|
95
|
-
errmsg += msg
|
95
|
+
msg = f'corr-coef error for {varname1} and {varname2}\n'
|
96
|
+
errmsg += msg
|
96
97
|
cor = 9.99 # because could not compute it
|
97
98
|
var1_cc.append(cor)
|
98
99
|
table[varname1] = var1_cc
|
@@ -105,7 +106,7 @@ def calculate_mean_stats(calc, table, year):
|
|
105
106
|
Calculate weighted means for year.
|
106
107
|
"""
|
107
108
|
total_weight = calc.total_weight()
|
108
|
-
means =
|
109
|
+
means = []
|
109
110
|
for varname in table.index:
|
110
111
|
wmean = calc.weighted_total(varname) / total_weight
|
111
112
|
means.append(wmean)
|
@@ -131,14 +132,13 @@ def differences(new_filename, old_filename, stat_kind):
|
|
131
132
|
if diffs:
|
132
133
|
new_name = os.path.basename(new_filename)
|
133
134
|
old_name = os.path.basename(old_filename)
|
134
|
-
msg = '{} RESULTS DIFFER:\n'
|
135
|
+
msg = f'{stat_kind} RESULTS DIFFER:\n'
|
135
136
|
msg += '-------------------------------------------------'
|
136
137
|
msg += '-------------\n'
|
137
|
-
msg += '--- NEW RESULTS IN {} FILE ---\n'
|
138
|
-
msg += '--- if new OK, copy {} to
|
139
|
-
msg += '--- {}
|
140
|
-
msg += '--- and rerun test.
|
141
|
-
msg += ' ---\n'
|
138
|
+
msg += f'--- NEW RESULTS IN {new_name} FILE ---\n'
|
139
|
+
msg += f'--- if new OK, copy {new_name} to\n'
|
140
|
+
msg += f'--- {old_name} \n'
|
141
|
+
msg += '--- and rerun test. '
|
142
142
|
msg += '-------------------------------------------------'
|
143
143
|
msg += '-------------\n'
|
144
144
|
else:
|
taxcalc/tests/test_pufcsv.py
CHANGED
@@ -8,8 +8,6 @@ and from the Census CPS file for the corresponding year. If you have
|
|
8
8
|
acquired from IRS the most recent SOI PUF file and want to execute
|
9
9
|
this program, contact the Tax-Calculator development team to discuss
|
10
10
|
your options.
|
11
|
-
|
12
|
-
Read Tax-Calculator/TESTING.md for details.
|
13
11
|
"""
|
14
12
|
# CODING-STYLE CHECKS:
|
15
13
|
# pycodestyle test_pufcsv.py
|
@@ -20,8 +18,9 @@ import json
|
|
20
18
|
import pytest
|
21
19
|
import numpy as np
|
22
20
|
import pandas as pd
|
23
|
-
|
24
|
-
from taxcalc import
|
21
|
+
from taxcalc.policy import Policy
|
22
|
+
from taxcalc.records import Records
|
23
|
+
from taxcalc.calculator import Calculator
|
25
24
|
|
26
25
|
|
27
26
|
START_YEAR = 2017
|
@@ -57,7 +56,7 @@ def test_agg(tests_path, puf_fullsample):
|
|
57
56
|
if not np.allclose(adt[icol].values, edt[str(icol)].values):
|
58
57
|
diffs = True
|
59
58
|
if diffs:
|
60
|
-
new_filename = '{
|
59
|
+
new_filename = f'{aggres_path[:-10]}actual.csv'
|
61
60
|
adt.to_csv(new_filename, float_format='%.1f')
|
62
61
|
msg = 'PUFCSV AGG RESULTS DIFFER FOR FULL-SAMPLE\n'
|
63
62
|
msg += '-------------------------------------------------\n'
|
@@ -118,27 +117,32 @@ def mtr_bin_counts(mtr_data, bin_edges, recid):
|
|
118
117
|
res = ''
|
119
118
|
(bincount, _) = np.histogram(mtr_data.round(decimals=4), bins=bin_edges)
|
120
119
|
sum_bincount = np.sum(bincount)
|
121
|
-
res += '{} :'
|
120
|
+
res += f'{sum_bincount} :'
|
122
121
|
for idx in range(len(bin_edges) - 1):
|
123
|
-
res += ' {:6d}'
|
122
|
+
res += f' {bincount[idx]:6d}'
|
124
123
|
res += '\n'
|
125
124
|
if sum_bincount < mtr_data.size:
|
126
125
|
res += 'WARNING: sum of bin counts is too low\n'
|
127
|
-
recinfo = ' mtr={:.2f} for recid={}\n'
|
128
126
|
mtr_min = mtr_data.min()
|
129
127
|
mtr_max = mtr_data.max()
|
130
128
|
bin_min = min(bin_edges)
|
131
129
|
bin_max = max(bin_edges)
|
132
130
|
if mtr_min < bin_min:
|
133
|
-
res += ' min(mtr)={:.2f}\n'
|
131
|
+
res += f' min(mtr)={mtr_min:.2f}\n'
|
134
132
|
for idx in range(mtr_data.size):
|
135
133
|
if mtr_data[idx] < bin_min:
|
136
|
-
res +=
|
134
|
+
res += (
|
135
|
+
f' mtr={mtr_data[idx]:.2f} '
|
136
|
+
f'for recid={recid[idx]}\n'
|
137
|
+
)
|
137
138
|
if mtr_max > bin_max:
|
138
|
-
res += ' max(mtr)={:.2f}\n'
|
139
|
+
res += f' max(mtr)={mtr_max:.2f}\n'
|
139
140
|
for idx in range(mtr_data.size):
|
140
141
|
if mtr_data[idx] > bin_max:
|
141
|
-
res +=
|
142
|
+
res += (
|
143
|
+
f' mtr={mtr_data[idx]:.2f} '
|
144
|
+
f'for recid={recid[idx]}\n'
|
145
|
+
)
|
142
146
|
return res
|
143
147
|
|
144
148
|
|
@@ -169,24 +173,20 @@ def nonsmall_diffs(linelist1, linelist2, small=0.0):
|
|
169
173
|
for line1, line2 in zip(linelist1, linelist2):
|
170
174
|
if line1 == line2:
|
171
175
|
continue
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
if
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
else:
|
187
|
-
return True
|
188
|
-
else:
|
189
|
-
return True
|
176
|
+
tokens1 = line1.replace(',', '').split()
|
177
|
+
tokens2 = line2.replace(',', '').split()
|
178
|
+
for tok1, tok2 in zip(tokens1, tokens2):
|
179
|
+
tok1_isfloat = isfloat(tok1)
|
180
|
+
tok2_isfloat = isfloat(tok2)
|
181
|
+
if tok1_isfloat and tok2_isfloat:
|
182
|
+
if abs(float(tok1) - float(tok2)) <= smallamt:
|
183
|
+
continue
|
184
|
+
return True
|
185
|
+
if not tok1_isfloat and not tok2_isfloat:
|
186
|
+
if tok1 == tok2:
|
187
|
+
continue
|
188
|
+
return True
|
189
|
+
return True
|
190
190
|
return False
|
191
191
|
|
192
192
|
|
@@ -207,7 +207,7 @@ def test_mtr(tests_path, puf_path):
|
|
207
207
|
res += 'MTR computed using NEGATIVE finite_diff '
|
208
208
|
else:
|
209
209
|
res += 'MTR computed using POSITIVE finite_diff '
|
210
|
-
res += 'for tax year {}\n'
|
210
|
+
res += f'for tax year {MTR_TAX_YEAR}\n'
|
211
211
|
# create a Policy object (clp) containing current-law policy parameters
|
212
212
|
clp = Policy()
|
213
213
|
clp.set_year(MTR_TAX_YEAR)
|
@@ -216,15 +216,15 @@ def test_mtr(tests_path, puf_path):
|
|
216
216
|
recid = puf.RECID # pylint: disable=no-member
|
217
217
|
# create a Calculator object using clp policy and puf records
|
218
218
|
calc = Calculator(policy=clp, records=puf)
|
219
|
-
res += '
|
219
|
+
res += f'Total number of data records = {puf.array_length}\n'
|
220
220
|
res += 'PTAX mtr histogram bin edges:\n'
|
221
|
-
res += ' {}\n'
|
221
|
+
res += f' {PTAX_MTR_BIN_EDGES}\n'
|
222
222
|
res += 'ITAX mtr histogram bin edges:\n'
|
223
|
-
res += ' {}\n'
|
223
|
+
res += f' {ITAX_MTR_BIN_EDGES}\n'
|
224
224
|
variable_header = 'PTAX and ITAX mtr histogram bin counts for'
|
225
225
|
# compute marginal tax rate (mtr) histograms for each mtr variable
|
226
226
|
for var_str in Calculator.MTR_VALID_VARIABLES:
|
227
|
-
zero_out =
|
227
|
+
zero_out = var_str == 'e01400'
|
228
228
|
(mtr_ptax, mtr_itax, _) = calc.mtr(variable_str=var_str,
|
229
229
|
negative_finite_diff=MTR_NEG_DIFF,
|
230
230
|
zero_out_calculated_vars=zero_out,
|
@@ -250,17 +250,17 @@ def test_mtr(tests_path, puf_path):
|
|
250
250
|
# only MARS==2 filing units have valid MTR values
|
251
251
|
mtr_ptax = mtr_ptax[calc.array('MARS') == 2]
|
252
252
|
mtr_itax = mtr_itax[calc.array('MARS') == 2]
|
253
|
-
res += '{} {}:\n'
|
253
|
+
res += f'{variable_header} {var_str}:\n'
|
254
254
|
res += mtr_bin_counts(mtr_ptax, PTAX_MTR_BIN_EDGES, recid)
|
255
255
|
res += mtr_bin_counts(mtr_itax, ITAX_MTR_BIN_EDGES, recid)
|
256
256
|
# check for differences between actual and expected results
|
257
257
|
mtrres_path = os.path.join(tests_path, 'pufcsv_mtr_expect.txt')
|
258
|
-
with open(mtrres_path, 'r') as expected_file:
|
258
|
+
with open(mtrres_path, 'r', encoding='utf-8') as expected_file:
|
259
259
|
txt = expected_file.read()
|
260
260
|
expected_results = txt.rstrip('\n\t ') + '\n' # cleanup end of file txt
|
261
261
|
if nonsmall_diffs(res.splitlines(True), expected_results.splitlines(True)):
|
262
|
-
new_filename = '{
|
263
|
-
with open(new_filename, 'w') as new_file:
|
262
|
+
new_filename = f'{mtrres_path[:-10]}actual.txt'
|
263
|
+
with open(new_filename, 'w', encoding='utf-8') as new_file:
|
264
264
|
new_file.write(res)
|
265
265
|
msg = 'PUFCSV MTR RESULTS DIFFER\n'
|
266
266
|
msg += '-------------------------------------------------\n'
|
@@ -372,7 +372,7 @@ def test_puf_availability(tests_path, puf_path):
|
|
372
372
|
pufvars = set(list(pufdf))
|
373
373
|
# make set of variable names that are marked as puf.csv available
|
374
374
|
rvpath = os.path.join(tests_path, '..', 'records_variables.json')
|
375
|
-
with open(rvpath, 'r') as rvfile:
|
375
|
+
with open(rvpath, 'r', encoding='utf-8') as rvfile:
|
376
376
|
rvdict = json.load(rvfile)
|
377
377
|
recvars = set()
|
378
378
|
for vname, vdict in rvdict['read'].items():
|