def growmodel_analysis(input_data, tax_year,
                           baseline, reform, assump,
                           aging_input_data, exact_calculations,
                           writing_output_file=False,
                           output_tables=False,
                           output_graphs=False,
                           output_ceeu=False,
                           dump_varset=None,
                           output_dump=False,
                           output_sqldb=False):
        """
        High-level logic for dynamic analysis using GrowModel class.

        Parameters
        ----------
        First five parameters are same as the first five parameters of
        the TaxCalcIO.init method.

        Last seven parameters are same as the first seven parameters of
        the TaxCalcIO.analyze method.

        Returns
        -------
        Nothing
        """
        # pylint: disable=too-many-arguments,too-many-locals
        progress = 'STARTING ANALYSIS FOR YEAR {}'
        gdiff_dict = {Policy.JSON_START_YEAR: {}}
        for year in range(Policy.JSON_START_YEAR, tax_year + 1):
            print(progress.format(year))  # pylint: disable=superfluous-parens
            # specify growdiff_response using gdiff_dict
            growdiff_response = Growdiff()
            growdiff_response.update_growdiff(gdiff_dict)
            gd_dict = TaxCalcIO.annual_analysis(input_data, tax_year,
                                                baseline, reform, assump,
                                                aging_input_data,
                                                exact_calculations,
                                                growdiff_response, year,
                                                writing_output_file,
                                                output_tables,
                                                output_graphs,
                                                output_ceeu,
                                                dump_varset,
                                                output_dump,
                                                output_sqldb)
            gdiff_dict[year + 1] = gd_dict
Exemple #2
0
    def dynamic_analysis(input_data,
                         tax_year,
                         reform,
                         assump,
                         aging_input_data,
                         exact_calculations,
                         writing_output_file=False,
                         output_graph=False,
                         output_ceeu=False,
                         output_dump=False):
        """
        High-level logic for dyanamic tax analysis.

        Parameters
        ----------
        First six parameters are same as the first six parameters of
        the TaxCalcIO constructor.

        Last four parameters are same as the first four parameters of
        the TaxCalcIO static_analysis method.

        Returns
        -------
        Nothing
        """
        # pylint: disable=too-many-arguments
        # pylint: disable=superfluous-parens
        progress = 'STARTING ANALYSIS FOR YEAR {}'
        gdiff_dict = {Policy.JSON_START_YEAR: {}}
        for year in range(Policy.JSON_START_YEAR, tax_year + 1):
            print(progress.format(year))
            # specify growdiff_response using gdiff_dict
            growdiff_response = Growdiff()
            growdiff_response.update_growdiff(gdiff_dict)
            gd_dict = TaxCalcIO.annual_analysis(
                input_data, tax_year, reform, assump, aging_input_data,
                exact_calculations, growdiff_response, year,
                writing_output_file, output_graph, output_ceeu, output_dump)
            gdiff_dict[year + 1] = gd_dict
Exemple #3
0
 def with_suffix(gdict, growdiff_baseline_dict, growdiff_response_dict):
     """
     Return param_base:year dictionary having only suffix parameters.
     """
     if bool(growdiff_baseline_dict) or bool(growdiff_response_dict):
         gdiff_baseline = Growdiff()
         gdiff_baseline.update_growdiff(growdiff_baseline_dict)
         gdiff_response = Growdiff()
         gdiff_response.update_growdiff(growdiff_response_dict)
         growfactors = Growfactors()
         gdiff_baseline.apply_to(growfactors)
         gdiff_response.apply_to(growfactors)
     else:
         growfactors = None
     pol = Policy(gfactors=growfactors)
     pol.ignore_reform_errors()
     odict = dict()
     for param in gdict.keys():
         odict[param] = dict()
         for year in sorted(gdict[param].keys()):
             odict[param][year] = dict()
             for suffix in gdict[param][year].keys():
                 plist = getattr(pol, param).tolist()
                 dvals = plist[int(year) - Policy.JSON_START_YEAR]
                 odict[param][year] = [dvals]
                 idx = Policy.JSON_REFORM_SUFFIXES[suffix]
                 odict[param][year][0][idx] = gdict[param][year][suffix]
                 udict = {int(year): {param: odict[param][year]}}
                 pol.implement_reform(udict)
     return odict
    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
