taxcalc 4.2.1__py3-none-any.whl → 4.2.2__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/assumptions/ASSUMPTIONS.md +53 -0
- taxcalc/assumptions/README.md +17 -0
- taxcalc/assumptions/economic_assumptions_template.json +77 -0
- taxcalc/calcfunctions.py +7 -4
- taxcalc/data.py +10 -5
- taxcalc/policy_current_law.json +2033 -184
- taxcalc/reforms/2017_law.json +125 -0
- taxcalc/reforms/2017_law.out.csv +10 -0
- taxcalc/reforms/ARPA.json +78 -0
- taxcalc/reforms/ARPA.out.csv +10 -0
- taxcalc/reforms/BrownKhanna.json +23 -0
- taxcalc/reforms/BrownKhanna.out.csv +10 -0
- taxcalc/reforms/CARES.json +40 -0
- taxcalc/reforms/CARES.out.csv +10 -0
- taxcalc/reforms/ConsolidatedAppropriationsAct2021.json +15 -0
- taxcalc/reforms/ConsolidatedAppropriationsAct2021.out.csv +10 -0
- taxcalc/reforms/Larson2019.json +36 -0
- taxcalc/reforms/Larson2019.out.csv +10 -0
- taxcalc/reforms/README.md +22 -0
- taxcalc/reforms/REFORMS.md +92 -0
- taxcalc/reforms/Renacci.json +61 -0
- taxcalc/reforms/Renacci.out.csv +10 -0
- taxcalc/reforms/SandersDeFazio.json +15 -0
- taxcalc/reforms/SandersDeFazio.out.csv +10 -0
- taxcalc/reforms/TCJA.json +160 -0
- taxcalc/reforms/TCJA.md +48 -0
- taxcalc/reforms/TCJA.out.csv +10 -0
- taxcalc/reforms/Trump2016.json +71 -0
- taxcalc/reforms/Trump2016.out.csv +10 -0
- taxcalc/reforms/Trump2017.json +51 -0
- taxcalc/reforms/Trump2017.out.csv +10 -0
- taxcalc/reforms/archive/Clinton2016.json +56 -0
- taxcalc/reforms/archive/RyanBrady.json +104 -0
- taxcalc/reforms/archive/TCJA_House.json +144 -0
- taxcalc/reforms/archive/TCJA_House_Amended.json +152 -0
- taxcalc/reforms/archive/TCJA_Reconciliation.json +187 -0
- taxcalc/reforms/archive/TCJA_Senate.json +116 -0
- taxcalc/reforms/archive/TCJA_Senate_111417.json +169 -0
- taxcalc/reforms/archive/TCJA_Senate_120117.json +174 -0
- taxcalc/reforms/cases.csv +10 -0
- taxcalc/reforms/clp.out.csv +10 -0
- taxcalc/reforms/ext.json +59 -0
- taxcalc/reforms/growfactors_ext.csv +65 -0
- taxcalc/reforms/ptaxes0.json +37 -0
- taxcalc/reforms/ptaxes0.out.csv +10 -0
- taxcalc/reforms/ptaxes1.json +21 -0
- taxcalc/reforms/ptaxes1.out.csv +10 -0
- taxcalc/reforms/ptaxes2.json +18 -0
- taxcalc/reforms/ptaxes2.out.csv +10 -0
- taxcalc/reforms/ptaxes3.json +28 -0
- taxcalc/reforms/ptaxes3.out.csv +10 -0
- taxcalc/reforms/rounding2022.json +153 -0
- taxcalc/reforms/rounding2022.out.csv +10 -0
- taxcalc/tests/benefits_expect.csv +169 -0
- taxcalc/tests/cmpi_cps_expect.txt +132 -0
- taxcalc/tests/cmpi_puf_expect.txt +132 -0
- taxcalc/tests/conftest.py +143 -0
- taxcalc/tests/cpscsv_agg_expect.csv +26 -0
- taxcalc/tests/puf_var_correl_coeffs_2016.csv +80 -0
- taxcalc/tests/puf_var_wght_means_by_year.csv +80 -0
- taxcalc/tests/pufcsv_agg_expect.csv +26 -0
- taxcalc/tests/pufcsv_mtr_expect.txt +63 -0
- taxcalc/tests/reforms.json +649 -0
- taxcalc/tests/reforms_expect.csv +65 -0
- taxcalc/tests/test_4package.py +67 -0
- taxcalc/tests/test_benefits.py +86 -0
- taxcalc/tests/test_calcfunctions.py +871 -0
- taxcalc/tests/test_calculator.py +1021 -0
- taxcalc/tests/test_compare.py +336 -0
- taxcalc/tests/test_compatible_data.py +338 -0
- taxcalc/tests/test_consumption.py +144 -0
- taxcalc/tests/test_cpscsv.py +163 -0
- taxcalc/tests/test_data.py +133 -0
- taxcalc/tests/test_decorators.py +332 -0
- taxcalc/tests/test_growdiff.py +102 -0
- taxcalc/tests/test_growfactors.py +94 -0
- taxcalc/tests/test_parameters.py +617 -0
- taxcalc/tests/test_policy.py +1575 -0
- taxcalc/tests/test_puf_var_stats.py +194 -0
- taxcalc/tests/test_pufcsv.py +385 -0
- taxcalc/tests/test_records.py +234 -0
- taxcalc/tests/test_reforms.py +385 -0
- taxcalc/tests/test_responses.py +41 -0
- taxcalc/tests/test_taxcalcio.py +755 -0
- taxcalc/tests/test_tmdcsv.py +38 -0
- taxcalc/tests/test_utils.py +792 -0
- taxcalc/tmd_growfactors.csv +54 -54
- taxcalc/tmd_weights.csv.gz +0 -0
- taxcalc/validation/CSV_INPUT_VARS.md +29 -0
- taxcalc/validation/CSV_OUTPUT_VARS.md +63 -0
- taxcalc/validation/README.md +68 -0
- taxcalc/validation/taxsim35/Differences_Explained.md +54 -0
- taxcalc/validation/taxsim35/README.md +139 -0
- taxcalc/validation/taxsim35/expected_differences/a17-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/a18-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/a19-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/a20-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/a21-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/b17-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/b18-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/b19-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/b20-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/b21-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/c17-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/c18-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/expected_differences/c19-taxdiffs-expect.csv +25 -0
- taxcalc/validation/taxsim35/input_setup.py +67 -0
- taxcalc/validation/taxsim35/main_comparison.py +183 -0
- taxcalc/validation/taxsim35/prepare_taxcalc_input.py +161 -0
- taxcalc/validation/taxsim35/process_taxcalc_output.py +140 -0
- taxcalc/validation/taxsim35/taxsim_emulation.json +49 -0
- taxcalc/validation/taxsim35/taxsim_input.py +321 -0
- taxcalc/validation/taxsim35/tc_sims.py +98 -0
- taxcalc/validation/taxsim35/tests_35.py +80 -0
- taxcalc/validation/tests_35.sh +13 -0
- {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/METADATA +3 -4
- taxcalc-4.2.2.dist-info/RECORD +144 -0
- {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/WHEEL +1 -1
- taxcalc-4.2.1.dist-info/RECORD +0 -34
- {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/LICENSE +0 -0
- {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/entry_points.txt +0 -0
- {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
# CODING-STYLE CHECKS:
|
2
|
+
# pycodestyle test_consumption.py
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
import paramtools
|
6
|
+
import pytest
|
7
|
+
import copy
|
8
|
+
from taxcalc import Policy, Records, Calculator, Consumption
|
9
|
+
|
10
|
+
|
11
|
+
def test_year_consistency():
|
12
|
+
assert Consumption.JSON_START_YEAR == Policy.JSON_START_YEAR
|
13
|
+
assert Consumption.DEFAULT_NUM_YEARS == Policy.DEFAULT_NUM_YEARS
|
14
|
+
|
15
|
+
|
16
|
+
def test_validity_of_consumption_vars_set():
|
17
|
+
records_varinfo = Records(data=None)
|
18
|
+
assert Consumption.RESPONSE_VARS.issubset(records_varinfo.USABLE_READ_VARS)
|
19
|
+
useable_vars = set(['housing', 'snap', 'tanf', 'vet', 'wic',
|
20
|
+
'mcare', 'mcaid', 'other'])
|
21
|
+
assert Consumption.BENEFIT_VARS.issubset(useable_vars)
|
22
|
+
|
23
|
+
|
24
|
+
def test_update_consumption():
|
25
|
+
consump = Consumption()
|
26
|
+
consump.update_consumption({})
|
27
|
+
revision = {
|
28
|
+
'MPC_e20400': {2014: 0.05,
|
29
|
+
2015: 0.06},
|
30
|
+
'BEN_mcare_value': {2014: 0.75,
|
31
|
+
2015: 0.80}
|
32
|
+
}
|
33
|
+
consump.update_consumption(revision)
|
34
|
+
expected_mpc_e20400 = np.full((Consumption.DEFAULT_NUM_YEARS,), 0.06)
|
35
|
+
expected_mpc_e20400[0] = 0.0
|
36
|
+
expected_mpc_e20400[1] = 0.05
|
37
|
+
assert np.allclose(consump._MPC_e20400,
|
38
|
+
expected_mpc_e20400,
|
39
|
+
rtol=0.0)
|
40
|
+
assert np.allclose(consump._MPC_e17500,
|
41
|
+
np.zeros((Consumption.DEFAULT_NUM_YEARS,)),
|
42
|
+
rtol=0.0)
|
43
|
+
expected_ben_mcare_value = np.full((Consumption.DEFAULT_NUM_YEARS,), 0.80)
|
44
|
+
expected_ben_mcare_value[0] = 1.0
|
45
|
+
expected_ben_mcare_value[1] = 0.75
|
46
|
+
assert np.allclose(consump._BEN_mcare_value,
|
47
|
+
expected_ben_mcare_value,
|
48
|
+
rtol=0.0)
|
49
|
+
assert np.allclose(consump._BEN_snap_value,
|
50
|
+
np.ones((Consumption.DEFAULT_NUM_YEARS,)),
|
51
|
+
rtol=0.0)
|
52
|
+
consump.set_year(2015)
|
53
|
+
assert consump.current_year == 2015
|
54
|
+
assert consump.MPC_e20400 == 0.06
|
55
|
+
assert consump.MPC_e17500 == 0.0
|
56
|
+
assert consump.BEN_mcare_value == 0.80
|
57
|
+
assert consump.BEN_snap_value == 1.0
|
58
|
+
|
59
|
+
|
60
|
+
def test_incorrect_update_consumption():
|
61
|
+
with pytest.raises(paramtools.ValidationError):
|
62
|
+
Consumption().update_consumption([])
|
63
|
+
with pytest.raises(paramtools.ValidationError):
|
64
|
+
Consumption().update_consumption({'MPC_e17500': {'xyz': 0.2}})
|
65
|
+
with pytest.raises(paramtools.ValidationError):
|
66
|
+
Consumption().update_consumption({'MPC_e17500': {2012: 0.2}})
|
67
|
+
with pytest.raises(paramtools.ValidationError):
|
68
|
+
Consumption().update_consumption({'MPC_e17500': {2099: 0.2}})
|
69
|
+
with pytest.raises(paramtools.ValidationError):
|
70
|
+
Consumption().update_consumption({'MPC_exxxxx': {2014: 0.2}})
|
71
|
+
with pytest.raises(paramtools.ValidationError):
|
72
|
+
Consumption().update_consumption({'MPC_e17500': {2014: -0.1}})
|
73
|
+
with pytest.raises(paramtools.ValidationError):
|
74
|
+
Consumption().update_consumption({'MPC_e17500-indexed': {2014: 0.1}})
|
75
|
+
|
76
|
+
|
77
|
+
def test_future_update_consumption():
|
78
|
+
consump = Consumption()
|
79
|
+
assert consump.current_year == consump.start_year
|
80
|
+
assert consump.has_response() is False
|
81
|
+
cyr = 2020
|
82
|
+
consump.set_year(cyr)
|
83
|
+
consump.update_consumption({'MPC_e20400': {cyr: 0.01}})
|
84
|
+
assert consump.current_year == cyr
|
85
|
+
assert consump.has_response() is True
|
86
|
+
consump.set_year(cyr - 1)
|
87
|
+
assert consump.has_response() is False
|
88
|
+
# test future updates for benefits
|
89
|
+
consump_ben = Consumption()
|
90
|
+
assert consump_ben.current_year == consump_ben.start_year
|
91
|
+
assert consump_ben.has_response() is False
|
92
|
+
consump_ben.set_year(cyr)
|
93
|
+
consump_ben.update_consumption({'BEN_vet_value': {cyr: 0.95}})
|
94
|
+
assert consump_ben.current_year == cyr
|
95
|
+
assert consump_ben.has_response() is True
|
96
|
+
consump_ben.set_year(cyr - 1)
|
97
|
+
assert consump_ben.has_response() is False
|
98
|
+
|
99
|
+
|
100
|
+
def test_consumption_default_data():
|
101
|
+
consump = Consumption()
|
102
|
+
pdata = consump.specification(meta_data=True, ignore_state=True)
|
103
|
+
for pname in pdata.keys():
|
104
|
+
if pname.startswith('MPC'):
|
105
|
+
assert pdata[pname]['value'] == [{"value": 0.0, "year": 2013}]
|
106
|
+
elif pname.startswith('BEN'):
|
107
|
+
assert pdata[pname]['value'] == [{"value": 1.0, "year": 2013}]
|
108
|
+
|
109
|
+
|
110
|
+
def test_consumption_response(cps_subsample):
|
111
|
+
consump = Consumption()
|
112
|
+
mpc = 0.5
|
113
|
+
consumption_response = {'MPC_e20400': {2013: mpc}}
|
114
|
+
consump.update_consumption(consumption_response)
|
115
|
+
# test incorrect call to response method
|
116
|
+
with pytest.raises(ValueError):
|
117
|
+
consump.response(list(), 1)
|
118
|
+
# test correct call to response method
|
119
|
+
rec = Records.cps_constructor(data=cps_subsample)
|
120
|
+
pre = copy.deepcopy(rec.e20400)
|
121
|
+
consump.response(rec, 1.0)
|
122
|
+
post = rec.e20400
|
123
|
+
actual_diff = post - pre
|
124
|
+
expected_diff = np.ones(rec.array_length) * mpc
|
125
|
+
assert np.allclose(actual_diff, expected_diff)
|
126
|
+
# compute earnings mtr with no consumption response
|
127
|
+
rec = Records.cps_constructor(data=cps_subsample)
|
128
|
+
ided0 = copy.deepcopy(rec.e20400)
|
129
|
+
calc0 = Calculator(policy=Policy(), records=rec, consumption=None)
|
130
|
+
(mtr0_ptax, mtr0_itax, _) = calc0.mtr(variable_str='e00200p',
|
131
|
+
wrt_full_compensation=False)
|
132
|
+
assert np.allclose(calc0.array('e20400'), ided0)
|
133
|
+
# compute earnings mtr with consumption response
|
134
|
+
calc1 = Calculator(policy=Policy(), records=rec, consumption=consump)
|
135
|
+
mtr1_ptax, mtr1_itax, _ = calc1.mtr(variable_str='e00200p',
|
136
|
+
wrt_full_compensation=False)
|
137
|
+
assert np.allclose(calc1.array('e20400'), ided0)
|
138
|
+
# confirm that payroll mtr values are no different
|
139
|
+
assert np.allclose(mtr1_ptax, mtr0_ptax)
|
140
|
+
# confirm that all mtr with cons-resp are no greater than without cons-resp
|
141
|
+
assert np.all(np.less_equal(np.around(mtr1_itax, decimals=5),
|
142
|
+
np.around(mtr0_itax, decimals=5)))
|
143
|
+
# confirm that some mtr with cons-resp are less than without cons-resp
|
144
|
+
assert np.any(np.less(mtr1_itax, mtr0_itax))
|
@@ -0,0 +1,163 @@
|
|
1
|
+
"""
|
2
|
+
Tests of Tax-Calculator using cps.csv input.
|
3
|
+
|
4
|
+
Note that the CPS-related files that are required to run this program
|
5
|
+
have been constructed by the Tax-Calculator development team from publicly
|
6
|
+
available Census data files. Hence, the CPS-related files are freely
|
7
|
+
available and are part of the Tax-Calculator repository.
|
8
|
+
|
9
|
+
Read Tax-Calculator/TESTING.md for details.
|
10
|
+
"""
|
11
|
+
# CODING-STYLE CHECKS:
|
12
|
+
# pycodestyle test_cpscsv.py
|
13
|
+
# pylint --disable=locally-disabled test_cpscsv.py
|
14
|
+
|
15
|
+
import os
|
16
|
+
import json
|
17
|
+
import pytest
|
18
|
+
import numpy as np
|
19
|
+
import pandas as pd
|
20
|
+
# pylint: disable=import-error
|
21
|
+
from taxcalc import Policy, Records, Calculator
|
22
|
+
|
23
|
+
|
24
|
+
START_YEAR = 2017
|
25
|
+
|
26
|
+
|
27
|
+
@pytest.mark.cpscsv_agg
|
28
|
+
def test_agg(tests_path, cps_fullsample):
|
29
|
+
"""
|
30
|
+
Test current-law aggregate taxes using cps.csv file.
|
31
|
+
"""
|
32
|
+
# pylint: disable=too-many-statements,too-many-locals
|
33
|
+
nyrs = 10
|
34
|
+
# create a baseline Policy object with current-law policy parameters
|
35
|
+
baseline_policy = Policy()
|
36
|
+
# create a Records object (rec) containing all cps.csv input records
|
37
|
+
recs = Records.cps_constructor(data=cps_fullsample)
|
38
|
+
# create a Calculator object using baseline policy and cps records
|
39
|
+
calc = Calculator(policy=baseline_policy, records=recs)
|
40
|
+
calc.advance_to_year(START_YEAR)
|
41
|
+
calc_start_year = calc.current_year
|
42
|
+
# create aggregate diagnostic table (adt) as a Pandas DataFrame object
|
43
|
+
adt = calc.diagnostic_table(nyrs).round(1) # column labels are int
|
44
|
+
taxes_fullsample = adt.loc["Combined Liability ($b)"]
|
45
|
+
# compare actual DataFrame, adt, with the expected DataFrame, edt
|
46
|
+
aggres_path = os.path.join(tests_path, 'cpscsv_agg_expect.csv')
|
47
|
+
edt = pd.read_csv(aggres_path, index_col=False) # column labels are str
|
48
|
+
edt.drop('Unnamed: 0', axis='columns', inplace=True)
|
49
|
+
assert len(adt.columns.values) == len(edt.columns.values)
|
50
|
+
diffs = False
|
51
|
+
for icol in adt.columns.values:
|
52
|
+
if not np.allclose(adt[icol], edt[str(icol)]):
|
53
|
+
diffs = True
|
54
|
+
if diffs:
|
55
|
+
new_filename = '{}{}'.format(aggres_path[:-10], 'actual.csv')
|
56
|
+
adt.to_csv(new_filename, float_format='%.1f')
|
57
|
+
msg = 'CPSCSV AGG RESULTS DIFFER\n'
|
58
|
+
msg += '-------------------------------------------------\n'
|
59
|
+
msg += '--- NEW RESULTS IN cpscsv_agg_actual.csv FILE ---\n'
|
60
|
+
msg += '--- if new OK, copy cpscsv_agg_actual.csv to ---\n'
|
61
|
+
msg += '--- cpscsv_agg_expect.csv ---\n'
|
62
|
+
msg += '--- and rerun test. ---\n'
|
63
|
+
msg += '--- (both are in taxcalc/tests) ---\n'
|
64
|
+
msg += '-------------------------------------------------\n'
|
65
|
+
raise ValueError(msg)
|
66
|
+
# create aggregate diagnostic table using unweighted sub-sample of records
|
67
|
+
rn_seed = 180 # to ensure sub-sample is always the same
|
68
|
+
subfrac = 0.07# 0.03 # sub-sample fraction
|
69
|
+
subsample = cps_fullsample.sample(frac=subfrac, random_state=rn_seed)
|
70
|
+
recs_subsample = Records.cps_constructor(data=subsample)
|
71
|
+
calc_subsample = Calculator(policy=baseline_policy, records=recs_subsample)
|
72
|
+
calc_subsample.advance_to_year(START_YEAR)
|
73
|
+
adt_subsample = calc_subsample.diagnostic_table(nyrs)
|
74
|
+
# compare combined tax liability from full and sub samples for each year
|
75
|
+
taxes_subsample = adt_subsample.loc["Combined Liability ($b)"]
|
76
|
+
print('taxes_submsampe = ', taxes_subsample)
|
77
|
+
print('TAXES full sample = ', taxes_fullsample)
|
78
|
+
msg = ''
|
79
|
+
for cyr in range(calc_start_year, calc_start_year + nyrs):
|
80
|
+
if cyr == calc_start_year:
|
81
|
+
reltol = 0.0232
|
82
|
+
else:
|
83
|
+
reltol = 0.0444
|
84
|
+
if not np.allclose(taxes_subsample[cyr], taxes_fullsample[cyr],
|
85
|
+
atol=0.0, rtol=reltol):
|
86
|
+
reldiff = (taxes_subsample[cyr] / taxes_fullsample[cyr]) - 1.
|
87
|
+
line1 = '\nCPSCSV AGG SUB-vs-FULL RESULTS DIFFER IN {}'
|
88
|
+
line2 = '\n when subfrac={:.3f}, rtol={:.4f}, seed={}'
|
89
|
+
line3 = '\n with sub={:.3f}, full={:.3f}, rdiff={:.4f}'
|
90
|
+
msg += line1.format(cyr)
|
91
|
+
msg += line2.format(subfrac, reltol, rn_seed)
|
92
|
+
msg += line3.format(taxes_subsample[cyr],
|
93
|
+
taxes_fullsample[cyr],
|
94
|
+
reldiff)
|
95
|
+
if msg:
|
96
|
+
raise ValueError(msg)
|
97
|
+
|
98
|
+
|
99
|
+
def test_cps_availability(tests_path, cps_path):
|
100
|
+
"""
|
101
|
+
Cross-check records_variables.json data with variables in cps.csv file.
|
102
|
+
"""
|
103
|
+
cpsdf = pd.read_csv(cps_path)
|
104
|
+
cpsvars = set(list(cpsdf))
|
105
|
+
# make set of variable names that are marked as cps.csv available
|
106
|
+
rvpath = os.path.join(tests_path, '..', 'records_variables.json')
|
107
|
+
with open(rvpath, 'r') as rvfile:
|
108
|
+
rvdict = json.load(rvfile)
|
109
|
+
recvars = set()
|
110
|
+
for vname, vdict in rvdict['read'].items():
|
111
|
+
if 'taxdata_cps' in vdict.get('availability', ''):
|
112
|
+
recvars.add(vname)
|
113
|
+
# check that cpsvars and recvars sets are the same
|
114
|
+
assert (cpsvars - recvars) == set()
|
115
|
+
assert (recvars - cpsvars) == set()
|
116
|
+
|
117
|
+
|
118
|
+
def nonsmall_diffs(linelist1, linelist2, small=0.0):
|
119
|
+
"""
|
120
|
+
Return True if line lists differ significantly; otherwise return False.
|
121
|
+
Significant numerical difference means one or more numbers differ (between
|
122
|
+
linelist1 and linelist2) by more than the specified small amount.
|
123
|
+
"""
|
124
|
+
# embedded function used only in nonsmall_diffs function
|
125
|
+
def isfloat(value):
|
126
|
+
"""
|
127
|
+
Return True if value can be cast to float; otherwise return False.
|
128
|
+
"""
|
129
|
+
try:
|
130
|
+
float(value)
|
131
|
+
return True
|
132
|
+
except ValueError:
|
133
|
+
return False
|
134
|
+
# begin nonsmall_diffs logic
|
135
|
+
assert isinstance(linelist1, list)
|
136
|
+
assert isinstance(linelist2, list)
|
137
|
+
if len(linelist1) != len(linelist2):
|
138
|
+
return True
|
139
|
+
assert 0.0 <= small <= 1.0
|
140
|
+
epsilon = 1e-6
|
141
|
+
smallamt = small + epsilon
|
142
|
+
for line1, line2 in zip(linelist1, linelist2):
|
143
|
+
if line1 == line2:
|
144
|
+
continue
|
145
|
+
else:
|
146
|
+
tokens1 = line1.replace(',', '').split()
|
147
|
+
tokens2 = line2.replace(',', '').split()
|
148
|
+
for tok1, tok2 in zip(tokens1, tokens2):
|
149
|
+
tok1_isfloat = isfloat(tok1)
|
150
|
+
tok2_isfloat = isfloat(tok2)
|
151
|
+
if tok1_isfloat and tok2_isfloat:
|
152
|
+
if abs(float(tok1) - float(tok2)) <= smallamt:
|
153
|
+
continue
|
154
|
+
else:
|
155
|
+
return True
|
156
|
+
elif not tok1_isfloat and not tok2_isfloat:
|
157
|
+
if tok1 == tok2:
|
158
|
+
continue
|
159
|
+
else:
|
160
|
+
return True
|
161
|
+
else:
|
162
|
+
return True
|
163
|
+
return False
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# CODING-STYLE CHECKS:
|
2
|
+
# pycodestyle test_data.py
|
3
|
+
|
4
|
+
import os
|
5
|
+
import tempfile
|
6
|
+
import pytest
|
7
|
+
import numpy as np
|
8
|
+
import pandas as pd
|
9
|
+
from taxcalc import Data, GrowFactors
|
10
|
+
|
11
|
+
|
12
|
+
# Test specification and use of simple Data-derived class.
|
13
|
+
# This derived class is called Recs and it contains aged data.
|
14
|
+
#
|
15
|
+
# The following pytest fixture specifies the VARINFO file for the
|
16
|
+
# Recs class, which is defined in the test_recs_class function.
|
17
|
+
|
18
|
+
|
19
|
+
VARINFO_JSON = """
|
20
|
+
{
|
21
|
+
"read": {
|
22
|
+
"RECID": {
|
23
|
+
"required": true,
|
24
|
+
"type": "int",
|
25
|
+
"desc": "Unique numeric identifier for record"
|
26
|
+
},
|
27
|
+
"MARS": {
|
28
|
+
"required": true,
|
29
|
+
"type": "int",
|
30
|
+
"desc": "Filing (marital) status [1..5]"
|
31
|
+
},
|
32
|
+
"e00300": {
|
33
|
+
"type": "float",
|
34
|
+
"desc": "Taxable interest income"
|
35
|
+
},
|
36
|
+
"s006": {
|
37
|
+
"type": "float",
|
38
|
+
"desc": "Record sampling weight"
|
39
|
+
}
|
40
|
+
},
|
41
|
+
"calc": {
|
42
|
+
"expanded_income": {
|
43
|
+
"type": "float"
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
"""
|
48
|
+
|
49
|
+
|
50
|
+
@pytest.fixture(scope='module', name='recs_varinfo_file')
|
51
|
+
def fixture_recs_varinfo_json_file():
|
52
|
+
"""
|
53
|
+
Define JSON VARINFO file for Data-derived Recs class.
|
54
|
+
"""
|
55
|
+
with tempfile.NamedTemporaryFile(mode='a', delete=False) as pfile:
|
56
|
+
pfile.write(VARINFO_JSON + '\n')
|
57
|
+
pfile.close()
|
58
|
+
yield pfile
|
59
|
+
os.remove(pfile.name)
|
60
|
+
|
61
|
+
|
62
|
+
def test_recs_class(recs_varinfo_file, cps_subsample):
|
63
|
+
"""
|
64
|
+
Specify Data-derived Recs class and test it.
|
65
|
+
"""
|
66
|
+
|
67
|
+
class Recs(Data):
|
68
|
+
"""
|
69
|
+
The Recs class is derived from the abstract base Data class.
|
70
|
+
"""
|
71
|
+
VARINFO_FILE_NAME = recs_varinfo_file.name
|
72
|
+
VARINFO_FILE_PATH = ''
|
73
|
+
|
74
|
+
def __init__(self, data, start_year, gfactors, weights):
|
75
|
+
super().__init__(data, start_year, gfactors, weights)
|
76
|
+
|
77
|
+
def _extrapolate(self, year):
|
78
|
+
self.e00300 *= self.gfactors.factor_value('AINTS', year)
|
79
|
+
|
80
|
+
# test Recs class for incorrect instantiation
|
81
|
+
with pytest.raises(ValueError):
|
82
|
+
Recs(data=list(), start_year=2000,
|
83
|
+
gfactors=None, weights=None)
|
84
|
+
with pytest.raises(ValueError):
|
85
|
+
Recs(data=cps_subsample, start_year=list(),
|
86
|
+
gfactors=None, weights=None)
|
87
|
+
with pytest.raises(ValueError):
|
88
|
+
Recs(data=cps_subsample, start_year=2000,
|
89
|
+
gfactors=None, weights='')
|
90
|
+
with pytest.raises(ValueError):
|
91
|
+
Recs(data=cps_subsample, start_year=2000,
|
92
|
+
gfactors=GrowFactors(), weights=None)
|
93
|
+
with pytest.raises(ValueError):
|
94
|
+
Recs(data=cps_subsample, start_year=2000,
|
95
|
+
gfactors='', weights='')
|
96
|
+
# test Recs class for correct instantiation with no aging of data
|
97
|
+
syr = 2014
|
98
|
+
rec = Recs(data=cps_subsample, start_year=syr,
|
99
|
+
gfactors=None, weights=None)
|
100
|
+
assert isinstance(rec, Recs)
|
101
|
+
assert np.all(rec.MARS != 0)
|
102
|
+
assert rec.data_year == syr
|
103
|
+
assert rec.current_year == syr
|
104
|
+
sum_e00300_in_syr = rec.e00300.sum()
|
105
|
+
rec.increment_year()
|
106
|
+
assert rec.data_year == syr
|
107
|
+
assert rec.current_year == syr + 1
|
108
|
+
sum_e00300_in_syr_plus_one = rec.e00300.sum()
|
109
|
+
assert np.allclose([sum_e00300_in_syr], [sum_e00300_in_syr_plus_one])
|
110
|
+
del rec
|
111
|
+
# test Recs class for correct instantiation with aging of data
|
112
|
+
wghts_path = os.path.join(GrowFactors.FILE_PATH, 'cps_weights.csv.gz')
|
113
|
+
wghts_df = pd.read_csv(wghts_path)
|
114
|
+
rec = Recs(data=cps_subsample, start_year=syr,
|
115
|
+
gfactors=GrowFactors(), weights=wghts_df)
|
116
|
+
assert isinstance(rec, Recs)
|
117
|
+
assert np.all(rec.MARS != 0)
|
118
|
+
assert rec.data_year == syr
|
119
|
+
assert rec.current_year == syr
|
120
|
+
sum_s006_in_syr = rec.s006.sum()
|
121
|
+
sum_e00300_in_syr = rec.e00300.sum()
|
122
|
+
rec.increment_year()
|
123
|
+
assert rec.data_year == syr
|
124
|
+
assert rec.current_year == syr + 1
|
125
|
+
sum_s006_in_syr_plus_one = rec.s006.sum()
|
126
|
+
assert sum_s006_in_syr_plus_one > sum_s006_in_syr
|
127
|
+
sum_e00300_in_syr_plus_one = rec.e00300.sum()
|
128
|
+
assert sum_e00300_in_syr_plus_one > sum_e00300_in_syr
|
129
|
+
# test private methods
|
130
|
+
rec._read_data(data=None)
|
131
|
+
rec._read_weights(weights=None)
|
132
|
+
with pytest.raises(ValueError):
|
133
|
+
rec._read_weights(weights=list())
|