示例#1
0
class SimpleTaxIO():
    """
    Constructor for the simple tax input-output class.

    Parameters
    ----------
    input_filename: string
        name of required INPUT file.

    reform: None or string or dictionary
        None implies no reform (current-law policy), or
        string is name of optional json REFORM file, or
        dictionary suitable for passing to Policy.implement_reform() method.

    exact_calculations: boolean
        specifies whether or not exact tax calculations are done without
        any smoothing of "stair-step" provisions in income tax law.

    emulate_taxsim_2441_logic: boolean
        true implies emulation of questionable Internet-TAXSIM logic, which
        is necessary if the SimpleTaxIO class is being used in validation
        tests against Internet-TAXSIM output.

    output_records: boolean
        true implies write a CSV-formatted file containing for each
        INPUT filing unit the TAXYEAR values of each variable in the
        Records.USABLE_READ_VARS set.

    Raises
    ------
    ValueError:
        if file with input_filename is not a string or does not exist.
        if reform is neither None, string, nor dictionary.
        if earliest INPUT year before simtax start year.
        if latest INPUT year after simtax end year.

    Returns
    -------
    class instance: SimpleTaxIO
    """

    def __init__(self,
                 input_filename,
                 reform,
                 exact_calculations,
                 emulate_taxsim_2441_logic,
                 output_records):
        """
        SimpleTaxIO class constructor.
        """
        # pylint: disable=too-many-arguments
        # check that input_filename is a string
        if not isinstance(input_filename, str):
            msg = 'SimpleTaxIO.ctor input_filename is not a string'
            raise ValueError(msg)
        # construct output_filename and delete old output file if it exists
        # ... construct reform extension to output_filename
        if reform is None:
            ref = ''
            self._using_reform_file = True
        else:  # if reform is not None
            if isinstance(reform, str):
                if reform.endswith('.json'):
                    ref = '-{}'.format(reform[:-5])
                else:
                    ref = '-{}'.format(reform)
                self._using_reform_file = True
            elif isinstance(reform, dict):
                ref = ''
                self._using_reform_file = False
            else:
                msg = 'SimpleTaxIO.ctor reform is neither None, str, nor dict'
                raise ValueError(msg)
        # ... construct whole output_filename
        self._using_input_file = True
        self._output_filename = '{}.out-simtax{}'.format(input_filename, ref)
        if os.path.isfile(self._output_filename):
            os.remove(self._output_filename)  # pragma: no cover
        # check for existence of file named input_filename
        if not os.path.isfile(input_filename):
            msg = 'INPUT file named {} could not be found'
            raise ValueError(msg.format(input_filename))
        # read input file contents into self._input dictionary
        self._read_input(input_filename)
        self.policy = Policy()
        # implement reform if reform is specified
        if reform:
            if self._using_reform_file:
                param_dict = Calculator.read_json_param_objects(reform, None)
                r_pol = param_dict['policy']
            else:
                r_pol = reform
            self.policy.implement_reform(r_pol)
        # validate input variable values
        self._validate_input()
        self.calc = self._calc_object(exact_calculations,
                                      emulate_taxsim_2441_logic,
                                      output_records)

    def start_year(self):
        """
        Returns starting year for SimpleTaxIO calculations
        """
        return self.policy.start_year

    def end_year(self):
        """
        Returns ending year for SimpleTaxIO calculations
        """
        return self.policy.end_year

    def calculate(self, writing_output_file=False,
                  exact_output=False):
        """
        Calculate taxes for all INPUT lines and write or return OUTPUT lines.

        Output lines will be written to file if SimpleTaxIO constructor was
        passed an input_filename string and a reform string or None and if
        writing_output_file is True.

        Parameters
        ----------
        writing_output_file: boolean

        exact_output: boolean

        Returns
        -------
        output_lines: string
            empty string if OUTPUT lines are written to a file;
            otherwise output_lines contain all OUTPUT lines
        """
        # pylint: disable=too-many-locals
        # loop through self._year_set doing tax calculations and saving output
        output = {}  # dictionary indexed by Records index for filing unit
        for calcyr in self._year_set:
            if calcyr != self.calc.policy_current_year():
                self.calc.policy_current_year(calcyr)
                self.calc.records_current_year(calcyr)
            self.calc.calc_all()
            (mtr_ptax, mtr_itax,
             _) = self.calc.mtr(wrt_full_compensation=False)
            cr_taxyr = self.calc.array('FLPDYR')
            for idx in range(0, self.calc.array_len):
                indyr = cr_taxyr[idx]
                if indyr == calcyr:
                    ovar = SimpleTaxIO.extract_output(self.calc, idx,
                                                      exact=exact_output)
                    ovar[7] = 100 * mtr_itax[idx]
                    ovar[9] = 100 * mtr_ptax[idx]
                    output[idx] = ovar
        assert len(output) == len(self._input)
        # handle disposition of calculated output
        olines = ''
        writing_possible = self._using_input_file and self._using_reform_file
        if writing_possible and writing_output_file:
            SimpleTaxIO.write_output_file(
                output, self._output_filename)  # pragma: no cover
        else:
            idx_range = range(0, len(output))
            for idx in idx_range:
                olines += SimpleTaxIO.construct_output_line(output[idx])
        return olines

    def number_input_lines(self):
        """
        Return number of lines read from INPUT file.
        """
        return len(self._input)

    @staticmethod
    def show_iovar_definitions():
        """
        Write definitions of INPUT and OUTPUT variables to stdout.

        Parameters
        ----------
        none: void

        Returns
        -------
        nothing: void
        """
        ivd = ('**** SimpleTaxIO INPUT variables in Internet-TAXSIM format:\n'
               'Note that each INPUT variable must be an integer.\n'
               '(NEG) notation means that variable can be negative;\n'
               '      otherwise variable must be non-negative.\n'
               '[ 1] arbitrary id of income tax filing unit\n'
               '[ 2] calendar year of income tax filing\n'
               '[ 3] state code [MUST BE ZERO]\n'
               '[ 4] filing status [MUST BE 1, 2, or 3]\n'
               '     1=single, 2=married_filing_jointly, 3=head_of_household\n'
               '[ 5] total number of dependents\n'
               '[ 6] taxpayer/spouse age code: 100*taxpayer_age+spouse_age\n'
               '     where spouse_age is zero when there is no spouse\n'
               '[ 7] taxpayer wage and salary income\n'
               '[ 8] spouse wage and salary income\n'
               '[ 9] qualified dividend income\n'
               '[10] other property income (NEG)\n'
               '[11] pension benefits that are federally taxable\n'
               '[12] total social security (OASDI) benefits\n'
               '[13] other non-fed-taxable transfer income [MUST BE ZERO]\n'
               '[14] rent paid [MUST BE ZERO]\n'
               '[15] real-estate taxes paid: an AMT-preference item\n'
               '[16] other itemized deductions that are AMT-preference items\n'
               '[17] child care expenses\n'
               '[18] unemployment (UI) benefits received\n'
               '[19] number of dependents under age 17\n'
               '[20] itemized deductions that are not AMT-preference items\n'
               '[21] short-term capital gains or losses (NEG)\n'
               '[22] long-term capital gains or losses (NEG)\n')
        sys.stdout.write(ivd)
        ovd = ('**** SimpleTaxIO OUTPUT variables in Internet-TAXSIM format:\n'
               '[ 1] arbitrary id of income tax filing unit\n'
               '[ 2] calendar year of income tax filing\n'
               '[ 3] state code [ALWAYS ZERO]\n'
               '[ 4] federal income tax liability\n'
               '[ 5] state income tax liability [ALWAYS ZERO]\n'
               '[ 6] OASDI+HI payroll tax liability (sum of ee and er share)\n'
               '[ 7] marginal federal income tax rate as percent\n'
               '[ 8] marginal state income tax rate [ALWAYS ZERO]\n'
               '[ 9] marginal payroll tax rate as percent\n'
               '[10] federal adjusted gross income, AGI\n'
               '[11] unemployment (UI) benefits included in AGI\n'
               '[12] social security (OASDI) benefits included in AGI\n'
               '[13] [ALWAYS ZERO]\n'
               '[14] personal exemption after phase-out\n'
               '[15] phased-out (i.e., disallowed) personal exemption\n'
               '[16] phased-out (i.e., disallowed) itemized deduction\n'
               '[17] itemized deduction after phase-out '
               '(zero for non-itemizer)\n'
               '[18] federal regular taxable income\n'
               '[19] regular tax on regular taxable income '
               '(no special capital gains rates)\n'
               '     EXCEPT use special rates WHEN --exact OPTION SPECIFIED\n'
               '[20] [ALWAYS ZERO]\n'
               '[21] [ALWAYS ZERO]\n'
               '[22] child tax credit (adjusted)\n'
               '[23] child tax credit (refunded)\n'
               '[24] credit for child care expenses\n'
               '[25] federal earned income tax credit, EITC\n'
               '[26] federal AMT taxable income\n'
               '[27] federal AMT liability\n'
               '[28] federal income tax (excluding AMT) before credits\n')
        sys.stdout.write(ovd)

    @staticmethod
    def extract_output(calc, idx, exact=False, extract_weight=False):
        """
        Extracts tax output from Calculator object for one tax filing unit.

        Parameters
        ----------
        calc: Calculator
            Calculator object containing embedded Records object.

        idx: integer
            Records array index of the one tax filing unit.

        exact: boolean
            whether or not ovar[19] is exact regular tax on regular income.

        extract_weight: boolean
            whether or not to extract s006 sample weight into ovar[29].

        Returns
        -------
        ovar: dictionary of output variables indexed from 1 to OVAR_NUM,
            or from 1 to OVAR_NUM+1 if extract_weight is True,
            of from 1 to OVAR_NUM+? if debugging variables are included.

        Notes
        -----
        The value of each output variable is stored in the ovar dictionary,
        which is indexed as Internet-TAXSIM output variables are (where the
        index begins with one).
        """
        ovar = {}
        ovar[1] = calc.array('RECID')[idx]  # id for tax filing unit
        ovar[2] = calc.array('FLPDYR')[idx]  # year taxes are calculated
        ovar[3] = 0  # state code is always zero
        ovar[4] = calc.array('iitax')[idx]  # federal income tax liability
        ovar[5] = 0.0  # no state income tax calculation
        ovar[6] = calc.array('payrolltax')[idx]  # ee+er for OASDI+HI
        ovar[7] = 0.0  # marginal federal income tax rate as percent
        ovar[8] = 0.0  # no state income tax calculation
        ovar[9] = 0.0  # marginal payroll tax rate as percent
        ovar[10] = calc.array('c00100')[idx]  # federal AGI
        ovar[11] = calc.array('e02300')[idx]  # UI benefits in AGI
        ovar[12] = calc.array('c02500')[idx]  # OASDI benefits in AGI
        ovar[13] = 0.0  # always set zero-bracket amount to zero
        pre_phase_out_pe = calc.array('pre_c04600')[idx]
        post_phase_out_pe = calc.array('c04600')[idx]
        phased_out_pe = pre_phase_out_pe - post_phase_out_pe
        ovar[14] = post_phase_out_pe  # post-phase-out personal exemption
        ovar[15] = phased_out_pe  # personal exemption that is phased out
        # ovar[16] can be positive for non-itemizer:
        ovar[16] = calc.array('c21040')[idx]  # phased out itemized deduction
        # ovar[17] is zero for non-itemizer:
        ovar[17] = calc.array('c04470')[idx]  # post-phase-out item deduction
        ovar[18] = calc.array('c04800')[idx]  # federal regular taxable income
        # ovar[19] is regular tax on taxable income
        if exact:
            ovar[19] = calc.array('taxbc')[idx]
        else:  # Internet-TAXSIM ovar[19] that ignores special qdiv+ltcg rates
            ovar[19] = calc.array('c05200')[idx]  # tax from Sch X,Y,Z tables
        ovar[20] = 0.0  # always set exemption surtax to zero
        ovar[21] = 0.0  # always set general tax credit to zero
        ovar[22] = calc.array('c07220')[idx]  # child tax credit (adjusted)
        ovar[23] = calc.array('c11070')[idx]  # extra refunded child tax credit
        ovar[24] = calc.array('c07180')[idx]  # child care credit
        ovar[25] = calc.array('eitc')[idx]  # federal EITC
        ovar[26] = calc.array('c62100')[idx]  # federal AMT taxable income
        amt_liability = calc.array('c09600')[idx]  # federal AMT liability
        ovar[27] = amt_liability
        # ovar[28] is federal income tax before credits; the Tax-Calculator
        # calc.array('c05800')[idx] is this concept but includes AMT liability
        # while Internet-TAXSIM ovar[28] explicitly excludes AMT liability, so
        # we have the following:
        ovar[28] = calc.array('c05800')[idx] - amt_liability
        # add optional weight and debugging output to ovar dictionary
        if extract_weight:
            ovar[29] = calc.array('s006')[idx]  # sample weight
            num = SimpleTaxIO.OVAR_NUM + 1
        else:
            num = SimpleTaxIO.OVAR_NUM
        for dvar_name in SimpleTaxIO.DVAR_NAMES:
            num += 1
            dvar = calc.array(dvar_name)
            if dvar is None:
                msg = '{} "{}" {}'.format(  # pragma: no cover
                    'debugging variable name',
                    dvar_name,
                    'not in Records object')
                raise ValueError(msg)  # pragma: no cover
            else:
                ovar[num] = dvar[idx]
        return ovar

    @staticmethod
    def write_output_file(output, output_filename):
        """
        Write all output to file with output_filename.

        Parameters
        ----------
        output: dictionary of OUTPUT variables for each INPUT tax filing unit

        output_filename: string

        Returns
        -------
        nothing: void
        """
        with open(output_filename, 'w') as output_file:  # pragma: no cover
            idx_range = range(0, len(output))  # pragma: no cover
            for idx in idx_range:  # pragma: no cover
                outline = SimpleTaxIO.construct_output_line(
                    output[idx])  # pragma: no cover
                output_file.write(outline)  # pragma: no cover

    OVAR_NUM = 28
    DVAR_NAMES = [  # OPTIONAL DEBUGGING OUTPUT VARIABLE NAMES
        # '...',  # first debugging variable
        # '...',  # second debugging variable
        # etc.
        # '...'   # last debugging variable
    ]
    OVAR_FMT = {1: '{:d}.',  # add decimal point as in Internet-TAXSIM output
                2: ' {:.0f}',
                3: ' {:d}',
                4: ' {:.2f}',
                5: ' {:.2f}',
                6: ' {:.2f}',
                7: ' {:.2f}',
                8: ' {:.2f}',
                9: ' {:.2f}',
                10: ' {:.2f}',
                11: ' {:.2f}',
                12: ' {:.2f}',
                13: ' {:.2f}',
                14: ' {:.2f}',
                15: ' {:.2f}',
                16: ' {:.2f}',
                17: ' {:.2f}',
                18: ' {:.2f}',
                19: ' {:.2f}',
                20: ' {:.2f}',
                21: ' {:.2f}',
                22: ' {:.2f}',
                23: ' {:.2f}',
                24: ' {:.2f}',
                25: ' {:.2f}',
                26: ' {:.2f}',
                27: ' {:.2f}',
                28: ' {:.2f}'}

    @staticmethod
    def construct_output_line(output_dict):
        """
        Construct line of OUTPUT from a filing unit output_dict.

        Parameters
        ----------
        output_dict: dictionary
            calculated output values indexed from 1 to len(output_dict).

        Returns
        -------
        output_line: string
        """
        outline = ''
        for vnum in range(1, len(output_dict) + 1):
            fnum = min(vnum, SimpleTaxIO.OVAR_NUM)
            outline += SimpleTaxIO.OVAR_FMT[fnum].format(output_dict[vnum])
        outline += '\n'
        return outline

    # --- begin private methods of SimpleTaxIO class --- #

    IVAR_NUM = 22
    IVAR_NONNEG = {1: True, 2: True, 3: True, 4: True, 5: True,
                   6: True, 7: True, 8: True, 9: True, 10: False,
                   11: True, 12: True, 13: True, 14: True, 15: True,
                   16: True, 17: True, 18: True, 19: True, 20: True,
                   21: False, 22: False}  # True ==> value must be non-negative

    def _read_input(self, input_filename):
        """
        Read INPUT and save input variables in self._input dictionary.

        Parameters
        ----------
        input_filename: string
            name of simtax INPUT file.

        Raises
        ------
        ValueError:
            if INPUT variables are not in Internet-TAXSIM format.
            if INPUT variables have improper numeric type or value.

        Returns
        -------
        nothing: void

        Notes
        -----
        The integer value of each input variable is stored in the
        self._input dictionary, which is indexed by INPUT file line
        number and by INPUT file variable number (where both indexes
        begin with one).
        """
        self._input = {}  # indexed by line number and by variable number
        lnum = 0
        used_var1_strings = set()
        with open(input_filename, 'r') as input_file:
            for line in input_file:
                lnum += 1
                istrlist = line.split()
                if len(istrlist) != SimpleTaxIO.IVAR_NUM:
                    msg = ('simtax INPUT line {} has {} not '
                           '{} space-delimited variables')
                    raise ValueError(msg.format(lnum, len(istrlist),
                                                SimpleTaxIO.IVAR_NUM))
                vnum = 0
                vardict = {}
                for istr in istrlist:
                    vnum += 1
                    # convert istr to integer value
                    try:
                        val = int(istr)
                    except ValueError:
                        msg = ('simtax INPUT line {} variable {} has '
                               'value {} that is not an integer')
                        raise ValueError(msg.format(lnum, vnum, istr))
                    # check val for proper value range
                    if SimpleTaxIO.IVAR_NONNEG[vnum]:
                        if val < 0:
                            msg = ('var[{}]={} on line {} of simtax INPUT has '
                                   'a negative value')
                            raise ValueError(msg.format(vnum, val, lnum))
                    # check that var[1] is unique in INPUT file
                    if vnum == 1:
                        if istr in used_var1_strings:
                            msg = ('var[1]={} on line {} of simtax INPUT has '
                                   'already been used')
                            raise ValueError(msg.format(istr, lnum))
                        else:
                            used_var1_strings.add(istr)
                    # add val for vnum to vardict
                    vardict[vnum] = val
                # add lnum:vardict pair to self._input dictionary
                self._input[lnum] = vardict

    def _validate_input(self):
        """
        Validate INPUT variable values stored in self._input dictionary.

        Parameters
        ----------
        none: void

        Raises
        ------
        ValueError:
            if INPUT variable values are not in proper range of values.

        Returns
        -------
        nothing: void

        Notes
        -----
        The integer value of each input variable is stored in the
        self._input dictionary, which is indexed by INPUT file line
        number and by INPUT file variable number (where both indexes
        begin with one).
        """
        self._year_set = set()
        min_year = self.start_year()
        max_year = self.end_year()
        for lnum in range(1, len(self._input) + 1):
            var = self._input[lnum]
            year = var[2]
            if year < min_year or year > max_year:
                msg = ('var[2]={} on line {} of simtax INPUT is not in '
                       '[{},{}] Policy start-year, end-year range')
                raise ValueError(msg.format(year, lnum, min_year, max_year))
            if year not in self._year_set:
                self._year_set.add(year)
            state = var[3]
            if state != 0:
                msg = ('var[3]={} on line {} of simtax INPUT is not zero '
                       'to indicate no state income tax calculations')
                raise ValueError(msg.format(state, lnum))
            filing_status = var[4]
            if filing_status < 1 or filing_status > 3:
                msg = ('var[4]={} on line {} of simtax INPUT is not '
                       'in [1,3] filing-status range')
                raise ValueError(msg.format(filing_status, lnum))
            num_all_dependents = var[5]
            if filing_status == 3 and num_all_dependents == 0:
                msg = ('var[5]={} on line {} of simtax INPUT is not '
                       'positive when var[4] equals 3')
                raise ValueError(msg.format(num_all_dependents, lnum))
            agecode = var[6]
            if agecode < 100:  # using old Internet-TAXSIM agecode
                if ((filing_status == 2 and agecode > 2) or
                        (filing_status != 2 and agecode > 1)):
                    msg = ('var[6]={} on line {} of simtax INPUT is has '
                           'illegal value')
                    raise ValueError(msg.format(agecode, lnum))
            else:  # using new Internet-TAXSIM agecode
                pass
            transfers = var[13]
            if transfers != 0:
                msg = ('var[13]={} on line {} of simtax INPUT is not zero '
                       'to indicate no state income tax calculations')
                raise ValueError(msg.format(transfers, lnum))
            rent = var[14]
            if rent != 0:
                msg = ('var[14]={} on line {} of simtax INPUT is not zero '
                       'to indicate no state income tax calculations')
                raise ValueError(msg.format(rent, lnum))
            num_young_dependents = var[19]
            if num_young_dependents > num_all_dependents:
                msg = ('var[19]={} on line {} of simtax INPUT is not less '
                       'than or equal to var[5]={}')
                raise ValueError(msg.format(num_young_dependents, lnum,
                                            num_all_dependents))

    def _calc_object(self, exact_calcs,
                     emulate_taxsim_2441_logic, output_records):
        """
        Create and return Calculator object to conduct the tax calculations.

        Parameters
        ----------
        exact_calcs: boolean

        emulate_taxsim_2441_logic: boolean

        output_records: boolean

        Returns
        -------
        calc: Calculator
        """
        # create all-zeros dictionary and then list of all-zero dictionaries
        Records.read_var_info()
        zero_dict = {}
        for varname in Records.USABLE_READ_VARS:
            zero_dict[varname] = 0
        dict_list = [zero_dict for _ in range(0, len(self._input))]
        # use dict_list to create a Pandas DataFrame and Records object
        recsdf = pd.DataFrame(dict_list, dtype='int64')
        recsdf['MARS'] = recsdf['MARS'].add(1)  # because MARS==0 is illegal
        recs = Records(data=recsdf, exact_calculations=exact_calcs,
                       gfactors=None, weights=None,
                       start_year=self.policy.start_year)
        assert recs.array_length == len(self._input)
        # specify input for each tax filing unit in Records object
        lnum = 0
        for idx in range(0, recs.array_length):
            lnum += 1
            SimpleTaxIO._specify_input(recs, idx, self._input[lnum],
                                       emulate_taxsim_2441_logic)
        # optionally write Records.USABLE_READ_VARS content to file
        if output_records:
            recdf = pd.DataFrame()  # pragma: no cover
            for varname in Records.USABLE_READ_VARS:  # pragma: no cover
                vardata = getattr(recs, varname)  # pragma: no cover
                recdf[varname] = vardata  # pragma: no cover
            recdf.to_csv(re.sub('out-simtax', 'records',  # pragma: no cover
                                self._output_filename),
                         float_format='%.2f', index=False)
        # create Calculator object containing all tax filing units
        return Calculator(policy=self.policy, records=recs, sync_years=False)

    @staticmethod
    def _specify_input(recs, idx, ivar, emulate_taxsim_2441_logic):
        """
        Specifies recs values for index idx using ivar input variables.

        Parameters
        ----------
        recs: Records
            Records object containing a row for each tax filing unit.

        idx: integer
            recs index of the tax filing unit whose input is in ivar.

        ivar: dictionary
            input variables for a single tax filing unit.

        emulate_taxsim_2441_logic: boolean

        Returns
        -------
        nothing: void
        """
        recs.RECID[idx] = ivar[1]  # tax filing unit id
        recs.FLPDYR[idx] = ivar[2]  # tax year
        # no use of ivar[3], state code (always equal to zero)
        if ivar[4] == 3:  # head-of-household is 3 in SimpleTaxIO INPUT file
            mars_value = 4  # head-of-household is MARS=4 in Tax-Calculator
            num_taxpayers = 1
        else:  # if ivar[4] is 1=single or 2=married_filing_jointly
            mars_value = ivar[4]
            num_taxpayers = ivar[4]
        recs.MARS[idx] = mars_value  # income tax filing status
        num_dependents = ivar[5]  # total number of dependents
        num_eitc_qualified_kids = num_dependents  # simplifying assumption
        recs.EIC[idx] = min(num_eitc_qualified_kids, 3)
        total_num_exemptions = num_taxpayers + num_dependents
        recs.XTOT[idx] = total_num_exemptions
        if ivar[6] < 3:  # old coding of Internet-TAXSIM ivar[6]
            recs.age_head[idx] = 50
            recs.age_spouse[idx] = 50
            if ivar[6] >= 1:
                recs.age_head[idx] = 70
                if ivar[6] >= 2:
                    recs.age_spouse[idx] = 70
        else:  # new coding of Internet-TAXSIM ivar[6] since March 2016
            recs.age_head[idx] = ivar[6] // 100  # integer division
            recs.age_spouse[idx] = ivar[6] % 100  # division remainder
        recs.e00200p[idx] = ivar[7]  # wage+salary income of taxpayer
        recs.e00200s[idx] = ivar[8]  # wage+salary income of spouse
        recs.e00200[idx] = ivar[7] + ivar[8]  # combined wage+salary income
        recs.e00650[idx] = ivar[9]  # qualified dividend income
        recs.e00600[idx] = ivar[9]  # qual.div. included in ordinary dividends
        recs.e00300[idx] = ivar[10]  # other property income (+/-)
        recs.e01700[idx] = ivar[11]  # federally taxable pensions
        recs.e02400[idx] = ivar[12]  # gross social security benefits
        recs.e00400[idx] = ivar[13]  # federal tax-exempt interest
        # no use of ivar[14] because no state income tax calculations
        recs.e18500[idx] = ivar[15]  # real-estate (property) taxes paid
        recs.e18400[idx] = ivar[16]  # other AMT-preferred deductions
        recs.e32800[idx] = ivar[17]  # child care expenses (Form 2441)
        # approximate number of Form 2441 qualified persons associated with
        # the child care expenses specified by ivar[17] (Note that the exact
        # number is the number of dependents under age 13, but that is not
        # an Internet-TAXSIM input variable; hence the need to approximate.)
        if emulate_taxsim_2441_logic:
            recs.f2441[idx] = num_dependents  # all dependents of any age
        else:
            recs.f2441[idx] = ivar[19]  # number dependents under age 17
        recs.e02300[idx] = ivar[18]  # unemployment compensation received
        recs.n24[idx] = ivar[19]  # number dependents under age 17
        recs.e19200[idx] = ivar[20]  # AMT-nonpreferred deductions
        recs.p22250[idx] = ivar[21]  # short-term capital gains (+/-)
        recs.p23250[idx] = ivar[22]  # long-term capital gains (+/-)