Exemple #5
0
    def reform_documentation(params):
        """
        Generate reform documentation.

        Parameters
        ----------
        params: dict
            compound dictionary structured as dict returned from
            the static Calculator method read_json_param_objects()

        Returns
        -------
        doc: String
            the documentation for the policy reform specified in params
        """

        # pylint: disable=too-many-statements,too-many-branches

        # nested function used only in reform_documentation
        def param_doc(years, change, base):
            """
            Parameters
            ----------
            years: list of change years
            change: dictionary of parameter changes
            base: Policy or Growdiff object with baseline values
            syear: parameter start calendar year

            Returns
            -------
            doc: String
            """

            # nested function used only in param_doc
            def lines(text, num_indent_spaces, max_line_length=77):
                """
                Return list of text lines, each one of which is no longer
                than max_line_length, with the second and subsequent lines
                being indented by the number of specified num_indent_spaces;
                each line in the list ends with the '\n' character
                """
                if len(text) < max_line_length:
                    # all text fits on one line
                    line = text + '\n'
                    return [line]
                # all text does not fix on one line
                first_line = True
                line_list = list()
                words = text.split()
                while words:
                    if first_line:
                        line = ''
                        first_line = False
                    else:
                        line = ' ' * num_indent_spaces
                    while (words
                           and (len(words[0]) + len(line)) < max_line_length):
                        line += words.pop(0) + ' '
                    line = line[:-1] + '\n'
                    line_list.append(line)
                return line_list

            # begin main logic of param_doc
            # pylint: disable=too-many-nested-blocks
            assert len(years) == len(change.keys())
            basevals = getattr(base, '_vals', None)
            assert isinstance(basevals, dict)
            doc = ''
            for year in years:
                # write year
                base.set_year(year)
                doc += '{}:\n'.format(year)
                # write info for each param in year
                for param in sorted(change[year].keys()):
                    # ... write param:value line
                    pval = change[year][param]
                    if isinstance(pval, list):
                        pval = pval[0]
                        if basevals[param]['boolean_value']:
                            if isinstance(pval, list):
                                pval = [
                                    True if item else False for item in pval
                                ]
                            else:
                                pval = bool(pval)
                    doc += ' {} : {}\n'.format(param, pval)
                    # ... write optional param-index line
                    if isinstance(pval, list):
                        pval = basevals[param]['col_label']
                        pval = [str(item) for item in pval]
                        doc += ' ' * (4 + len(param)) + '{}\n'.format(pval)
                    # ... write name line
                    if param.endswith('_cpi'):
                        rootparam = param[:-4]
                        name = '{} inflation indexing status'.format(rootparam)
                    else:
                        name = basevals[param]['long_name']
                    for line in lines('name: ' + name, 6):
                        doc += '  ' + line
                    # ... write optional desc line
                    if not param.endswith('_cpi'):
                        desc = basevals[param]['description']
                        for line in lines('desc: ' + desc, 6):
                            doc += '  ' + line
                    # ... write baseline_value line
                    if isinstance(base, Policy):
                        if param.endswith('_cpi'):
                            rootparam = param[:-4]
                            bval = basevals[rootparam].get(
                                'cpi_inflated', False)
                        else:
                            bval = getattr(base, param[1:], None)
                            if isinstance(bval, np.ndarray):
                                # pylint: disable=no-member
                                bval = bval.tolist()
                                if basevals[param]['boolean_value']:
                                    bval = [
                                        True if item else False
                                        for item in bval
                                    ]
                            elif basevals[param]['boolean_value']:
                                bval = bool(bval)
                        doc += '  baseline_value: {}\n'.format(bval)
                    else:  # if base is Growdiff object
                        # all Growdiff parameters have zero as default value
                        doc += '  baseline_value: 0.0\n'
            return doc

        # begin main logic of reform_documentation
        # create Policy object with pre-reform (i.e., baseline) values
        # ... create gdiff_baseline object
        gdb = Growdiff()
        gdb.update_growdiff(params['growdiff_baseline'])
        # ... create Growfactors clp object that incorporates gdiff_baseline
        gfactors_clp = Growfactors()
        gdb.apply_to(gfactors_clp)
        # ... create Policy object containing pre-reform parameter values
        clp = Policy(gfactors=gfactors_clp)
        # generate documentation text
        doc = 'REFORM DOCUMENTATION\n'
        doc += 'Baseline Growth-Difference Assumption Values by Year:\n'
        years = sorted(params['growdiff_baseline'].keys())
        if years:
            doc += param_doc(years, params['growdiff_baseline'], gdb)
        else:
            doc += 'none: using default baseline growth assumptions\n'
        doc += 'Policy Reform Parameter Values by Year:\n'
        years = sorted(params['policy'].keys())
        if years:
            doc += param_doc(years, params['policy'], clp)
        else:
            doc += 'none: using current-law policy parameters\n'
        return doc
Exemple #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)