def test_update_and_apply_growdiff(): gdiff = GrowDiff() # update GrowDiff instance diffs = { 'AWAGE': {2014: 0.01, 2016: 0.02} } gdiff.update_growdiff(diffs) expected_wage_diffs = [0.00, 0.01, 0.01, 0.02, 0.02] extra_years = GrowDiff.DEFAULT_NUM_YEARS - len(expected_wage_diffs) expected_wage_diffs.extend([0.02] * extra_years) assert np.allclose(gdiff._AWAGE, expected_wage_diffs, atol=0.0, rtol=0.0) # apply growdiff to GrowFactors instance gf = GrowFactors() syr = GrowDiff.JSON_START_YEAR nyrs = GrowDiff.DEFAULT_NUM_YEARS lyr = syr + nyrs - 1 pir_pre = gf.price_inflation_rates(syr, lyr) wgr_pre = gf.wage_growth_rates(syr, lyr) gfactors = GrowFactors() gdiff.apply_to(gfactors) pir_pst = gfactors.price_inflation_rates(syr, lyr) wgr_pst = gfactors.wage_growth_rates(syr, lyr) expected_wgr_pst = [wgr_pre[i] + expected_wage_diffs[i] for i in range(0, nyrs)] assert np.allclose(pir_pre, pir_pst, atol=0.0, rtol=0.0) assert np.allclose(wgr_pst, expected_wgr_pst, atol=1.0e-9, rtol=0.0)
def test_response_json(tests_path): """ Check that each JSON file can be converted into dictionaries that can be used to construct objects needed for a Calculator object. """ # pylint: disable=too-many-locals responses_path = os.path.join(tests_path, '..', 'responses', '*.json') for jpf in glob.glob(responses_path): # read contents of jpf (JSON parameter filename) jfile = open(jpf, 'r') jpf_text = jfile.read() # check that jpf_text can be used to construct objects response_file = ('"consumption"' in jpf_text and '"growdiff_baseline"' in jpf_text and '"growdiff_response"' in jpf_text) if response_file: consumption = Consumption() con_change = Consumption.read_json_update(jpf_text) consumption.update_consumption(con_change) del consumption for topkey in ['growdiff_baseline', 'growdiff_response']: growdiff = GrowDiff() gdiff_change = GrowDiff.read_json_update(jpf_text, topkey) growdiff.update_growdiff(gdiff_change) del growdiff else: # jpf_text is not a valid JSON response assumption file print('test-failing-filename: ' + jpf) assert False
def test_has_any_response(): start_year = GrowDiff.JSON_START_YEAR gdiff = GrowDiff() assert gdiff.current_year == start_year assert gdiff.has_any_response() is False gdiff.update_growdiff({'AWAGE': {2020: 0.01}}) assert gdiff.current_year == start_year assert gdiff.has_any_response() is True
def reform_warnings_errors(user_mods, using_puf): """ The reform_warnings_errors function assumes user_mods is a dictionary returned by the Calculator.read_json_param_objects() function. This function returns a dictionary containing five STR:STR subdictionaries, where the dictionary keys are: 'policy', 'consumption', 'growdiff_baseline' and 'growdiff_response'; and the subdictionaries are: {'warnings': '<empty-or-message(s)>', 'errors': '<empty-or-message(s)>'}. Note that non-policy parameters have no warnings, so the 'warnings' string for the non-policy parameters is always empty. """ rtn_dict = {'policy': {'warnings': '', 'errors': ''}, 'consumption': {'warnings': '', 'errors': ''}, 'growdiff_baseline': {'warnings': '', 'errors': ''}, 'growdiff_response': {'warnings': '', 'errors': ''}} # create GrowDiff objects gdiff_baseline = GrowDiff() try: gdiff_baseline.update_growdiff(user_mods['growdiff_baseline']) except ValueError as valerr_msg: rtn_dict['growdiff_baseline']['errors'] = valerr_msg.__str__() gdiff_response = GrowDiff() try: gdiff_response.update_growdiff(user_mods['growdiff_response']) except ValueError as valerr_msg: rtn_dict['growdiff_response']['errors'] = valerr_msg.__str__() # create Growfactors object growfactors = GrowFactors() gdiff_baseline.apply_to(growfactors) gdiff_response.apply_to(growfactors) # create Policy object pol = Policy(gfactors=growfactors) try: pol.implement_reform(user_mods['policy'], print_warnings=False, raise_errors=False) if using_puf: rtn_dict['policy']['warnings'] = pol.parameter_warnings rtn_dict['policy']['errors'] = pol.parameter_errors except ValueError as valerr_msg: rtn_dict['policy']['errors'] = valerr_msg.__str__() # create Consumption object consump = Consumption() try: consump.update_consumption(user_mods['consumption']) except ValueError as valerr_msg: rtn_dict['consumption']['errors'] = valerr_msg.__str__() # return composite dictionary of warnings/errors return rtn_dict
def test_response_json(tests_path): """ Check that each JSON file can be converted into dictionaries that can be used to construct objects needed for a Calculator object. """ # pylint: disable=too-many-locals responses_path = os.path.join(tests_path, '..', 'responses', '*.json') for jpf in glob.glob(responses_path): # read contents of jpf (JSON parameter filename) jfile = open(jpf, 'r') jpf_text = jfile.read() # check that jpf_text can be used to construct objects response_file = ('"consumption"' in jpf_text and '"behavior"' in jpf_text and '"growdiff_baseline"' in jpf_text and '"growdiff_response"' in jpf_text and '"growmodel"' in jpf_text) if response_file: # pylint: disable=protected-access (con, beh, gdiff_base, gdiff_resp, grow_model) = Calculator._read_json_econ_assump_text(jpf_text) cons = Consumption() cons.update_consumption(con) behv = Behavior() behv.update_behavior(beh) growdiff_baseline = GrowDiff() growdiff_baseline.update_growdiff(gdiff_base) growdiff_response = GrowDiff() growdiff_response.update_growdiff(gdiff_resp) growmodel = GrowModel() growmodel.update_growmodel(grow_model) else: # jpf_text is not a valid JSON response assumption file print('test-failing-filename: ' + jpf) assert False
def test_response_json(tests_path): """ Check that each JSON file can be converted into dictionaries that can be used to construct objects needed for a Calculator object. """ # pylint: disable=too-many-locals responses_path = os.path.join(tests_path, '..', 'responses', '*.json') for jpf in glob.glob(responses_path): # read contents of jpf (JSON parameter filename) jfile = open(jpf, 'r') jpf_text = jfile.read() # check that jpf_text can be used to construct objects response_file = ('"consumption"' in jpf_text and '"growdiff_baseline"' in jpf_text and '"growdiff_response"' in jpf_text) if response_file: # pylint: disable=protected-access (con, gdiff_base, gdiff_resp) = Calculator._read_json_econ_assump_text(jpf_text) cons = Consumption() cons.update_consumption(con) growdiff_baseline = GrowDiff() growdiff_baseline.update_growdiff(gdiff_base) growdiff_response = GrowDiff() growdiff_response.update_growdiff(gdiff_resp) else: # jpf_text is not a valid JSON response assumption file print('test-failing-filename: ' + jpf) assert False
def test_update_and_apply_growdiff(): gdiff = GrowDiff() # update GrowDiff instance diffs = {'AWAGE': {2014: 0.01, 2016: 0.02}} gdiff.update_growdiff(diffs) expected_wage_diffs = [0.00, 0.01, 0.01, 0.02, 0.02] extra_years = GrowDiff.DEFAULT_NUM_YEARS - len(expected_wage_diffs) expected_wage_diffs.extend([0.02] * extra_years) assert np.allclose(gdiff._AWAGE, expected_wage_diffs, atol=0.0, rtol=0.0) # apply growdiff to GrowFactors instance gf = GrowFactors() syr = GrowDiff.JSON_START_YEAR nyrs = GrowDiff.DEFAULT_NUM_YEARS lyr = syr + nyrs - 1 pir_pre = gf.price_inflation_rates(syr, lyr) wgr_pre = gf.wage_growth_rates(syr, lyr) gfactors = GrowFactors() gdiff.apply_to(gfactors) pir_pst = gfactors.price_inflation_rates(syr, lyr) wgr_pst = gfactors.wage_growth_rates(syr, lyr) expected_wgr_pst = [ wgr_pre[i] + expected_wage_diffs[i] for i in range(0, nyrs) ] assert np.allclose(pir_pre, pir_pst, atol=0.0, rtol=0.0) assert np.allclose(wgr_pst, expected_wgr_pst, atol=1.0e-9, rtol=0.0)
def test_has_any_response(): syr = 2014 gdiff = GrowDiff(start_year=syr) assert gdiff.has_any_response() is False gdiff.update_growdiff({2020: {'_AWAGE': [0.01]}}) assert gdiff.current_year == syr assert gdiff.has_any_response() is True
def test_incorrect_update_growdiff(): with pytest.raises(ValueError): GrowDiff().update_growdiff([]) with pytest.raises(ValueError): GrowDiff().update_growdiff({'xyz': {'_ABOOK': [0.02]}}) with pytest.raises(ValueError): GrowDiff().update_growdiff({2012: {'_ABOOK': [0.02]}}) with pytest.raises(ValueError): GrowDiff().update_growdiff({2052: {'_ABOOK': [0.02]}}) with pytest.raises(ValueError): GrowDiff().update_growdiff({2014: {'_MPC_exxxxx': [0.02]}}) with pytest.raises(ValueError): GrowDiff().update_growdiff({2014: {'_ABOOK': [-1.1]}})
def test_update_and_apply_growdiff(): syr = 2013 nyrs = 5 lyr = syr + nyrs - 1 gdiff = GrowDiff(start_year=syr, num_years=nyrs) # update GrowDiff instance diffs = {2014: {'_AWAGE': [0.01]}, 2016: {'_AWAGE': [0.02]}} gdiff.update_growdiff(diffs) expected_wage_diffs = [0.00, 0.01, 0.01, 0.02, 0.02] assert_allclose(gdiff._AWAGE, expected_wage_diffs, atol=0.0, rtol=0.0) # apply growdiff to GrowFactors instance gf = GrowFactors() pir_pre = gf.price_inflation_rates(syr, lyr) wgr_pre = gf.wage_growth_rates(syr, lyr) gfactors = GrowFactors() gdiff.apply_to(gfactors) pir_pst = gfactors.price_inflation_rates(syr, lyr) wgr_pst = gfactors.wage_growth_rates(syr, lyr) expected_wgr_pst = [ wgr_pre[i] + expected_wage_diffs[i] for i in range(0, nyrs) ] assert_allclose(pir_pre, pir_pst, atol=0.0, rtol=0.0) assert_allclose(wgr_pst, expected_wgr_pst, atol=1.0e-9, rtol=0.0)
def calculators(year_n, start_year, use_puf_not_cps, use_full_sample, user_mods): """ This function assumes that the specified user_mods is a dictionary returned by the Calculator.read_json_param_objects() function. This function returns (calc1, calc2) where calc1 is pre-reform Calculator object for year_n, and calc2 is post-reform Calculator object for year_n. Neither Calculator object has had the calc_all() method executed. """ # pylint: disable=too-many-locals,too-many-branches,too-many-statements check_user_mods(user_mods) # specify Consumption instance consump = Consumption() consump_assumptions = user_mods['consumption'] consump.update_consumption(consump_assumptions) # specify growdiff_baseline and growdiff_response growdiff_baseline = GrowDiff() growdiff_response = GrowDiff() growdiff_base_assumps = user_mods['growdiff_baseline'] growdiff_resp_assumps = user_mods['growdiff_response'] growdiff_baseline.update_growdiff(growdiff_base_assumps) growdiff_response.update_growdiff(growdiff_resp_assumps) # create pre-reform and post-reform GrowFactors instances growfactors_pre = GrowFactors() growdiff_baseline.apply_to(growfactors_pre) growfactors_post = GrowFactors() growdiff_baseline.apply_to(growfactors_post) growdiff_response.apply_to(growfactors_post) # create sample pd.DataFrame from specified input file and sampling scheme tbi_path = os.path.abspath(os.path.dirname(__file__)) if use_puf_not_cps: # first try TaxBrain deployment path input_path = 'puf.csv.gz' if not os.path.isfile(input_path): # otherwise try local Tax-Calculator deployment path input_path = os.path.join(tbi_path, '..', '..', 'puf.csv') sampling_frac = 0.05 sampling_seed = 2222 else: # if using cps input not puf input # first try Tax-Calculator code path input_path = os.path.join(tbi_path, '..', 'cps.csv.gz') if not os.path.isfile(input_path): # otherwise read from taxcalc package "egg" input_path = None # pragma: no cover full_sample = read_egg_csv('cps.csv.gz') # pragma: no cover sampling_frac = 0.03 sampling_seed = 180 if input_path: full_sample = pd.read_csv(input_path) if use_full_sample: sample = full_sample else: sample = full_sample.sample(frac=sampling_frac, random_state=sampling_seed) # create pre-reform Calculator instance if use_puf_not_cps: recs1 = Records(data=sample, gfactors=growfactors_pre) else: recs1 = Records.cps_constructor(data=sample, gfactors=growfactors_pre) policy1 = Policy(gfactors=growfactors_pre) calc1 = Calculator(policy=policy1, records=recs1, consumption=consump) while calc1.current_year < start_year: calc1.increment_year() assert calc1.current_year == start_year # create post-reform Calculator instance if use_puf_not_cps: recs2 = Records(data=sample, gfactors=growfactors_post) else: recs2 = Records.cps_constructor(data=sample, gfactors=growfactors_post) policy2 = Policy(gfactors=growfactors_post) policy_reform = user_mods['policy'] policy2.implement_reform(policy_reform) calc2 = Calculator(policy=policy2, records=recs2, consumption=consump) while calc2.current_year < start_year: calc2.increment_year() assert calc2.current_year == start_year # delete objects now embedded in calc1 and calc2 del sample del full_sample del consump del growdiff_baseline del growdiff_response del growfactors_pre del growfactors_post del recs1 del recs2 del policy1 del policy2 # increment Calculator objects for year_n years for _ in range(0, year_n): calc1.increment_year() calc2.increment_year() # return Calculator objects return (calc1, calc2)
def reform_warnings_errors(user_mods, using_puf): """ The reform_warnings_errors function assumes user_mods is a dictionary returned by the Calculator.read_json_param_objects() function. This function returns a dictionary containing five STR:STR subdictionaries, where the dictionary keys are: 'policy', 'behavior', consumption', 'growdiff_baseline' and 'growdiff_response'; and the subdictionaries are: {'warnings': '<empty-or-message(s)>', 'errors': '<empty-or-message(s)>'}. Note that non-policy parameters have no warnings, so the 'warnings' string for the non-policy parameters is always empty. """ rtn_dict = {'policy': {'warnings': '', 'errors': ''}, 'behavior': {'warnings': '', 'errors': ''}, 'consumption': {'warnings': '', 'errors': ''}, 'growdiff_baseline': {'warnings': '', 'errors': ''}, 'growdiff_response': {'warnings': '', 'errors': ''}} # create GrowDiff objects gdiff_baseline = GrowDiff() try: gdiff_baseline.update_growdiff(user_mods['growdiff_baseline']) except ValueError as valerr_msg: rtn_dict['growdiff_baseline']['errors'] = valerr_msg.__str__() gdiff_response = GrowDiff() try: gdiff_response.update_growdiff(user_mods['growdiff_response']) except ValueError as valerr_msg: rtn_dict['growdiff_response']['errors'] = valerr_msg.__str__() # create Growfactors object growfactors = GrowFactors() gdiff_baseline.apply_to(growfactors) gdiff_response.apply_to(growfactors) # create Policy object pol = Policy(gfactors=growfactors) try: pol.implement_reform(user_mods['policy'], print_warnings=False, raise_errors=False) if using_puf: rtn_dict['policy']['warnings'] = pol.parameter_warnings rtn_dict['policy']['errors'] = pol.parameter_errors except ValueError as valerr_msg: rtn_dict['policy']['errors'] = valerr_msg.__str__() # create Behavior object behv = Behavior() try: behv.update_behavior(user_mods['behavior']) except ValueError as valerr_msg: rtn_dict['behavior']['errors'] = valerr_msg.__str__() # create Consumption object consump = Consumption() try: consump.update_consumption(user_mods['consumption']) except ValueError as valerr_msg: rtn_dict['consumption']['errors'] = valerr_msg.__str__() # return composite dictionary of warnings/errors return rtn_dict
def calculator_objects(year_n, start_year, use_puf_not_cps, use_full_sample, user_mods, behavior_allowed): """ This function assumes that the specified user_mods is a dictionary returned by the Calculator.read_json_param_objects() function. This function returns (calc1, calc2) where calc1 is pre-reform Calculator object calculated for year_n, and calc2 is post-reform Calculator object calculated for year_n. Set behavior_allowed to False when generating static results or set behavior_allowed to True when generating dynamic results. """ # pylint: disable=too-many-arguments,too-many-locals # pylint: disable=too-many-branches,too-many-statements check_user_mods(user_mods) # specify Consumption instance consump = Consumption() consump_assumptions = user_mods['consumption'] consump.update_consumption(consump_assumptions) # specify growdiff_baseline and growdiff_response growdiff_baseline = GrowDiff() growdiff_response = GrowDiff() growdiff_base_assumps = user_mods['growdiff_baseline'] growdiff_resp_assumps = user_mods['growdiff_response'] growdiff_baseline.update_growdiff(growdiff_base_assumps) growdiff_response.update_growdiff(growdiff_resp_assumps) # create pre-reform and post-reform GrowFactors instances growfactors_pre = GrowFactors() growdiff_baseline.apply_to(growfactors_pre) growfactors_post = GrowFactors() growdiff_baseline.apply_to(growfactors_post) growdiff_response.apply_to(growfactors_post) # create sample pd.DataFrame from specified input file and sampling scheme tbi_path = os.path.abspath(os.path.dirname(__file__)) if use_puf_not_cps: # first try TaxBrain deployment path input_path = 'puf.csv.gz' if not os.path.isfile(input_path): # otherwise try local Tax-Calculator deployment path input_path = os.path.join(tbi_path, '..', '..', 'puf.csv') sampling_frac = 0.05 sampling_seed = 2222 else: # if using cps input not puf input # first try Tax-Calculator code path input_path = os.path.join(tbi_path, '..', 'cps.csv.gz') if not os.path.isfile(input_path): # otherwise read from taxcalc package "egg" input_path = None # pragma: no cover full_sample = read_egg_csv('cps.csv.gz') # pragma: no cover sampling_frac = 0.03 sampling_seed = 180 if input_path: full_sample = pd.read_csv(input_path) if use_full_sample: sample = full_sample else: sample = full_sample.sample(frac=sampling_frac, random_state=sampling_seed) # create pre-reform Calculator instance if use_puf_not_cps: recs1 = Records(data=sample, gfactors=growfactors_pre) else: recs1 = Records.cps_constructor(data=sample, gfactors=growfactors_pre) policy1 = Policy(gfactors=growfactors_pre) calc1 = Calculator(policy=policy1, records=recs1, consumption=consump) while calc1.current_year < start_year: calc1.increment_year() calc1.calc_all() assert calc1.current_year == start_year # specify Behavior instance behv = Behavior() behavior_assumps = user_mods['behavior'] behv.update_behavior(behavior_assumps) # always prevent both behavioral response and growdiff response if behv.has_any_response() and growdiff_response.has_any_response(): msg = 'BOTH behavior AND growdiff_response HAVE RESPONSE' raise ValueError(msg) # optionally prevent behavioral response if behv.has_any_response() and not behavior_allowed: msg = 'A behavior RESPONSE IS NOT ALLOWED' raise ValueError(msg) # create post-reform Calculator instance if use_puf_not_cps: recs2 = Records(data=sample, gfactors=growfactors_post) else: recs2 = Records.cps_constructor(data=sample, gfactors=growfactors_post) policy2 = Policy(gfactors=growfactors_post) policy_reform = user_mods['policy'] policy2.implement_reform(policy_reform) calc2 = Calculator(policy=policy2, records=recs2, consumption=consump, behavior=behv) while calc2.current_year < start_year: calc2.increment_year() assert calc2.current_year == start_year # delete objects now embedded in calc1 and calc2 del sample del full_sample del consump del growdiff_baseline del growdiff_response del growfactors_pre del growfactors_post del behv del recs1 del recs2 del policy1 del policy2 # increment Calculator objects for year_n years and calculate for _ in range(0, year_n): calc1.increment_year() calc2.increment_year() calc1.calc_all() if calc2.behavior_has_response(): calc2 = Behavior.response(calc1, calc2) else: calc2.calc_all() # return calculated Calculator objects return (calc1, calc2)
def test_incorrect_growdiff_ctor(): with pytest.raises(ValueError): gdiff = GrowDiff(start_year=2000) with pytest.raises(ValueError): gdiff = GrowDiff(num_years=0)