예제 #1
0
def test_correct_Growfactors_usage():
    gf = Growfactors()
    pir = gf.price_inflation_rates(2013, 2020)
    assert len(pir) == 8
    wgr = gf.wage_growth_rates(2013, 2021)
    assert len(wgr) == 9
    val = gf.factor_value('AWAGE', 2013)
    assert val > 1.0
예제 #2
0
def test_growfactors_csv_values():
    """
    Test numerical contents of growfactors.csv file.
    """
    gfo = Growfactors()
    min_data_year = min(Records.PUFCSV_YEAR, Records.CPSCSV_YEAR)
    if min_data_year < Policy.JSON_START_YEAR:
        for gfname in Growfactors.VALID_NAMES:
            val = gfo.factor_value(gfname, min_data_year)
            assert val == 1
예제 #3
0
def test_proper_usage():
    """
    Test proper usage of Growfactors object.
    """
    gfo = Growfactors()
    pir = gfo.price_inflation_rates(2013, 2020)
    assert len(pir) == 8
    wgr = gfo.wage_growth_rates(2013, 2021)
    assert len(wgr) == 9
    val = gfo.factor_value('AWAGE', 2013)
    assert val > 1.0
예제 #4
0
def test_correct_Records_instantiation(cps_subsample):
    rec1 = Records.cps_constructor(data=cps_subsample)
    assert rec1
    assert np.all(rec1.MARS != 0)
    assert rec1.current_year == rec1.data_year
    sum_e00200_in_cps_year = rec1.e00200.sum()
    rec1.set_current_year(rec1.data_year + 1)
    sum_e00200_in_cps_year_plus_one = rec1.e00200.sum()
    assert sum_e00200_in_cps_year_plus_one == sum_e00200_in_cps_year
    wghts_path = os.path.join(Records.CUR_PATH, Records.CPS_WEIGHTS_FILENAME)
    wghts_df = pd.read_csv(wghts_path)
    ratio_path = os.path.join(Records.CUR_PATH, Records.PUF_RATIOS_FILENAME)
    ratio_df = pd.read_csv(ratio_path)
    ratio_df = ratio_df.transpose()
    benefit_path = os.path.join(Records.CUR_PATH,
                                Records.CPS_BENEFITS_FILENAME)
    benefit_df = pd.read_csv(benefit_path)
    rec2 = Records(data=cps_subsample,
                   exact_calculations=False,
                   gfactors=Growfactors(),
                   weights=wghts_df,
                   adjust_ratios=ratio_df,
                   benefits=benefit_df,
                   start_year=Records.CPSCSV_YEAR)
    assert rec2
    assert np.all(rec2.MARS != 0)
    assert rec2.current_year == rec2.data_year
예제 #5
0
def reform_warnings_errors(user_mods):
    """
    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 two STR:STR pairs:
    {'warnings': '<empty-or-message(s)>', 'errors': '<empty-or-message(s)>'}
    In each pair the second string is empty if there are no messages.
    Any returned messages are generated using current_law_policy.json
    information on known policy parameter names and parameter value ranges.

    Note that this function will return one or more error messages if
    the user_mods['policy'] dictionary contains any unknown policy
    parameter names or if any *_cpi parameters have values other than
    True or False.  These situations prevent implementing the policy
    reform specified in user_mods, and therefore, no range-related
    warnings or errors will be returned in this case.
    """
    rtn_dict = {
        'policy': {
            'warnings': '',
            'errors': ''
        },
        'behavior': {
            'warnings': '',
            'errors': ''
        }
    }
    # create Growfactors object
    gdiff_baseline = Growdiff()
    gdiff_baseline.update_growdiff(user_mods['growdiff_baseline'])
    gdiff_response = Growdiff()
    gdiff_response.update_growdiff(user_mods['growdiff_response'])
    growfactors = Growfactors()
    gdiff_baseline.apply_to(growfactors)
    gdiff_response.apply_to(growfactors)
    # create Policy object and implement reform
    pol = Policy(gfactors=growfactors)
    try:
        pol.implement_reform(user_mods['policy'],
                             print_warnings=False,
                             raise_errors=False)
        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 and implement revisions
    # Note that Behavior does not have a `parameter_warnings`
    # attribute.
    behv = Behavior()
    try:
        behv.update_behavior(user_mods['behavior'])
        rtn_dict['behavior']['errors'] = behv.parameter_errors
    except ValueError as valerr_msg:
        rtn_dict['behavior']['errors'] = valerr_msg.__str__()
    return rtn_dict
