def test_calculator_using_nonstd_input(rawinputfile): """ Test Calculator using non-standard input records. """ # check Calculator handling of raw, non-standard input data with no aging pol = Policy() pol.set_year(RAWINPUTFILE_YEAR) # set policy params to input data year nonstd = Records(data=rawinputfile.name, gfactors=None, # keeps raw data unchanged weights=None, start_year=RAWINPUTFILE_YEAR) # set raw input data year assert nonstd.array_length == RAWINPUTFILE_FUNITS calc = Calculator(policy=pol, records=nonstd, sync_years=False) # keeps raw data unchanged assert calc.current_year == RAWINPUTFILE_YEAR calc.calc_all() assert calc.weighted_total('e00200') == 0 assert calc.total_weight() == 0 varlist = ['RECID', 'MARS'] dframe = calc.dataframe(varlist) assert isinstance(dframe, pd.DataFrame) assert dframe.shape == (RAWINPUTFILE_FUNITS, len(varlist)) mars = calc.array('MARS') assert isinstance(mars, np.ndarray) assert mars.shape == (RAWINPUTFILE_FUNITS,) exp_iitax = np.zeros((nonstd.array_length,)) assert np.allclose(calc.array('iitax'), exp_iitax) mtr_ptax, _, _ = calc.mtr(wrt_full_compensation=False) exp_mtr_ptax = np.zeros((nonstd.array_length,)) exp_mtr_ptax.fill(0.153) assert np.allclose(mtr_ptax, exp_mtr_ptax)
def test_consumption_response(cps_subsample): consump = Consumption() mpc = 0.5 consumption_response = {'MPC_e20400': {2013: mpc}} consump.update_consumption(consumption_response) # test incorrect call to response method with pytest.raises(ValueError): consump.response(list(), 1) # test correct call to response method rec = Records.cps_constructor(data=cps_subsample) pre = copy.deepcopy(rec.e20400) consump.response(rec, 1.0) post = rec.e20400 actual_diff = post - pre expected_diff = np.ones(rec.array_length) * mpc assert np.allclose(actual_diff, expected_diff) # compute earnings mtr with no consumption response rec = Records.cps_constructor(data=cps_subsample) ided0 = copy.deepcopy(rec.e20400) calc0 = Calculator(policy=Policy(), records=rec, consumption=None) (mtr0_ptax, mtr0_itax, _) = calc0.mtr(variable_str='e00200p', wrt_full_compensation=False) assert np.allclose(calc0.array('e20400'), ided0) # compute earnings mtr with consumption response calc1 = Calculator(policy=Policy(), records=rec, consumption=consump) mtr1_ptax, mtr1_itax, _ = calc1.mtr(variable_str='e00200p', wrt_full_compensation=False) assert np.allclose(calc1.array('e20400'), ided0) # confirm that payroll mtr values are no different assert np.allclose(mtr1_ptax, mtr0_ptax) # confirm that all mtr with cons-resp are no greater than without cons-resp assert np.all(np.less_equal(np.around(mtr1_itax, decimals=5), np.around(mtr0_itax, decimals=5))) # confirm that some mtr with cons-resp are less than without cons-resp assert np.any(np.less(mtr1_itax, mtr0_itax))
def test_calculator_using_nonstd_input(rawinputfile): # check Calculator handling of raw, non-standard input data with no aging pol = Policy() pol.set_year(RAWINPUTFILE_YEAR) # set policy params to input data year nonstd = Records( data=rawinputfile.name, gfactors=None, # keeps raw data unchanged weights=None, start_year=RAWINPUTFILE_YEAR) # set raw input data year assert nonstd.dim == RAWINPUTFILE_FUNITS calc = Calculator(policy=pol, records=nonstd, sync_years=False) # keeps raw data unchanged assert calc.current_year == RAWINPUTFILE_YEAR calc.calc_all() assert calc.weighted_total('e00200') == 0 assert calc.total_weight() == 0 varlist = ['RECID', 'MARS'] pdf = calc.dataframe(varlist) assert isinstance(pdf, pd.DataFrame) assert pdf.shape == (RAWINPUTFILE_FUNITS, len(varlist)) mars = calc.array('MARS') assert isinstance(mars, np.ndarray) assert mars.shape == (RAWINPUTFILE_FUNITS, ) exp_iitax = np.zeros((nonstd.dim, )) assert np.allclose(calc.records.iitax, exp_iitax) mtr_ptax, _, _ = calc.mtr(wrt_full_compensation=False) exp_mtr_ptax = np.zeros((nonstd.dim, )) exp_mtr_ptax.fill(0.153) assert np.allclose(mtr_ptax, exp_mtr_ptax)
def reform_results(rid, reform_dict, puf_data, reform_2017_law): """ Return actual results of the reform specified by rid and reform_dict. """ # pylint: disable=too-many-locals rec = Records(data=puf_data) # create baseline Calculator object, calc1 pol = Policy() if reform_dict['baseline'] == '2017_law.json': pol.implement_reform(reform_2017_law) elif reform_dict['baseline'] == 'policy_current_law.json': pass else: msg = 'illegal baseline value {}' raise ValueError(msg.format(reform_dict['baseline'])) calc1 = Calculator(policy=pol, records=rec, verbose=False) # create reform Calculator object, calc2 start_year = reform_dict['start_year'] reform = dict() for name, value in reform_dict['value'].items(): reform[name] = {start_year: value} pol.implement_reform(reform) calc2 = Calculator(policy=pol, records=rec, verbose=False) # increment both Calculator objects to reform's start_year calc1.advance_to_year(start_year) calc2.advance_to_year(start_year) # calculate baseline and reform output for several years output_type = reform_dict['output_type'] num_years = 4 results = list() for _ in range(0, num_years): calc1.calc_all() baseline = calc1.array(output_type) calc2.calc_all() reform = calc2.array(output_type) diff = reform - baseline weighted_sum_diff = (diff * calc1.array('s006')).sum() * 1.0e-9 results.append(weighted_sum_diff) calc1.increment_year() calc2.increment_year() # write actual results to actual_str actual_str = '{}'.format(rid) for iyr in range(0, num_years): actual_str += ',{:.1f}'.format(results[iyr]) return actual_str
def test_ID_RealEstate_HC_vs_CRT(cps_subsample): """ Test that a cap on all state, local, and foreign real estate tax deductions at 0 percent of AGI is equivalent to a complete haircut on the same real estate tax deductions. """ rec = Records.cps_constructor(data=cps_subsample) # specify real estate complete haircut reform policy and Calculator object hc_reform = {2013: {'_ID_RealEstate_hc': [1.0]}} hc_policy = Policy() hc_policy.implement_reform(hc_reform) hc_calc = Calculator(policy=hc_policy, records=rec) hc_calc.calc_all() # specify AGI cap reform policy and Calculator object crt_reform = {2013: {'_ID_RealEstate_crt': [0.0]}} crt_policy = Policy() crt_policy.implement_reform(crt_reform) crt_calc = Calculator(policy=crt_policy, records=rec) crt_calc.calc_all() # compare calculated tax results generated by the two reforms assert np.allclose(hc_calc.array('payrolltax'), crt_calc.array('payrolltax')) assert np.allclose(hc_calc.array('iitax'), crt_calc.array('iitax'))
def test_ID_StateLocal_HC_vs_CRT(cps_subsample): """ Test that a cap on state/local income and sales tax deductions at 0 percent of AGI is equivalent to a complete haircut on the same state/local tax deductions. """ rec = Records.cps_constructor(data=cps_subsample) # specify state/local complete haircut reform policy and Calculator object hc_policy = Policy() hc_reform = {'ID_StateLocalTax_hc': {2013: 1.0}} hc_policy.implement_reform(hc_reform) hc_calc = Calculator(policy=hc_policy, records=rec) hc_calc.calc_all() # specify AGI cap reform policy and Calculator object crt_policy = Policy() crt_reform = {'ID_StateLocalTax_crt': {2013: 0.0}} crt_policy.implement_reform(crt_reform) crt_calc = Calculator(policy=crt_policy, records=rec) crt_calc.calc_all() # compare calculated tax results generated by the two reforms assert np.allclose(hc_calc.array('payrolltax'), crt_calc.array('payrolltax')) assert np.allclose(hc_calc.array('iitax'), crt_calc.array('iitax'))
def test_calculator_mtr(cps_subsample): """ Test Calculator mtr method. """ rec = Records.cps_constructor(data=cps_subsample) calcx = Calculator(policy=Policy(), records=rec) calcx.calc_all() combinedx = calcx.array('combined') c00100x = calcx.array('c00100') calc = Calculator(policy=Policy(), records=rec) recs_pre_e00200p = copy.deepcopy(calc.array('e00200p')) (mtr_ptx, mtr_itx, mtr_cmb) = calc.mtr(variable_str='e00200p', zero_out_calculated_vars=True) recs_post_e00200p = calc.array('e00200p') assert np.allclose(recs_post_e00200p, recs_pre_e00200p) assert np.allclose(calc.array('combined'), combinedx) assert np.allclose(calc.array('c00100'), c00100x) assert np.array_equal(mtr_cmb, mtr_ptx) is False assert np.array_equal(mtr_ptx, mtr_itx) is False with pytest.raises(ValueError): calc.mtr(variable_str='bad_income_type') (_, _, mtr_combined) = calc.mtr(variable_str='e00200s', calc_all_already_called=True) assert isinstance(mtr_combined, np.ndarray) (_, _, mtr_combined) = calc.mtr(variable_str='e00650', negative_finite_diff=True, calc_all_already_called=True) assert isinstance(mtr_combined, np.ndarray) (_, _, mtr_combined) = calc.mtr(variable_str='e00900p', calc_all_already_called=True) assert isinstance(mtr_combined, np.ndarray) (_, _, mtr_combined) = calc.mtr(variable_str='e01700', calc_all_already_called=True) assert isinstance(mtr_combined, np.ndarray) (_, _, mtr_combined) = calc.mtr(variable_str='e26270', calc_all_already_called=True) assert isinstance(mtr_combined, np.ndarray) (_, _, mtr_combined) = calc.mtr(variable_str='e00200p', calc_all_already_called=True) assert np.allclose(mtr_combined, mtr_cmb) assert np.allclose(calc.array('combined'), combinedx) assert np.allclose(calc.array('c00100'), c00100x)
def test_benefits(tests_path, cps_fullsample): """ Test CPS benefits. """ # pylint: disable=too-many-locals benefit_names = ['ssi', 'mcare', 'mcaid', 'snap', 'wic', 'tanf', 'vet', 'housing'] # write benefits_actual.csv file recs = Records.cps_constructor(data=cps_fullsample) start_year = recs.current_year calc = Calculator(policy=Policy(), records=recs, verbose=False) assert calc.current_year == start_year year_list = list() bname_list = list() benamt_list = list() bencnt_list = list() benavg_list = list() for year in range(start_year, Policy.LAST_BUDGET_YEAR + 1): calc.advance_to_year(year) size = calc.array('XTOT') wght = calc.array('s006') # compute benefit aggregate amounts and head counts and average benefit # (head counts include all members of filing unit receiving a benefit, # which means benavg is f.unit benefit amount divided by f.unit size) for bname in benefit_names: ben = calc.array('{}_ben'.format(bname)) benamt = round((ben * wght).sum() * 1e-9, 3) bencnt = round((size[ben > 0] * wght[ben > 0]).sum() * 1e-6, 3) benavg = round(benamt / bencnt, 1) year_list.append(year) bname_list.append(bname) benamt_list.append(benamt) bencnt_list.append(bencnt) benavg_list.append(benavg) adict = {'year': year_list, 'bname': bname_list, 'benamt': benamt_list, 'bencnt': bencnt_list, 'benavg': benavg_list} adf = pd.DataFrame(data=adict, columns=['year', 'bname', 'benamt', 'bencnt', 'benavg']) ben_act_path = os.path.join(tests_path, 'benefits_actual.csv') adf.to_csv(ben_act_path, index=False) # read benefits_expect.csv file ben_exp_path = os.path.join(tests_path, 'benefits_expect.csv') edf = pd.read_csv(ben_exp_path) # compare benefit information atol = 0.0001 rtol = 0.0 diffs = False for col in ['benamt', 'bencnt', 'benavg']: if not np.allclose(adf[col], edf[col], atol=atol, rtol=rtol): diffs = True if diffs: msg = 'CPS BENEFITS RESULTS DIFFER\n' msg += '-------------------------------------------------\n' msg += '--- NEW RESULTS IN benefits_actual.txt FILE ---\n' msg += '--- if new OK, copy benefits_actual.txt to ---\n' msg += '--- benefits_expect.txt ---\n' msg += '--- and rerun test. ---\n' msg += '-------------------------------------------------\n' raise ValueError(msg) else: os.remove(ben_act_path)
def test_mtr(tests_path, puf_path): """ Test Tax-Calculator marginal tax rates with no policy reform using puf.csv Compute histograms for each marginal tax rate income type using sample input from the puf.csv file and writing output to a string, which is then compared for differences with EXPECTED_MTR_RESULTS. """ # pylint: disable=too-many-locals,too-many-statements assert len(PTAX_MTR_BIN_EDGES) == len(ITAX_MTR_BIN_EDGES) # construct actual results string, res res = '' if MTR_NEG_DIFF: res += 'MTR computed using NEGATIVE finite_diff ' else: res += 'MTR computed using POSITIVE finite_diff ' res += 'for tax year {}\n'.format(MTR_TAX_YEAR) # create a Policy object (clp) containing current-law policy parameters clp = Policy() clp.set_year(MTR_TAX_YEAR) # create a Records object (puf) containing puf.csv input records puf = Records(data=puf_path) recid = puf.RECID # pylint: disable=no-member # create a Calculator object using clp policy and puf records calc = Calculator(policy=clp, records=puf) res += '{} = {}\n'.format('Total number of data records', puf.array_length) res += 'PTAX mtr histogram bin edges:\n' res += ' {}\n'.format(PTAX_MTR_BIN_EDGES) res += 'ITAX mtr histogram bin edges:\n' res += ' {}\n'.format(ITAX_MTR_BIN_EDGES) variable_header = 'PTAX and ITAX mtr histogram bin counts for' # compute marginal tax rate (mtr) histograms for each mtr variable for var_str in Calculator.MTR_VALID_VARIABLES: zero_out = (var_str == 'e01400') (mtr_ptax, mtr_itax, _) = calc.mtr(variable_str=var_str, negative_finite_diff=MTR_NEG_DIFF, zero_out_calculated_vars=zero_out, wrt_full_compensation=False) if zero_out: # check that calculated variables are consistent assert np.allclose((calc.array('iitax') + calc.array('payrolltax')), calc.array('combined')) assert np.allclose((calc.array('ptax_was') + calc.array('setax') + calc.array('ptax_amc')), calc.array('payrolltax')) assert np.allclose(calc.array('c21060') - calc.array('c21040'), calc.array('c04470')) assert np.allclose(calc.array('taxbc') + calc.array('c09600'), calc.array('c05800')) assert np.allclose((calc.array('c05800') + calc.array('othertaxes') - calc.array('c07100')), calc.array('c09200')) assert np.allclose(calc.array('c09200') - calc.array('refund'), calc.array('iitax')) if var_str == 'e00200s': # only MARS==2 filing units have valid MTR values mtr_ptax = mtr_ptax[calc.array('MARS') == 2] mtr_itax = mtr_itax[calc.array('MARS') == 2] res += '{} {}:\n'.format(variable_header, var_str) res += mtr_bin_counts(mtr_ptax, PTAX_MTR_BIN_EDGES, recid) res += mtr_bin_counts(mtr_itax, ITAX_MTR_BIN_EDGES, recid) # check for differences between actual and expected results mtrres_path = os.path.join(tests_path, 'pufcsv_mtr_expect.txt') with open(mtrres_path, 'r') as expected_file: txt = expected_file.read() expected_results = txt.rstrip('\n\t ') + '\n' # cleanup end of file txt if nonsmall_diffs(res.splitlines(True), expected_results.splitlines(True)): new_filename = '{}{}'.format(mtrres_path[:-10], 'actual.txt') with open(new_filename, 'w') as new_file: new_file.write(res) msg = 'PUFCSV MTR RESULTS DIFFER\n' msg += '-------------------------------------------------\n' msg += '--- NEW RESULTS IN pufcsv_mtr_actual.txt FILE ---\n' msg += '--- if new OK, copy pufcsv_mtr_actual.txt to ---\n' msg += '--- pufcsv_mtr_expect.txt ---\n' msg += '--- and rerun test. ---\n' msg += '-------------------------------------------------\n' raise ValueError(msg)
def test_itemded_component_amounts(year, cvname, hcname, puf_fullsample): """ Check that all c04470 components are adjusted to reflect the filing unit's standard-vs-itemized-deduction decision. Check for 2018 (when current law has no Pease phaseout of itemized deductions and already has complete haircuts for Casualty and Miscellaneous deductions) and 2017 (when current law has a Pease phaseout of itemized deductions and has no haircuts). The calcfunctions.py code makes no attempt to adjust the components for the effects of Pease-like phaseout or any other type of limitation on total itemized deductions, so the pre-2018 tests here use c21060, instead of c04470, as the itemized deductions total. """ # pylint: disable=too-many-locals recs = Records(data=puf_fullsample) # policy1 such that everybody itemizes deductions and all are allowed policy1 = Policy() reform1 = { 'STD_Aged': {year: [0.0, 0.0, 0.0, 0.0, 0.0]}, 'STD': {year: [0.0, 0.0, 0.0, 0.0, 0.0]} } policy1.implement_reform(reform1) assert not policy1.parameter_errors # policy2 such that everybody itemizes deductions but one is disallowed policy2 = Policy() reform2 = { 'STD_Aged': {year: [0.0, 0.0, 0.0, 0.0, 0.0]}, 'STD': {year: [0.0, 0.0, 0.0, 0.0, 0.0]}, hcname: {year: 1.0} } policy2.implement_reform(reform2) assert not policy2.parameter_errors # compute tax liability in specified year calc1 = Calculator(policy=policy1, records=recs, verbose=False) calc1.advance_to_year(year) calc1.calc_all() calc2 = Calculator(policy=policy2, records=recs, verbose=False) calc2.advance_to_year(year) calc2.calc_all() # confirm that nobody is taking the standard deduction assert np.allclose(calc1.array('standard'), 0.) assert np.allclose(calc2.array('standard'), 0.) # calculate different in total itemized deductions if year == 2017: # pre-Pease limitation total itemized deductions itmded1 = calc1.weighted_total('c21060') * 1e-9 itmded2 = calc2.weighted_total('c21060') * 1e-9 elif year == 2018: # total itemized deductions (no Pease-like limitation) itmded1 = calc1.weighted_total('c04470') * 1e-9 itmded2 = calc2.weighted_total('c04470') * 1e-9 else: raise ValueError('illegal year value = {}'.format(year)) difference_in_total_itmded = itmded1 - itmded2 # calculate itemized component amount component_amt = calc1.weighted_total(cvname) * 1e-9 # confirm that component amount is equal to difference in total deductions if year == 2017 and cvname == 'c19700': atol = 0.009 elif year == 2017 and cvname == 'c19200': atol = 0.010 else: atol = 0.00001 if not np.allclose(component_amt, difference_in_total_itmded, atol=atol): txt = '\n{}={:.3f} != {:.3f}=difference_in_total_itemized_deductions' msg = txt.format(cvname, component_amt, difference_in_total_itmded) raise ValueError(msg)