PSLmodels / ParamTools

Library for parameter processing and validation with a focus on computational modeling projects
https://paramtools.dev
MIT License
19 stars 14 forks source link

Add internal attribute on all extend values #102

Closed hdoupe closed 4 years ago

hdoupe commented 4 years ago

PT can automatically extend parameter values using the the label label_to_extend and its range of possible values. This PR adds an attribute to each value object that is added automatically. It could be helpful to know which values are added automatically and which are set for a specific reason:

In [1]: from taxcalc import Policy                                                                                                                                                            

In [2]: pol = Policy()                                                                                                                                                                        

In [3]: pol.STD                                                                                                                                                                               
Out[3]: array([[ 6100., 12200.,  6100.,  8950., 12200.]])

In [4]: pol.select_eq("STD", pt_extend=True)                                                                                                                                                  
Out[4]: 
[{'MARS': 'single', 'year': 2027, 'pt_extend': True, 'value': 7805.55},
 {'MARS': 'single', 'year': 2028, 'pt_extend': True, 'value': 7961.66},
 {'MARS': 'single', 'year': 2029, 'pt_extend': True, 'value': 8119.3},
 {'MARS': 'single', 'year': 2030, 'pt_extend': True, 'value': 8280.87},
 {'MARS': 'mjoint', 'year': 2027, 'pt_extend': True, 'value': 15612.12},
 {'MARS': 'mjoint', 'year': 2028, 'pt_extend': True, 'value': 15924.36},
 {'MARS': 'mjoint', 'year': 2029, 'pt_extend': True, 'value': 16239.66},
 {'MARS': 'mjoint', 'year': 2030, 'pt_extend': True, 'value': 16562.83},
 {'MARS': 'mseparate', 'year': 2027, 'pt_extend': True, 'value': 7805.55},
 {'MARS': 'mseparate', 'year': 2028, 'pt_extend': True, 'value': 7961.66},
 {'MARS': 'mseparate', 'year': 2029, 'pt_extend': True, 'value': 8119.3},
 {'MARS': 'mseparate', 'year': 2030, 'pt_extend': True, 'value': 8280.87},
 {'MARS': 'headhh', 'year': 2027, 'pt_extend': True, 'value': 11493.57},
 {'MARS': 'headhh', 'year': 2028, 'pt_extend': True, 'value': 11723.44},
 {'MARS': 'headhh', 'year': 2029, 'pt_extend': True, 'value': 11955.56},
 {'MARS': 'headhh', 'year': 2030, 'pt_extend': True, 'value': 12193.48},
 {'MARS': 'widow', 'year': 2027, 'pt_extend': True, 'value': 15612.12},
 {'MARS': 'widow', 'year': 2028, 'pt_extend': True, 'value': 15924.36},
 {'MARS': 'widow', 'year': 2029, 'pt_extend': True, 'value': 16239.66},
 {'MARS': 'widow', 'year': 2030, 'pt_extend': True, 'value': 16562.83}]

In [5]:  
hdoupe commented 4 years ago

cc @Peter-Metz let me know if something like this would be helpful for the CPI_offset problem in taxcalc. I'm still thinking through how this should work. So, some parts of this PR may change.

Peter-Metz commented 4 years ago

@hdoupe this would be great! I'm doing something similar now in my branch, but having pt_extend would simplify things.

hdoupe commented 4 years ago

Great, I did make a couple changes to the API. The idea is pretty much the same though.

Instead of pt_extend, I am using _auto. I also added a clobber keyword argument to the adjust method that may be helpful for you. This makes it possible to do something like this:

import paramtools

class TaxParams(paramtools.Parameters):
    defaults = {
        "schema": {
            "labels": {
                "year": {
                    "type": "int",
                    "validators": {"range": {"min": 2013, "max": 2027}}
                },
                "marital_status": {
                    "type": "str",
                    "validators": {"choice": {"choices": ["single", "joint"]}}
                },
            }
        },
        "standard_deduction": {
            "title": "Standard deduction amount",
            "description": "Amount filing unit can use as a standard deduction.",
            "type": "float",

            # Set indexed to True to extend standard_deduction with the built-in
            # extension logic.
            "indexed": True,

            "value": [
                {"year": 2017, "marital_status": "single", "value": 6350},
                {"year": 2017, "marital_status": "joint", "value": 12700},
                {"year": 2018, "marital_status": "single", "value": 12000},
                {"year": 2018, "marital_status": "joint", "value": 24000},
                {"year": 2026, "marital_status": "single", "value": 7685},
                {"year": 2026, "marital_status": "joint", "value": 15369}],
            "validators": {
                "range": {
                    "min": 0,
                    "max": 9e+99
                }
            }
        },
    }
    array_first = True
    label_to_extend = "year"
    # Activate use of extend_func method.
    uses_extend_func = True
    # inflation rates from Tax-Calculator v2.5.0
    index_rates = {
        2013: 0.0148,
        2014: 0.0159,
        2015: 0.0012,
        2016: 0.0127,
        2017: 0.0187,
        2018: 0.0224,
        2019: 0.0186,
        2020: 0.0233,
        2021: 0.0229,
        2022: 0.0228,
        2023: 0.0221,
        2024: 0.0211,
        2025: 0.0209,
        2026: 0.0211,
        2027: 0.0208,
        2028: 0.021,
        2029: 0.021
    }

params = TaxParams()
params.standard_deduction

# array([[ 6074.92, 12149.84],
#        [ 6164.83, 12329.66],
#        [ 6262.85, 12525.7 ],
#        [ 6270.37, 12540.73],
#        [ 6350.  , 12700.  ],
#        [12000.  , 24000.  ],
#        [12268.8 , 24537.6 ],
#        [12497.  , 24994.  ],
#        [12788.18, 25576.36],
#        [13081.03, 26162.06],
#        [13379.28, 26758.55],
#        [13674.96, 27349.91],
#        [13963.5 , 27926.99],
#        [ 7685.  , 15369.  ],
#        [ 7847.15, 15693.29]])

Then, update and apply the rates like this:

offset = 0.0025
for year, rate in params.index_rates.items():
    params.index_rates[year] = rate + offset

automatically_added = params.select_eq(
    "standard_deduction", strict=True, _auto=True
)

params.delete(
    {
        "standard_deduction": automatically_added
    }
)

params.standard_deduction

# array([[ 6015.22, 12030.41],
#        [ 6119.28, 12238.54],
#        [ 6231.87, 12463.73],
#        [ 6254.93, 12509.85],
#        [ 6350.  , 12700.  ],
#        [12000.  , 24000.  ],
#        [12298.8 , 24597.6 ],
#        [12558.3 , 25116.61],
#        [12882.3 , 25764.62],
#        [13209.51, 26419.04],
#        [13543.71, 27087.44],
#        [13876.89, 27753.79],
#        [14204.38, 28408.78],
#        [ 7685.  , 15369.  ],
#        [ 7866.37, 15731.71]])
hdoupe commented 4 years ago

Merging #102 so that I can push some fixes that depend on these changes. I'm still open to feedback on how this should work. I also need to take another look at how sort_values interacts with the _auto field before doing a release.

Thanks for your feedback so far on #102 @Peter-Metz