예제 #6
0
def test_chained_cpi_reform(offset):
    """
    Test that _cpi_offset policy parameter works as expected.
    """
    # specify reform without using cpi_offset parameter
    pem = 10000
    bare_reform = {2022: {'_II_em': [pem]}}
    with_reform = {2022: {'_II_em': [pem]}, 2020: {'_cpi_offset': [offset]}}
    syr = Policy.JSON_START_YEAR
    pol_bare = Policy(start_year=syr)
    pol_bare.implement_reform(bare_reform)
    pol_with = Policy(start_year=syr)
    pol_with.implement_reform(with_reform)
    # check relative _II_em values in the two reforms for several years
    pem_bare = pol_bare._II_em
    pem_with = pol_with._II_em
    if offset == 0:
        assert pem_with[2019 - syr] == pem_bare[2019 - syr]
        assert pem_with[2020 - syr] == pem_bare[2020 - syr]
        assert pem_with[2021 - syr] == pem_bare[2021 - syr]
        assert pem_with[2022 - syr] == pem
        assert pem_bare[2022 - syr] == pem
        assert pem_with[2023 - syr] == pem_bare[2023 - syr]
    elif offset < 0:
        assert pem_with[2019 - syr] == pem_bare[2019 - syr]
        assert pem_with[2020 - syr] == pem_bare[2020 - syr]
        assert pem_with[2021 - syr] < pem_bare[2021 - syr]
        assert pem_with[2022 - syr] == pem
        assert pem_bare[2022 - syr] == pem
        assert pem_with[2023 - syr] < pem_bare[2023 - syr]
    # check exact _II_em values for 2023, which are
    # equal to 2022 values indexed by 2022 inflation rates
    unchained_cpi = pol_bare.inflation_rates()[2022 - syr]
    assert pem_bare[2023 - syr] == round(pem * (1 + unchained_cpi), 2)
    chained_cpi = unchained_cpi + offset
    assert pem_with[2023 - syr] == round(pem * (1 + chained_cpi), 2)
    # check that _STD value for 2023 with chained CPI indexing is
    # equal to _STD value for 2023 when specifying chained CPI indexing
    # as a difference in assumed inflation rates
    if offset != 0:
        # ... compute _STD value using difference-in-growth-factors approach
        growfactors = Growfactors()
        growdiff = Growdiff()
        growdiff.update_growdiff({2020: {'_ACPIU': [offset]}})
        growdiff.apply_to(growfactors)
        pol = Policy(gfactors=growfactors, start_year=syr)
        pol.implement_reform(bare_reform)
        # ... compare the _STD values derived from the two approaches
        assert_allclose(pol_with._STD[2023 - syr],
                        pol._STD[2023 - syr],
                        atol=0.01,
                        rtol=0.0)
예제 #7
0
def test_update_after_use():
    """
    Test of improper update after Growfactors object has been used.
    """
    gfo = Growfactors()
    gfo.price_inflation_rates(gfo.first_year, gfo.last_year)
    with pytest.raises(ValueError):
        gfo.update('AWAGE', 2013, 0.01)