示例#2
0
    def init(self, input_data, tax_year, reform, assump, growdiff_response,
             aging_input_data, exact_calculations):
        """
        TaxCalcIO class post-constructor method that completes initialization.

        Parameters
        ----------
        First four parameters are same as for TaxCalcIO constructor:
            input_data, tax_year, reform, assump.

        growdiff_response: Growdiff object or None
            growdiff_response Growdiff object is used only by the
            TaxCalcIO.growmodel_analysis method;
            must be None in all other cases.

        aging_input_data: boolean
            whether or not to extrapolate Records data from data year to
            tax_year.

        exact_calculations: boolean
            specifies whether or not exact tax calculations are done without
            any smoothing of "stair-step" provisions in the tax law.
        """
        # pylint: disable=too-many-arguments,too-many-locals
        # pylint: disable=too-many-statements,too-many-branches
        self.errmsg = ''
        # get parameter dictionaries from --reform and --assump files
        paramdict = Calculator.read_json_param_objects(reform, assump)
        # create Behavior object
        beh = Behavior()
        beh.update_behavior(paramdict['behavior'])
        self.behavior_has_any_response = beh.has_any_response()
        # create gdiff_baseline object
        gdiff_baseline = Growdiff()
        gdiff_baseline.update_growdiff(paramdict['growdiff_baseline'])
        # create Growfactors clp object that incorporates gdiff_baseline
        gfactors_clp = Growfactors()
        gdiff_baseline.apply_to(gfactors_clp)
        # specify gdiff_response object
        if growdiff_response is None:
            gdiff_response = Growdiff()
            gdiff_response.update_growdiff(paramdict['growdiff_response'])
        elif isinstance(growdiff_response, Growdiff):
            gdiff_response = growdiff_response
        else:
            gdiff_response = None
            msg = 'TaxCalcIO.more_init: growdiff_response is neither None '
            msg += 'nor a Growdiff object'
            self.errmsg += 'ERROR: {}\n'.format(msg)
        if gdiff_response is not None:
            some_gdiff_response = gdiff_response.has_any_response()
            if self.behavior_has_any_response and some_gdiff_response:
                msg = 'ASSUMP file cannot specify any "behavior" when using '
                msg += 'GrowModel or when ASSUMP file has "growdiff_response"'
                self.errmsg += 'ERROR: {}\n'.format(msg)
        # create Growfactors ref object that has both gdiff objects applied
        gfactors_ref = Growfactors()
        gdiff_baseline.apply_to(gfactors_ref)
        if gdiff_response is not None:
            gdiff_response.apply_to(gfactors_ref)
        # create Policy objects
        if self.specified_reform:
            pol = Policy(gfactors=gfactors_ref)
            try:
                pol.implement_reform(paramdict['policy'])
                self.errmsg += pol.reform_errors
            except ValueError as valerr_msg:
                self.errmsg += valerr_msg.__str__()
        else:
            pol = Policy(gfactors=gfactors_clp)
        clp = Policy(gfactors=gfactors_clp)
        # check for valid tax_year value
        if tax_year < pol.start_year:
            msg = 'tax_year {} less than policy.start_year {}'
            msg = msg.format(tax_year, pol.start_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        if tax_year > pol.end_year:
            msg = 'tax_year {} greater than policy.end_year {}'
            msg = msg.format(tax_year, pol.end_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        # any errors imply cannot proceed with calculations
        if self.errmsg:
            return
        # set policy to tax_year
        pol.set_year(tax_year)
        clp.set_year(tax_year)
        # read input file contents into Records objects
        if aging_input_data:
            if self.cps_input_data:
                recs = Records.cps_constructor(
                    gfactors=gfactors_ref,
                    exact_calculations=exact_calculations)
                recs_clp = Records.cps_constructor(
                    gfactors=gfactors_clp,
                    exact_calculations=exact_calculations)
            else:  # if not cps_input_data
                recs = Records(data=input_data,
                               gfactors=gfactors_ref,
                               exact_calculations=exact_calculations)
                recs_clp = Records(data=input_data,
                                   gfactors=gfactors_clp,
                                   exact_calculations=exact_calculations)
        else:  # input_data are raw data that are not being aged
            recs = Records(data=input_data,
                           gfactors=None,
                           exact_calculations=exact_calculations,
                           weights=None,
                           adjust_ratios=None,
                           start_year=tax_year)
            recs_clp = copy.deepcopy(recs)
        if tax_year < recs.data_year:
            msg = 'tax_year {} less than records.data_year {}'
            msg = msg.format(tax_year, recs.data_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        # create Calculator objects
        con = Consumption()
        con.update_consumption(paramdict['consumption'])
        self.calc = Calculator(policy=pol,
                               records=recs,
                               verbose=True,
                               consumption=con,
                               behavior=beh,
                               sync_years=aging_input_data)
        self.calc_clp = Calculator(policy=clp,
                                   records=recs_clp,
                                   verbose=False,
                                   consumption=con,
                                   sync_years=aging_input_data)
        # remember parameter dictionary for reform documentation
        self.param_dict = paramdict
示例#3
0
    def init(self, input_data, tax_year, baseline, reform, assump,
             growdiff_growmodel, aging_input_data, exact_calculations):
        """
        TaxCalcIO class post-constructor method that completes initialization.

        Parameters
        ----------
        First five are same as the first five of the TaxCalcIO constructor:
            input_data, tax_year, baseline, reform, assump.

        growdiff_growmodel: GrowDiff object or None
            growdiff_growmodel GrowDiff object is used only in the
            TaxCalcIO.growmodel_analysis method.

        aging_input_data: boolean
            whether or not to extrapolate Records data from data year to
            tax_year.

        exact_calculations: boolean
            specifies whether or not exact tax calculations are done without
            any smoothing of "stair-step" provisions in the tax law.
        """
        # pylint: disable=too-many-arguments,too-many-locals
        # pylint: disable=too-many-statements,too-many-branches
        self.errmsg = ''
        # get policy parameter dictionary from --baseline file
        basedict = Calculator.read_json_param_objects(baseline, None)
        # get assumption sub-dictionaries
        paramdict = Calculator.read_json_param_objects(None, assump)
        # get policy parameter dictionaries from --reform file(s)
        policydicts = list()
        if self.specified_reform:
            reforms = reform.split('+')
            for ref in reforms:
                pdict = Calculator.read_json_param_objects(ref, None)
                policydicts.append(pdict['policy'])
            paramdict['policy'] = policydicts[0]
        # remember parameters for reform documentation
        self.param_dict = paramdict
        self.policy_dicts = policydicts
        # create Behavior object
        beh = Behavior()
        try:
            beh.update_behavior(paramdict['behavior'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        self.behavior_has_any_response = beh.has_any_response()
        # create gdiff_baseline object
        gdiff_baseline = GrowDiff()
        try:
            gdiff_baseline.update_growdiff(paramdict['growdiff_baseline'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # create GrowFactors base object that incorporates gdiff_baseline
        gfactors_base = GrowFactors()
        gdiff_baseline.apply_to(gfactors_base)
        # specify gdiff_response object
        gdiff_response = GrowDiff()
        try:
            gdiff_response.update_growdiff(paramdict['growdiff_response'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # create GrowFactors ref object that has all gdiff objects applied
        gfactors_ref = GrowFactors()
        gdiff_baseline.apply_to(gfactors_ref)
        gdiff_response.apply_to(gfactors_ref)
        if growdiff_growmodel:
            growdiff_growmodel.apply_to(gfactors_ref)
        # create Policy objects:
        # ... the baseline Policy object
        base = Policy(gfactors=gfactors_base)
        try:
            base.implement_reform(basedict['policy'],
                                  print_warnings=False,
                                  raise_errors=False)
            self.errmsg += base.parameter_errors
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # ... the reform Policy object
        if self.specified_reform:
            pol = Policy(gfactors=gfactors_ref)
            for poldict in policydicts:
                try:
                    pol.implement_reform(poldict,
                                         print_warnings=False,
                                         raise_errors=False)
                    self.errmsg += pol.parameter_errors
                except ValueError as valerr_msg:
                    self.errmsg += valerr_msg.__str__()
        else:
            pol = Policy(gfactors=gfactors_base)
        # create Consumption object
        con = Consumption()
        try:
            con.update_consumption(paramdict['consumption'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # create GrowModel object
        self.growmodel = GrowModel()
        try:
            self.growmodel.update_growmodel(paramdict['growmodel'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # check for valid tax_year value
        if tax_year < pol.start_year:
            msg = 'tax_year {} less than policy.start_year {}'
            msg = msg.format(tax_year, pol.start_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        if tax_year > pol.end_year:
            msg = 'tax_year {} greater than policy.end_year {}'
            msg = msg.format(tax_year, pol.end_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        # any errors imply cannot proceed with calculations
        if self.errmsg:
            return
        # set policy to tax_year
        pol.set_year(tax_year)
        base.set_year(tax_year)
        # read input file contents into Records objects
        if aging_input_data:
            if self.cps_input_data:
                recs = Records.cps_constructor(
                    gfactors=gfactors_ref,
                    exact_calculations=exact_calculations)
                recs_base = Records.cps_constructor(
                    gfactors=gfactors_base,
                    exact_calculations=exact_calculations)
            else:  # if not cps_input_data but aging_input_data
                recs = Records(data=input_data,
                               gfactors=gfactors_ref,
                               exact_calculations=exact_calculations)
                recs_base = Records(data=input_data,
                                    gfactors=gfactors_base,
                                    exact_calculations=exact_calculations)
        else:  # input_data are raw data that are not being aged
            recs = Records(data=input_data,
                           gfactors=None,
                           exact_calculations=exact_calculations,
                           weights=None,
                           adjust_ratios=None,
                           start_year=tax_year)
            recs_base = copy.deepcopy(recs)
        if tax_year < recs.data_year:
            msg = 'tax_year {} less than records.data_year {}'
            msg = msg.format(tax_year, recs.data_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        # create Calculator objects
        self.calc = Calculator(policy=pol,
                               records=recs,
                               verbose=True,
                               consumption=con,
                               behavior=beh,
                               sync_years=aging_input_data)
        self.calc_base = Calculator(policy=base,
                                    records=recs_base,
                                    verbose=False,
                                    consumption=con,
                                    sync_years=aging_input_data)
    def init(self, input_data, tax_year, baseline, reform, assump,
             aging_input_data, exact_calculations):
        """
        TaxCalcIO class post-constructor method that completes initialization.

        Parameters
        ----------
        First five are same as the first five of the TaxCalcIO constructor:
            input_data, tax_year, baseline, reform, assump.

        aging_input_data: boolean
            whether or not to extrapolate Records data from data year to
            tax_year.

        exact_calculations: boolean
            specifies whether or not exact tax calculations are done without
            any smoothing of "stair-step" provisions in the tax law.
        """
        # pylint: disable=too-many-arguments,too-many-locals
        # pylint: disable=too-many-statements,too-many-branches
        self.errmsg = ''
        # get policy parameter dictionary from --baseline file
        basedict = Calculator.read_json_param_objects(baseline, None)
        # get assumption sub-dictionaries
        paramdict = Calculator.read_json_param_objects(None, assump)
        # get policy parameter dictionaries from --reform file(s)
        policydicts = list()
        if self.specified_reform:
            reforms = reform.split('+')
            for ref in reforms:
                pdict = Calculator.read_json_param_objects(ref, None)
                policydicts.append(pdict['policy'])
            paramdict['policy'] = policydicts[0]
        # remember parameters for reform documentation
        self.param_dict = paramdict
        self.policy_dicts = policydicts
        # create gdiff_baseline object
        gdiff_baseline = GrowDiff()
        try:
            gdiff_baseline.update_growdiff(paramdict['growdiff_baseline'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # create GrowFactors base object that incorporates gdiff_baseline
        gfactors_base = GrowFactors()
        gdiff_baseline.apply_to(gfactors_base)
        # specify gdiff_response object
        gdiff_response = GrowDiff()
        try:
            gdiff_response.update_growdiff(paramdict['growdiff_response'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # create GrowFactors ref object that has all gdiff objects applied
        gfactors_ref = GrowFactors()
        gdiff_baseline.apply_to(gfactors_ref)
        gdiff_response.apply_to(gfactors_ref)
        # create Policy objects:
        # ... the baseline Policy object
        base = Policy(gfactors=gfactors_base)
        try:
            base.implement_reform(basedict['policy'],
                                  print_warnings=False,
                                  raise_errors=False)
            self.errmsg += base.parameter_errors
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # ... the reform Policy object
        if self.specified_reform:
            pol = Policy(gfactors=gfactors_ref)
            for poldict in policydicts:
                try:
                    pol.implement_reform(poldict,
                                         print_warnings=False,
                                         raise_errors=False)
                    self.errmsg += pol.parameter_errors
                except ValueError as valerr_msg:
                    self.errmsg += valerr_msg.__str__()
        else:
            pol = Policy(gfactors=gfactors_base)
        # create Consumption object
        con = Consumption()
        try:
            con.update_consumption(paramdict['consumption'])
        except ValueError as valerr_msg:
            self.errmsg += valerr_msg.__str__()
        # check for valid tax_year value
        if tax_year < pol.start_year:
            msg = 'tax_year {} less than policy.start_year {}'
            msg = msg.format(tax_year, pol.start_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        if tax_year > pol.end_year:
            msg = 'tax_year {} greater than policy.end_year {}'
            msg = msg.format(tax_year, pol.end_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        # any errors imply cannot proceed with calculations
        if self.errmsg:
            return
        # set policy to tax_year
        pol.set_year(tax_year)
        base.set_year(tax_year)
        # read input file contents into Records objects
        if aging_input_data:
            if self.cps_input_data:
                recs = Records.cps_constructor(
                    gfactors=gfactors_ref,
                    exact_calculations=exact_calculations
                )
                recs_base = Records.cps_constructor(
                    gfactors=gfactors_base,
                    exact_calculations=exact_calculations
                )
            else:  # if not cps_input_data but aging_input_data
                recs = Records(
                    data=input_data,
                    gfactors=gfactors_ref,
                    exact_calculations=exact_calculations
                )
                recs_base = Records(
                    data=input_data,
                    gfactors=gfactors_base,
                    exact_calculations=exact_calculations
                )
        else:  # input_data are raw data that are not being aged
            recs = Records(data=input_data,
                           gfactors=None,
                           exact_calculations=exact_calculations,
                           weights=None,
                           adjust_ratios=None,
                           start_year=tax_year)
            recs_base = copy.deepcopy(recs)
        if tax_year < recs.data_year:
            msg = 'tax_year {} less than records.data_year {}'
            msg = msg.format(tax_year, recs.data_year)
            self.errmsg += 'ERROR: {}\n'.format(msg)
        # create Calculator objects
        self.calc = Calculator(policy=pol, records=recs,
                               verbose=True,
                               consumption=con,
                               sync_years=aging_input_data)
        self.calc_base = Calculator(policy=base, records=recs_base,
                                    verbose=False,
                                    consumption=con,
                                    sync_years=aging_input_data)
def generate_policy_revenues():
    from taxcalc.growfactors import GrowFactors
    from taxcalc.policy import Policy
    from taxcalc.records import Records
    from taxcalc.gstrecords import GSTRecords
    from taxcalc.corprecords import CorpRecords
    from taxcalc.parameters import ParametersBase
    from taxcalc.calculator import Calculator
    

    """
    for num in range(1, num_reforms):
        block_selected_dict[num]['selected_item']= block_widget_dict[num][1].get()
        block_selected_dict[num]['selected_value']= block_widget_dict[num][3].get()
        block_selected_dict[num]['selected_year']= block_widget_dict[num][2].get()
    print(block_selected_dict)
    """
    f = open('reform.json')
    block_selected_dict = json.load(f)
    print("block_selected_dict from json",block_selected_dict)
    #print(block_selected_dict)
    # create Records object containing pit.csv and pit_weights.csv input data
    #print("growfactors filename ", growfactors_filename)
    #recs = Records(data=data_filename, weights=weights_filename, gfactors=GrowFactors(growfactors_filename=growfactors_filename))
    #recs = Records(data=data_filename, weights=weights_filename, gfactors=GrowFactors(growfactors_filename=growfactors_filename))

    #recs.increment_year1(3.0)
    
    #grecs = GSTRecords()
    f = open('global_vars.json')
    vars = json.load(f)
        
    print("data_filename: ", vars['cit_data_filename'])
    print("weights_filename: ", vars['cit_weights_filename'])
    print("growfactors_filename: ", vars['GROWFACTORS_FILENAME'])
    print("policy_filename: ", vars['DEFAULTS_FILENAME'])
    # create CorpRecords object using cross-section data
    #crecs1 = CorpRecords(data='cit_cross.csv', weights='cit_cross_wgts1.csv')
    crecs1 = CorpRecords(data=vars['cit_data_filename'], weights=vars['cit_weights_filename'], gfactors=GrowFactors(growfactors_filename=vars['GROWFACTORS_FILENAME']))
    #crecs1 = CorpRecords(data=vars['cit_weights_filename'], weights=vars['cit_weights_filename'])

    # Note: weights argument is optional
    assert isinstance(crecs1, CorpRecords)
    assert crecs1.current_year == 2017
    
    # create Policy object containing current-law policy
    pol = Policy(DEFAULTS_FILENAME=vars['DEFAULTS_FILENAME'])
    
    # specify Calculator objects for current-law policy
    #calc1 = Calculator(policy=pol, records=recs, corprecords=crecs1,
    #                   gstrecords=grecs, verbose=False)
    calc1 = Calculator(policy=pol, corprecords=crecs1, verbose=False)    
    #calc1.increment_year1(3.8)
    assert isinstance(calc1, Calculator)
    assert calc1.current_year == 2017

    np.seterr(divide='ignore', invalid='ignore')
    
    pol2 = Policy(DEFAULTS_FILENAME=vars['DEFAULTS_FILENAME'])
    
    years, reform=read_reform_dict(block_selected_dict)
    print("reform dictionary: ",reform) 
    #reform = Calculator.read_json_param_objects('app01_reform.json', None)
    pol2.implement_reform(reform['policy'])
    
    #calc2 = Calculator(policy=pol2, records=recs, corprecords=crecs1,
    #                   gstrecords=grecs, verbose=False)
    calc2 = Calculator(policy=pol2, corprecords=crecs1, verbose=False)
    pit_adjustment_factor={}
    revenue_dict_cit={}
    revenue_amount_dict = {}

    calc1.calc_all()
        
           
    for year in range(2019, 2024):
        cols = []
        calc1.advance_to_year(year)       
        calc2.advance_to_year(year)
        # NOTE: calc1 now contains a PRIVATE COPY of pol and a PRIVATE COPY of recs,
        #       so we can continue to use pol and recs in this script without any
        #       concern about side effects from Calculator method calls on calc1.

        # Produce DataFrame of results using the calculator
        
        # First run the calculator for the corporate income tax
        calc1.calc_all()
        
        print("***** Year ", year)
        weighted_citax1 = calc1.weighted_total_cit('citax')                
        citax_collection_billions1 = weighted_citax1/10**9       
        citax_collection_str1 = '{0:.2f}'.format(citax_collection_billions1)
              
        print("The CIT Collection in billions is: ", citax_collection_billions1)
 
        # Produce DataFrame of results using cross-section
        calc2.calc_all()
       
        weighted_citax2 = calc2.weighted_total_cit('citax')                
        citax_collection_billions2 = weighted_citax2/10**9    
        citax_collection_str2 = '{0:.2f}'.format(citax_collection_billions2)
        # This is the difference in the collection due to the reform
        # This amount will now be allocated to dividends of PIT
        citax_diff_collection_billions2 = (citax_collection_billions2-citax_collection_billions1)
        citax_diff_collection_str2 = '{0:.2f}'.format(citax_diff_collection_billions2)
              
        print("The CIT Collection after reform billions is: ", citax_collection_billions2)

        print("The difference in CIT Collection in billions is: ", citax_diff_collection_billions2)

        # Process of allocation of difference in CIT profits to PIT 
        # in the form of Dividends
        # Dividends in this case is reported as Income from Other Sources
        # TOTAL_INCOME_OS in the PIT form
        
        # First get the unadjusted amounts
        
        # Now calculate the adjusted amounts
        # contribution to PIT Dividends
        proportion_change_dividend = (weighted_citax1 - weighted_citax2)/weighted_citax1       
        new_dividend_proportion_of_old = (1 + proportion_change_dividend)
        pit_adjustment_factor[year]=new_dividend_proportion_of_old
        # Store Results
        revenue_dict_cit[year]={}
        revenue_dict_cit[year]['current_law']=citax_collection_str1
        revenue_dict_cit[year]['reform']=citax_collection_str2      
        revenue_dict_cit[year]['difference']=citax_diff_collection_str2
        
    print(revenue_dict_cit)
    
    print("new_dividend_proportion_of_old ", pit_adjustment_factor)     

    # now update pit.csv with this proportion
    # start a new round of simulation for pit
    recs = Records(data=vars['pit_data_filename'], weights=vars['pit_weights_filename'], gfactors=GrowFactors(growfactors_filename=vars['GROWFACTORS_FILENAME']))
    
    # create Policy object containing current-law policy
    pol = Policy(DEFAULTS_FILENAME=vars['DEFAULTS_FILENAME'])
    
    # specify Calculator objects for current-law policy
    #calc1 = Calculator(policy=pol, records=recs, corprecords=crecs1,
    #                   gstrecords=grecs, verbose=False)
    calc1 = Calculator(policy=pol, records=recs, verbose=False)    
    #calc1.increment_year1(3.8)
    assert isinstance(calc1, Calculator)
    assert calc1.current_year == 2017

    np.seterr(divide='ignore', invalid='ignore')
    
    pol2 = Policy(DEFAULTS_FILENAME=vars['DEFAULTS_FILENAME'])
    
    #years, reform=read_reform_dict(block_selected_dict)
    #print("reform dictionary: ", reform) 
    #reform = Calculator.read_json_param_objects('app01_reform.json', None)
    pol2.implement_reform(reform['policy'])
    
    #calc2 = Calculator(policy=pol2, records=recs, corprecords=crecs1,
    #                   gstrecords=grecs, verbose=False)

    calc2 = Calculator(policy=pol2, records=recs, verbose=False)
        
    total_revenue_text={}
    reform_revenue_text={}
    revenue_dict_pit={}
    revenue_amount_dict = {}
    num = 1
    first_time = True
    i=1
    j=0
    #rows = []
    
    window = tk.Toplevel()
    window.geometry("800x400+140+140")
    display_table(window, revenue_dict_cit, revenue_dict_pit, header=True)

    #for year in range(years[0], years[-1]+1):            
    for year in range(2019, 2024):
        cols = []
        calc1.advance_to_year(year)       
        calc2.advance_to_year(year)
        # NOTE: calc1 now contains a PRIVATE COPY of pol and a PRIVATE COPY of recs,
        #       so we can continue to use pol and recs in this script without any
        #       concern about side effects from Calculator method calls on calc1.

        # Produce DataFrame of results using the calculator
        
        # First run the calculator for the corporate income tax
        calc1.calc_all()
        
        weighted_pitax1 = calc1.weighted_total_pit('pitax')                
        pitax_collection_billions1 = weighted_pitax1/10**9        
        pitax_collection_str1 = '{0:.2f}'.format(pitax_collection_billions1)
        
        print('\n\n\n')
        print(f'TAX COLLECTION FOR THE YEAR - {year} \n')   
        print("The PIT Collection in billions is: ", pitax_collection_billions1)       
        #total_revenue_text[year] = "PIT COLLECTION UNDER CURRENT LAW FOR THE YEAR - " + str(year)+" : "+str(pitax_collection_str1)+" bill"

        # Produce DataFrame of results using cross-section
        calc2.calc_all()
       
        
        weighted_pitax2 = calc2.weighted_total_pit('pitax')
        pitax_collection_billions2 = weighted_pitax2/10**9        
        pitax_collection_str2 = '{0:.2f}'.format(pitax_collection_billions2)
        pitax_diff_collection_billions2 = (pitax_collection_billions2-pitax_collection_billions1)        
        pitax_diff_collection_str2 = '{0:.2f}'.format(pitax_diff_collection_billions2)
        
        # Now calculate the adjusted amounts
        # contribution to PIT Dividends

        print("Total Income from Other Sources (bill) no adjustment is ", calc2.weighted_total_pit('TOTAL_INCOME_OS')/10**9 )       
        calc2.adjust_pit(pit_adjustment_factor[year])
        print("Total Income from Other Sources (bill) after adjustment is ", calc2.weighted_total_pit('TOTAL_INCOME_OS')/10**9 )
        
        calc2.calc_all()
        
        weighted_pitax3 = calc2.weighted_total_pit('pitax')     
        pitax_collection_billions3 = weighted_pitax3/10**9        
        pitax_collection_str3 = '{0:.2f}'.format(pitax_collection_billions3)

        pitax_diff_collection_billions3 = (pitax_collection_billions3-pitax_collection_billions1)        
        pitax_diff_collection_str3 = '{0:.2f}'.format(pitax_diff_collection_billions3)

        pitax_diff_collection_billions4 = (pitax_collection_billions3-pitax_collection_billions2)        
        pitax_diff_collection_str4 = '{0:.2f}'.format(pitax_diff_collection_billions4)
        
        #save the results
        revenue_dict_pit[year]={}
        revenue_dict_pit[year]['current_law']=pitax_collection_str1
        revenue_dict_pit[year]['reform']={}
        revenue_dict_pit[year]['reform']['unadjusted']=pitax_collection_str2
        revenue_dict_pit[year]['reform']['adjusted']=pitax_collection_str3
        revenue_dict_pit[year]['difference']=pitax_diff_collection_str3
        
        print('\n\n\n')       
        print(f'TAX COLLECTION FOR THE YEAR UNDER REFORM - {year} \n')       
        print("The PIT Collection in billions is: ", pitax_collection_billions2)
        print("The difference in PIT Collection in billions is: ", pitax_diff_collection_billions2)
        print('****AFTER ADJUSTMENT \n\n\n')
        print('TAX COLLECTION FOR THE YEAR UNDER REFORM WITH ADJUSTMENT \n')       
        print("The PIT Collection in billions after adjusting for the impact of CIT is: ", pitax_collection_billions3)        
        print("The difference in PIT Collection in billions after adjusting for the impact of CIT is: ", pitax_diff_collection_billions3)

        print("The impact of adjustment is: ", pitax_diff_collection_billions4)
        
        display_table(window, revenue_dict_cit, revenue_dict_pit, year=year, row=i)
        i=i+1
        #reverse the adjustment to obtain baseline
        calc2.adjust_pit(1/pit_adjustment_factor[year])
   
    display_table(window, revenue_dict_cit, revenue_dict_pit, footer=i)

    
    
    """
示例#6
0
 def __init__(
         self,
         input_data,
         tax_year,
         reform,
         assump,
         growdiff_response,  # =None in static analysis
         aging_input_data,
         exact_calculations):
     """
     TaxCalcIO class constructor.
     """
     # pylint: disable=too-many-arguments
     # pylint: disable=too-many-locals
     # pylint: disable=too-many-branches
     # pylint: disable=too-many-statements
     # check for existence of INPUT file
     if isinstance(input_data, six.string_types):
         # remove any leading directory path from INPUT filename
         fname = os.path.basename(input_data)
         # check if fname ends with ".csv"
         if fname.endswith('.csv'):
             inp = '{}-{}'.format(fname[:-4], str(tax_year)[2:])
         else:
             msg = 'INPUT file named {} does not end in .csv'
             raise ValueError(msg.format(fname))
         # check existence of INPUT file
         if not os.path.isfile(input_data):
             msg = 'INPUT file named {} could not be found'
             raise ValueError(msg.format(input_data))
     elif isinstance(input_data, pd.DataFrame):
         inp = 'df-{}'.format(str(tax_year)[2:])
     else:
         msg = 'INPUT is neither string nor Pandas DataFrame'
         raise ValueError(msg)
     # construct output_filename and delete old output file if it exists
     if reform is None:
         self._reform = False
         ref = ''
     elif isinstance(reform, six.string_types):
         self._reform = True
         # remove any leading directory path from REFORM filename
         fname = os.path.basename(reform)
         # check if fname ends with ".json"
         if fname.endswith('.json'):
             ref = '-{}'.format(fname[:-5])
         else:
             msg = 'REFORM file named {} does not end in .json'
             raise ValueError(msg.format(fname))
     else:
         msg = 'TaxCalcIO.ctor reform is neither None nor str'
         raise ValueError(msg)
     if assump is None:
         asm = ''
     elif isinstance(assump, six.string_types):
         # remove any leading directory path from ASSUMP filename
         fname = os.path.basename(assump)
         # check if fname ends with ".json"
         if fname.endswith('.json'):
             asm = '-{}'.format(fname[:-5])
         else:
             msg = 'ASSUMP file named {} does not end in .json'
             raise ValueError(msg.format(fname))
     else:
         msg = 'TaxCalcIO.ctor assump is neither None nor str'
         raise ValueError(msg)
     self._output_filename = '{}{}{}.csv'.format(inp, ref, asm)
     delete_file(self._output_filename)
     # get parameter dictionaries from --reform and --assump files
     param_dict = Calculator.read_json_param_files(reform, assump)
     # make sure no behavioral response is specified in --assump
     beh = Behavior()
     beh.update_behavior(param_dict['behavior'])
     if beh.has_any_response():
         msg = '--assump ASSUMP cannot assume any "behavior"'
         raise ValueError(msg)
     # make sure no growdiff_response is specified in --assump
     gdiff_response = Growdiff()
     gdiff_response.update_growdiff(param_dict['growdiff_response'])
     if gdiff_response.has_any_response():
         msg = '--assump ASSUMP cannot assume any "growdiff_response"'
         raise ValueError(msg)
     # create gdiff_baseline object
     gdiff_baseline = Growdiff()
     gdiff_baseline.update_growdiff(param_dict['growdiff_baseline'])
     # create Growfactors clp object that incorporates gdiff_baseline
     gfactors_clp = Growfactors()
     gdiff_baseline.apply_to(gfactors_clp)
     # specify gdiff_response object
     if growdiff_response is None:
         gdiff_response = Growdiff()
     elif isinstance(growdiff_response, Growdiff):
         gdiff_response = growdiff_response
     else:
         msg = 'TaxCalcIO.ctor growdiff_response is neither None nor {}'
         raise ValueError(msg.format('a Growdiff object'))
     # create Growfactors ref object that has both gdiff objects applied
     gfactors_ref = Growfactors()
     gdiff_baseline.apply_to(gfactors_ref)
     gdiff_response.apply_to(gfactors_ref)
     # create Policy object and implement reform if specified
     if self._reform:
         pol = Policy(gfactors=gfactors_ref)
         pol.implement_reform(param_dict['policy'])
         clp = Policy(gfactors=gfactors_clp)
     else:
         pol = Policy(gfactors=gfactors_clp)
     # check for valid tax_year value
     if tax_year < pol.start_year:
         msg = 'tax_year {} less than policy.start_year {}'
         raise ValueError(msg.format(tax_year, pol.start_year))
     if tax_year > pol.end_year:
         msg = 'tax_year {} greater than policy.end_year {}'
         raise ValueError(msg.format(tax_year, pol.end_year))
     # set policy to tax_year
     pol.set_year(tax_year)
     if self._reform:
         clp.set_year(tax_year)
     # read input file contents into Records object(s)
     if aging_input_data:
         if self._reform:
             recs = Records(data=input_data,
                            gfactors=gfactors_ref,
                            exact_calculations=exact_calculations)
             recs_clp = Records(data=input_data,
                                gfactors=gfactors_clp,
                                exact_calculations=exact_calculations)
         else:
             recs = Records(data=input_data,
                            gfactors=gfactors_clp,
                            exact_calculations=exact_calculations)
     else:  # input_data are raw data that are not being aged
         recs = Records(data=input_data,
                        exact_calculations=exact_calculations,
                        gfactors=None,
                        adjust_ratios=None,
                        weights=None,
                        start_year=tax_year)
         if self._reform:
             recs_clp = copy.deepcopy(recs)
     # create Calculator object(s)
     con = Consumption()
     con.update_consumption(param_dict['consumption'])
     self._calc = Calculator(policy=pol,
                             records=recs,
                             verbose=True,
                             consumption=con,
                             sync_years=aging_input_data)
     if self._reform:
         self._calc_clp = Calculator(policy=clp,
                                     records=recs_clp,
                                     verbose=False,
                                     consumption=con,
                                     sync_years=aging_input_data)