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.
Files changed (123) hide show
  1. taxcalc/__init__.py +1 -1
  2. taxcalc/assumptions/ASSUMPTIONS.md +53 -0
  3. taxcalc/assumptions/README.md +17 -0
  4. taxcalc/assumptions/economic_assumptions_template.json +77 -0
  5. taxcalc/calcfunctions.py +7 -4
  6. taxcalc/data.py +10 -5
  7. taxcalc/policy_current_law.json +2033 -184
  8. taxcalc/reforms/2017_law.json +125 -0
  9. taxcalc/reforms/2017_law.out.csv +10 -0
  10. taxcalc/reforms/ARPA.json +78 -0
  11. taxcalc/reforms/ARPA.out.csv +10 -0
  12. taxcalc/reforms/BrownKhanna.json +23 -0
  13. taxcalc/reforms/BrownKhanna.out.csv +10 -0
  14. taxcalc/reforms/CARES.json +40 -0
  15. taxcalc/reforms/CARES.out.csv +10 -0
  16. taxcalc/reforms/ConsolidatedAppropriationsAct2021.json +15 -0
  17. taxcalc/reforms/ConsolidatedAppropriationsAct2021.out.csv +10 -0
  18. taxcalc/reforms/Larson2019.json +36 -0
  19. taxcalc/reforms/Larson2019.out.csv +10 -0
  20. taxcalc/reforms/README.md +22 -0
  21. taxcalc/reforms/REFORMS.md +92 -0
  22. taxcalc/reforms/Renacci.json +61 -0
  23. taxcalc/reforms/Renacci.out.csv +10 -0
  24. taxcalc/reforms/SandersDeFazio.json +15 -0
  25. taxcalc/reforms/SandersDeFazio.out.csv +10 -0
  26. taxcalc/reforms/TCJA.json +160 -0
  27. taxcalc/reforms/TCJA.md +48 -0
  28. taxcalc/reforms/TCJA.out.csv +10 -0
  29. taxcalc/reforms/Trump2016.json +71 -0
  30. taxcalc/reforms/Trump2016.out.csv +10 -0
  31. taxcalc/reforms/Trump2017.json +51 -0
  32. taxcalc/reforms/Trump2017.out.csv +10 -0
  33. taxcalc/reforms/archive/Clinton2016.json +56 -0
  34. taxcalc/reforms/archive/RyanBrady.json +104 -0
  35. taxcalc/reforms/archive/TCJA_House.json +144 -0
  36. taxcalc/reforms/archive/TCJA_House_Amended.json +152 -0
  37. taxcalc/reforms/archive/TCJA_Reconciliation.json +187 -0
  38. taxcalc/reforms/archive/TCJA_Senate.json +116 -0
  39. taxcalc/reforms/archive/TCJA_Senate_111417.json +169 -0
  40. taxcalc/reforms/archive/TCJA_Senate_120117.json +174 -0
  41. taxcalc/reforms/cases.csv +10 -0
  42. taxcalc/reforms/clp.out.csv +10 -0
  43. taxcalc/reforms/ext.json +59 -0
  44. taxcalc/reforms/growfactors_ext.csv +65 -0
  45. taxcalc/reforms/ptaxes0.json +37 -0
  46. taxcalc/reforms/ptaxes0.out.csv +10 -0
  47. taxcalc/reforms/ptaxes1.json +21 -0
  48. taxcalc/reforms/ptaxes1.out.csv +10 -0
  49. taxcalc/reforms/ptaxes2.json +18 -0
  50. taxcalc/reforms/ptaxes2.out.csv +10 -0
  51. taxcalc/reforms/ptaxes3.json +28 -0
  52. taxcalc/reforms/ptaxes3.out.csv +10 -0
  53. taxcalc/reforms/rounding2022.json +153 -0
  54. taxcalc/reforms/rounding2022.out.csv +10 -0
  55. taxcalc/tests/benefits_expect.csv +169 -0
  56. taxcalc/tests/cmpi_cps_expect.txt +132 -0
  57. taxcalc/tests/cmpi_puf_expect.txt +132 -0
  58. taxcalc/tests/conftest.py +143 -0
  59. taxcalc/tests/cpscsv_agg_expect.csv +26 -0
  60. taxcalc/tests/puf_var_correl_coeffs_2016.csv +80 -0
  61. taxcalc/tests/puf_var_wght_means_by_year.csv +80 -0
  62. taxcalc/tests/pufcsv_agg_expect.csv +26 -0
  63. taxcalc/tests/pufcsv_mtr_expect.txt +63 -0
  64. taxcalc/tests/reforms.json +649 -0
  65. taxcalc/tests/reforms_expect.csv +65 -0
  66. taxcalc/tests/test_4package.py +67 -0
  67. taxcalc/tests/test_benefits.py +86 -0
  68. taxcalc/tests/test_calcfunctions.py +871 -0
  69. taxcalc/tests/test_calculator.py +1021 -0
  70. taxcalc/tests/test_compare.py +336 -0
  71. taxcalc/tests/test_compatible_data.py +338 -0
  72. taxcalc/tests/test_consumption.py +144 -0
  73. taxcalc/tests/test_cpscsv.py +163 -0
  74. taxcalc/tests/test_data.py +133 -0
  75. taxcalc/tests/test_decorators.py +332 -0
  76. taxcalc/tests/test_growdiff.py +102 -0
  77. taxcalc/tests/test_growfactors.py +94 -0
  78. taxcalc/tests/test_parameters.py +617 -0
  79. taxcalc/tests/test_policy.py +1575 -0
  80. taxcalc/tests/test_puf_var_stats.py +194 -0
  81. taxcalc/tests/test_pufcsv.py +385 -0
  82. taxcalc/tests/test_records.py +234 -0
  83. taxcalc/tests/test_reforms.py +385 -0
  84. taxcalc/tests/test_responses.py +41 -0
  85. taxcalc/tests/test_taxcalcio.py +755 -0
  86. taxcalc/tests/test_tmdcsv.py +38 -0
  87. taxcalc/tests/test_utils.py +792 -0
  88. taxcalc/tmd_growfactors.csv +54 -54
  89. taxcalc/tmd_weights.csv.gz +0 -0
  90. taxcalc/validation/CSV_INPUT_VARS.md +29 -0
  91. taxcalc/validation/CSV_OUTPUT_VARS.md +63 -0
  92. taxcalc/validation/README.md +68 -0
  93. taxcalc/validation/taxsim35/Differences_Explained.md +54 -0
  94. taxcalc/validation/taxsim35/README.md +139 -0
  95. taxcalc/validation/taxsim35/expected_differences/a17-taxdiffs-expect.csv +25 -0
  96. taxcalc/validation/taxsim35/expected_differences/a18-taxdiffs-expect.csv +25 -0
  97. taxcalc/validation/taxsim35/expected_differences/a19-taxdiffs-expect.csv +25 -0
  98. taxcalc/validation/taxsim35/expected_differences/a20-taxdiffs-expect.csv +25 -0
  99. taxcalc/validation/taxsim35/expected_differences/a21-taxdiffs-expect.csv +25 -0
  100. taxcalc/validation/taxsim35/expected_differences/b17-taxdiffs-expect.csv +25 -0
  101. taxcalc/validation/taxsim35/expected_differences/b18-taxdiffs-expect.csv +25 -0
  102. taxcalc/validation/taxsim35/expected_differences/b19-taxdiffs-expect.csv +25 -0
  103. taxcalc/validation/taxsim35/expected_differences/b20-taxdiffs-expect.csv +25 -0
  104. taxcalc/validation/taxsim35/expected_differences/b21-taxdiffs-expect.csv +25 -0
  105. taxcalc/validation/taxsim35/expected_differences/c17-taxdiffs-expect.csv +25 -0
  106. taxcalc/validation/taxsim35/expected_differences/c18-taxdiffs-expect.csv +25 -0
  107. taxcalc/validation/taxsim35/expected_differences/c19-taxdiffs-expect.csv +25 -0
  108. taxcalc/validation/taxsim35/input_setup.py +67 -0
  109. taxcalc/validation/taxsim35/main_comparison.py +183 -0
  110. taxcalc/validation/taxsim35/prepare_taxcalc_input.py +161 -0
  111. taxcalc/validation/taxsim35/process_taxcalc_output.py +140 -0
  112. taxcalc/validation/taxsim35/taxsim_emulation.json +49 -0
  113. taxcalc/validation/taxsim35/taxsim_input.py +321 -0
  114. taxcalc/validation/taxsim35/tc_sims.py +98 -0
  115. taxcalc/validation/taxsim35/tests_35.py +80 -0
  116. taxcalc/validation/tests_35.sh +13 -0
  117. {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/METADATA +3 -4
  118. taxcalc-4.2.2.dist-info/RECORD +144 -0
  119. {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/WHEEL +1 -1
  120. taxcalc-4.2.1.dist-info/RECORD +0 -34
  121. {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/LICENSE +0 -0
  122. {taxcalc-4.2.1.dist-info → taxcalc-4.2.2.dist-info}/entry_points.txt +0 -0
  123. {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())