예제 #8
0
def test_correct_Records_instantiation_sample(puf_1991, weights_1991):
    sample = puf_1991.sample(frac=0.10)
    # instantiate Records object with no extrapolation
    rec1 = Records(data=sample, gfactors=None, weights=weights_1991)
    assert rec1
    assert np.all(rec1.MARS != 0)
    assert rec1.current_year == Records.PUF_YEAR
    sum_e00200_in_puf_year = rec1.e00200.sum()
    rec1.set_current_year(Records.PUF_YEAR + 1)
    sum_e00200_in_puf_year_plus_one = rec1.e00200.sum()
    assert sum_e00200_in_puf_year_plus_one == sum_e00200_in_puf_year
    # instantiate Records object with default extrapolation
    rec2 = Records(data=sample, gfactors=Growfactors(), weights=None)
    assert rec2
    assert np.all(rec2.MARS != 0)
    assert rec2.current_year == Records.PUF_YEAR
예제 #9
0
def test_correct_Records_instantiation(puf_1991, puf_1991_path, weights_1991):
    rec1 = Records(data=puf_1991_path, gfactors=None, weights=weights_1991)
    assert rec1
    assert np.all(rec1.MARS != 0)
    assert rec1.current_year == Records.PUF_YEAR
    sum_e00200_in_puf_year = rec1.e00200.sum()
    rec1.set_current_year(Records.PUF_YEAR + 1)
    sum_e00200_in_puf_year_plus_one = rec1.e00200.sum()
    assert sum_e00200_in_puf_year_plus_one == sum_e00200_in_puf_year
    rec2 = Records(data=puf_1991, gfactors=Growfactors(), weights=None)
    assert rec2
    assert np.all(rec2.MARS != 0)
    assert rec2.current_year == Records.PUF_YEAR
    adj_df = pd.read_csv(Records.ADJUST_RATIOS_PATH)
    adj_df = adj_df.transpose()
    rec3 = Records(data=puf_1991, weights=None, adjust_ratios=adj_df)
    assert rec3
    assert np.all(rec3.MARS != 0)
    assert rec3.current_year == Records.PUF_YEAR
예제 #10
0
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)
예제 #11
0
def test_improper_usage(bad_gf_file):
    """
    Tests of improper usage of Growfactors object.
    """
    with pytest.raises(ValueError):
        gfo = Growfactors(dict())
    with pytest.raises(ValueError):
        gfo = Growfactors(bad_gf_file.name)
    gfo = Growfactors()
    fyr = gfo.first_year
    lyr = gfo.last_year
    with pytest.raises(ValueError):
        gfo.price_inflation_rates(fyr - 1, lyr)
    with pytest.raises(ValueError):
        gfo.price_inflation_rates(fyr, lyr + 1)
    with pytest.raises(ValueError):
        gfo.price_inflation_rates(lyr, fyr)
    with pytest.raises(ValueError):
        gfo.wage_growth_rates(fyr - 1, lyr)
    with pytest.raises(ValueError):
        gfo.wage_growth_rates(fyr, lyr + 1)
    with pytest.raises(ValueError):
        gfo.wage_growth_rates(lyr, fyr)
    with pytest.raises(ValueError):
        gfo.factor_value('BADNAME', fyr)
    with pytest.raises(ValueError):
        gfo.factor_value('AWAGE', fyr - 1)
    with pytest.raises(ValueError):
        gfo.factor_value('AWAGE', lyr + 1)
예제 #12
0
def dropq_calculate(year_n, start_year, taxrec_df, user_mods, behavior_allowed,
                    mask_computed):
    """
    The dropq_calculate function assumes specified user_mods is
      a dictionary returned by the Calculator.read_json_parameter_files()
      function with an extra key:value pair that is specified as
      'gdp_elasticity': {'value': <float_value>}.
    The function returns (calc1, calc2, mask) where
      calc1 is pre-reform Calculator object calculated for year_n,
      calc2 is post-reform Calculator object calculated for year_n, and
      mask is boolean array if compute_mask=True or None otherwise
    """
    # pylint: disable=too-many-arguments,too-many-locals,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 pre-reform Calculator instance
    recs1 = Records(data=taxrec_df.copy(deep=True), 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

    # optionally compute mask
    if mask_computed:
        # create pre-reform Calculator instance with extra income
        recs1p = Records(data=taxrec_df.copy(deep=True),
                         gfactors=growfactors_pre)
        # add one dollar to total wages and salaries of each filing unit
        recs1p.e00200 += 1.0  # pylint: disable=no-member
        recs1p.e00200p += 1.0  # pylint: disable=no-member
        policy1p = Policy(gfactors=growfactors_pre)
        # create Calculator with recs1p and calculate for start_year
        calc1p = Calculator(policy=policy1p,
                            records=recs1p,
                            consumption=consump)
        while calc1p.current_year < start_year:
            calc1p.increment_year()
        calc1p.calc_all()
        assert calc1p.current_year == start_year
        # compute mask that shows which of the calc1 and calc1p results differ
        res1 = results(calc1.records)
        res1p = results(calc1p.records)
        mask = (res1.iitax != res1p.iitax)
    else:
        mask = None

    # 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
    recs2 = Records(data=taxrec_df.copy(deep=True), 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()
    calc2.calc_all()
    assert calc2.current_year == start_year

    # 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 and mask
    return (calc1, calc2, mask)
예제 #13
0
def calculate(year_n, start_year, use_puf_not_cps, use_full_sample, user_mods,
              behavior_allowed):
    """
    The calculate function assumes the specified user_mods is a dictionary
      returned by the Calculator.read_json_param_objects() function.
    The function returns (calc1, calc2, mask) where
      calc1 is pre-reform Calculator object calculated for year_n,
      calc2 is post-reform Calculator object calculated for year_n, and
      mask is boolean array marking records with reform-induced iitax diffs
    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
    stime = time.time()
    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 = 180
    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(  # pylint: disable=no-member
            frac=sampling_frac,
            random_state=sampling_seed)
    if use_puf_not_cps:
        print('puf-read-time= {:.1f}'.format(time.time() - stime))
    else:
        print('cps-read-time= {:.1f}'.format(time.time() - stime))

    # create pre-reform Calculator instance
    if use_puf_not_cps:
        recs1 = Records(data=copy.deepcopy(sample), gfactors=growfactors_pre)
    else:
        recs1 = Records.cps_constructor(data=copy.deepcopy(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

    # compute mask array
    res1 = calc1.dataframe(DIST_VARIABLES)
    if use_puf_not_cps:
        # create pre-reform Calculator instance with extra income
        recs1p = Records(data=copy.deepcopy(sample), gfactors=growfactors_pre)
        # add one dollar to the income of each filing unit to determine
        # which filing units undergo a resulting change in tax liability
        recs1p.e00200 += 1.0  # pylint: disable=no-member
        recs1p.e00200p += 1.0  # pylint: disable=no-member
        policy1p = Policy(gfactors=growfactors_pre)
        # create Calculator with recs1p and calculate for start_year
        calc1p = Calculator(policy=policy1p,
                            records=recs1p,
                            consumption=consump)
        while calc1p.current_year < start_year:
            calc1p.increment_year()
        calc1p.calc_all()
        assert calc1p.current_year == start_year
        # compute mask showing which of the calc1 and calc1p results differ;
        # mask is true if a filing unit's income tax liability changed after
        # a dollar was added to the filing unit's wage and salary income
        res1p = calc1p.dataframe(DIST_VARIABLES)
        mask = np.logical_not(  # pylint: disable=no-member
            np.isclose(res1.iitax, res1p.iitax, atol=0.001, rtol=0.0))
        assert np.any(mask)
    else:  # if use_cps_not_cps is False
        # indicate that no fuzzing of reform results is required
        mask = np.zeros(res1.shape[0], dtype=np.int8)

    # 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=copy.deepcopy(sample), gfactors=growfactors_post)
    else:
        recs2 = Records.cps_constructor(data=copy.deepcopy(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()
    calc2.calc_all()
    assert calc2.current_year == start_year

    # 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 and mask
    return (calc1, calc2, mask)
예제 #14
0
def calculate_baseline_and_reform(year_n, start_year, taxrec_df, user_mods):
    """
    calculate_baseline_and_reform function assumes specified user_mods is
    a dictionary returned by the Calculator.read_json_parameter_files()
    function with an extra key:value pair that is specified as
    'gdp_elasticity': {'value': <float_value>}.
    """
    # 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 pre-reform Calculator instance
    recs1 = Records(data=taxrec_df.copy(deep=True), 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

    # Create pre-reform Calculator instance with extra income
    recs1p = Records(data=taxrec_df.copy(deep=True), gfactors=growfactors_pre)
    # add one dollar to total wages and salaries of each filing unit
    recs1p.e00200 += 1.0  # pylint: disable=no-member
    policy1p = Policy(gfactors=growfactors_pre)
    calc1p = Calculator(policy=policy1p, records=recs1p, consumption=consump)
    while calc1p.current_year < start_year:
        calc1p.increment_year()
    calc1p.calc_all()
    assert calc1p.current_year == start_year

    # Construct mask to show which of the calc1 and calc1p results differ
    soit1 = results(calc1)
    soit1p = results(calc1p)
    mask = (soit1._iitax != soit1p._iitax)  # pylint: disable=protected-access

    # Specify Behavior instance
    behv = Behavior()
    behavior_assumps = user_mods['behavior']
    behv.update_behavior(behavior_assumps)

    # 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)

    # Create post-reform Calculator instance with behavior
    recs2 = Records(data=taxrec_df.copy(deep=True), 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()
    calc2.calc_all()
    assert calc2.current_year == start_year

    # Seed random number generator with a seed value based on user_mods
    seed = random_seed(user_mods)
    print('seed={}'.format(seed))
    np.random.seed(seed)  # pylint: disable=no-member

    # 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 results and mask
    soit1 = results(calc1)
    soit2 = results(calc2)
    return soit1, soit2, mask
예제 #15
0
def test_update_after_use():
    gf = Growfactors()
    pir = gf.price_inflation_rates(gf.first_year, gf.last_year)
    with pytest.raises(ValueError):
        gf.update('AWAGE', 2013, 0.01)
예제 #16
0
def test_incorrect_Growfactors_usage(bad_gf_file):
    with pytest.raises(ValueError):
        gf = Growfactors(dict())
    with pytest.raises(ValueError):
        gf = Growfactors(bad_gf_file.name)
    gf = Growfactors()
    with pytest.raises(ValueError):
        pir = gf.price_inflation_rates(2000, 2099)
    with pytest.raises(ValueError):
        pir = gf.price_inflation_rates(2009, 2099)
    with pytest.raises(ValueError):
        pir = gf.price_inflation_rates(2021, 2013)
    with pytest.raises(ValueError):
        wgr = gf.wage_growth_rates(2000, 2099)
    with pytest.raises(ValueError):
        wgr = gf.wage_growth_rates(2009, 2099)
    with pytest.raises(ValueError):
        wgr = gf.wage_growth_rates(2021, 2013)
    with pytest.raises(ValueError):
        val = gf.factor_value('BADNAME', 2020)
    with pytest.raises(ValueError):
        val = gf.factor_value('AWAGE', 2000)
    with pytest.raises(ValueError):
        val = gf.factor_value('AWAGE', 2099)
예제 #17
0
def calculate(year_n, start_year, use_puf_not_cps, use_full_sample, user_mods,
              behavior_allowed):
    """
    The calculate function assumes the specified user_mods is a dictionary
      returned by the Calculator.read_json_param_objects() function.
    The 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
    stime = time.time()
    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 = 180
    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(  # pylint: disable=no-member
            frac=sampling_frac,
            random_state=sampling_seed)
    if use_puf_not_cps:
        print('puf-read-time= {:.1f}'.format(time.time() - stime))
    else:
        print('cps-read-time= {:.1f}'.format(time.time() - stime))

    # 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)
예제 #18
0
def test_agg(tests_path):
    """
    Test current-law aggregate taxes using cps.csv file.
    """
    # pylint: disable=too-many-statements,too-many-locals
    nyrs = 10
    # create a baseline Policy object containing 2017_law.json parameters
    pre_tcja_jrf = os.path.join(tests_path, '..', 'reforms', '2017_law.json')
    pre_tcja = Calculator.read_json_param_objects(pre_tcja_jrf, None)
    baseline_policy = Policy()
    baseline_policy.implement_reform(pre_tcja['policy'])
    # create a Records object (rec) containing all cps.csv input records
    rec = Records.cps_constructor()
    # create a Calculator object using baseline policy and cps records
    calc = Calculator(policy=baseline_policy, records=rec)
    calc_start_year = calc.current_year
    # create aggregate diagnostic table (adt) as a Pandas DataFrame object
    adt = calc.diagnostic_table(nyrs)
    taxes_fullsample = adt.loc["Combined Liability ($b)"]
    # convert adt to a string with a trailing EOL character
    actual_results = adt.to_string() + '\n'
    # read expected results from file
    aggres_path = os.path.join(tests_path, 'cpscsv_agg_expect.txt')
    with open(aggres_path, 'r') as expected_file:
        txt = expected_file.read()
    expected_results = txt.rstrip('\n\t ') + '\n'  # cleanup end of file txt
    # ensure actual and expected results have no nonsmall differences
    if sys.version_info.major == 2:
        small = 0.0  # tighter test for Python 2.7
    else:
        small = 0.1  # looser test for Python 3.6
    diffs = nonsmall_diffs(actual_results.splitlines(True),
                           expected_results.splitlines(True), small)
    if diffs:
        new_filename = '{}{}'.format(aggres_path[:-10], 'actual.txt')
        with open(new_filename, 'w') as new_file:
            new_file.write(actual_results)
        msg = 'CPSCSV AGG RESULTS DIFFER\n'
        msg += '-------------------------------------------------\n'
        msg += '--- NEW RESULTS IN cpscsv_agg_actual.txt FILE ---\n'
        msg += '--- if new OK, copy cpscsv_agg_actual.txt to  ---\n'
        msg += '---                 cpscsv_agg_expect.txt     ---\n'
        msg += '---            and rerun test.                ---\n'
        msg += '-------------------------------------------------\n'
        raise ValueError(msg)
    # create aggregate diagnostic table using unweighted sub-sample of records
    cps_filepath = os.path.join(tests_path, '..', 'cps.csv.gz')
    fullsample = pd.read_csv(cps_filepath)
    rn_seed = 180  # to ensure sub-sample is always the same
    subfrac = 0.03  # sub-sample fraction
    subsample = fullsample.sample(frac=subfrac, random_state=rn_seed)
    rec_subsample = Records(data=subsample,
                            gfactors=Growfactors(),
                            weights=Records.CPS_WEIGHTS_FILENAME,
                            adjust_ratios=Records.CPS_RATIOS_FILENAME,
                            start_year=Records.CPSCSV_YEAR)
    calc_subsample = Calculator(policy=baseline_policy, records=rec_subsample)
    adt_subsample = calc_subsample.diagnostic_table(nyrs)
    # compare combined tax liability from full and sub samples for each year
    taxes_subsample = adt_subsample.loc["Combined Liability ($b)"]
    reltol = 0.01  # maximum allowed relative difference in tax liability
    # TODO: skip first year because of BUG in cps_weights.csv file
    taxes_subsample = taxes_subsample[1:]  # TODO: eliminate code
    taxes_fullsample = taxes_fullsample[1:]  # TODO: eliminate code
    if not np.allclose(
            taxes_subsample, taxes_fullsample, atol=0.0, rtol=reltol):
        msg = 'CPSCSV AGG RESULTS DIFFER IN SUB-SAMPLE AND FULL-SAMPLE\n'
        msg += 'WHEN subfrac={:.3f}, rtol={:.4f}, seed={}\n'.format(
            subfrac, reltol, rn_seed)
        it_sub = np.nditer(taxes_subsample, flags=['f_index'])
        it_all = np.nditer(taxes_fullsample, flags=['f_index'])
        while not it_sub.finished:
            cyr = it_sub.index + calc_start_year
            tax_sub = float(it_sub[0])
            tax_all = float(it_all[0])
            reldiff = abs(tax_sub - tax_all) / abs(tax_all)
            if reldiff > reltol:
                msgstr = ' year,sub,full,reldiff= {}\t{:.2f}\t{:.2f}\t{:.4f}\n'
                msg += msgstr.format(cyr, tax_sub, tax_all, reldiff)
            it_sub.iternext()
            it_all.iternext()
        raise ValueError(msg)
예제 #19
0
def run_nth_year_gdp_elast_model(year_n,
                                 start_year,
                                 taxrec_df,
                                 user_mods,
                                 return_json=True):
    """
    The run_nth_year_gdp_elast_model function assumes user_mods is a
    dictionary returned by the Calculator.read_json_parameter_files()
    function with an extra key:value pair that is specified as
    'gdp_elasticity': {'value': <float_value>}.
    """
    # pylint: disable=too-many-arguments,too-many-locals,too-many-statements

    check_user_mods(user_mods)

    # Only makes sense to run for budget years 1 through n-1 (not for year 0)
    assert year_n > 0

    # Specify value of gdp_elasticity
    gdp_elasticity = user_mods['gdp_elasticity']['value']

    # Specify Consumption instance
    consump = Consumption()
    consump_assumps = user_mods['consumption']
    consump.update_consumption(consump_assumps)

    # 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 pre-reform Calculator instance
    records1 = Records(data=taxrec_df.copy(deep=True),
                       gfactors=growfactors_pre)
    policy1 = Policy(gfactors=growfactors_pre)
    calc1 = Calculator(policy=policy1, records=records1, consumption=consump)
    while calc1.current_year < start_year:
        calc1.increment_year()
    assert calc1.current_year == start_year

    # Create post-reform Calculator instance
    records2 = Records(data=taxrec_df.copy(deep=True),
                       gfactors=growfactors_post)
    policy2 = Policy(gfactors=growfactors_post)
    policy_reform = user_mods['policy']
    policy2.implement_reform(policy_reform)
    calc2 = Calculator(policy=policy2, records=records2, consumption=consump)
    while calc2.current_year < start_year:
        calc2.increment_year()
    assert calc2.current_year == start_year

    # Seed random number generator with a seed value based on user_mods
    seed = random_seed(user_mods)
    np.random.seed(seed)  # pylint: disable=no-member
    for _ in range(0, year_n - 1):
        calc1.increment_year()
        calc2.increment_year()
    calc1.calc_all()
    calc2.calc_all()

    # Assert that the current year is one behind the year we are calculating
    assert (calc1.current_year + 1) == (start_year + year_n)
    assert (calc2.current_year + 1) == (start_year + year_n)

    # Compute gdp effect
    gdp_effect = proportional_change_gdp(calc1, calc2, gdp_elasticity)

    # Return gdp_effect results
    if return_json:
        gdp_df = pd.DataFrame(data=[gdp_effect], columns=['col0'])
        gdp_elast_names_i = [
            x + '_' + str(year_n) for x in GDP_ELAST_ROW_NAMES
        ]
        gdp_elast_total = create_json_table(gdp_df,
                                            row_names=gdp_elast_names_i,
                                            num_decimals=5)
        gdp_elast_total = dict((k, v[0]) for k, v in gdp_elast_total.items())
        return gdp_elast_total
    else:
        return gdp_effect