Package: paramtools
ParamToolsDefine, update, and validate your model's parameters.Install using pip:pip install paramtoolsInstall using conda:conda install -c conda-forge paramtoolsUsageSubclassparamtools.Parametersand define your model'sparameters:importparamtoolsclassParams(paramtools.Parameters):defaults={"schema":{"labels":{"date":{"type":"date","validators":{"range":{"min":"2020-01-01","max":"2021-01-01","step":{"months":1}}}}},},"a":{"title":"A","type":"int","value":[{"date":"2020-01-01","value":2},{"date":"2020-10-01","value":8},],"validators":{"range":{"min":0,"max":"b"}}},"b":{"title":"B","type":"float","value":[{"date":"2020-01-01","value":10.5}]}}Access parameter valuesAccess values using.sel:params=Params()params.sel["a"]Values([
  {'date': datetime.date(2020, 1, 1), 'value': 2},
  {'date': datetime.date(2020, 10, 1), 'value': 8},
])Look up parameter values using a pandas-like api:fromdatetimeimportdateresult=params.sel["a"]["date"]==date(2020,1,1)resultQueryResult([
  {'date': datetime.date(2020, 1, 1), 'value': 2}
])result.isel[0]["value"]2Adjust and validate parameter valuesAdd a new value:params.adjust({"a":[{"date":"2020-11-01","value":22}]})params.sel["a"]Values([
  {'date': datetime.date(2020, 1, 1), 'value': 2},
  {'date': datetime.date(2020, 10, 1), 'value': 8},
  {'date': datetime.date(2020, 11, 1), 'value': 22},
])Update an existing value:params.adjust({"a":[{"date":"2020-01-01","value":3}]})params.sel["a"]Values([
  {'date': datetime.date(2020, 1, 1), 'value': 3},
  {'date': datetime.date(2020, 10, 1), 'value': 8},
  {'date': datetime.date(2020, 11, 1), 'value': 22},
])Update all values:params.adjust({"a":7})params.sel["a"]Values([
  {'date': datetime.date(2020, 1, 1), 'value': 7},
  {'date': datetime.date(2020, 10, 1), 'value': 7},
  {'date': datetime.date(2020, 11, 1), 'value': 7},
])Errors on values that are out of range:params.adjust({"a":-1})---------------------------------------------------------------------------

ValidationError                           Traceback (most recent call last)

<ipython-input-8-f8f1b7f6cd9a> in <module>
----> 1 params.adjust({"a": -1})


~/Paramtools/paramtools/parameters.py in adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, clobber)
    253             least one existing value item's corresponding label values.
    254         """
--> 255         return self._adjust(
    256             params_or_path,
    257             ignore_warnings=ignore_warnings,


~/Paramtools/paramtools/parameters.py in _adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, is_deserialized, clobber)
    371             not ignore_warnings and has_warnings
    372         ):
--> 373             raise self.validation_error
    374
    375         # Update attrs for params that were adjusted.


ValidationError: {
    "errors": {
        "a": [
            "a -1 < min 0 "
        ]
    }
}params=Params()params.adjust({"a":[{"date":"2020-01-01","value":11}]})---------------------------------------------------------------------------

ValidationError                           Traceback (most recent call last)

<ipython-input-9-cc8a21f044d8> in <module>
      1 params = Params()
      2
----> 3 params.adjust({"a": [{"date": "2020-01-01", "value": 11}]})


~/Paramtools/paramtools/parameters.py in adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, clobber)
    253             least one existing value item's corresponding label values.
    254         """
--> 255         return self._adjust(
    256             params_or_path,
    257             ignore_warnings=ignore_warnings,


~/Paramtools/paramtools/parameters.py in _adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, is_deserialized, clobber)
    371             not ignore_warnings and has_warnings
    372         ):
--> 373             raise self.validation_error
    374
    375         # Update attrs for params that were adjusted.


ValidationError: {
    "errors": {
        "a": [
            "a[date=2020-01-01] 11 > max 10.5 b[date=2020-01-01]"
        ]
    }
}Errors on invalid values:params=Params()params.adjust({"b":"abc"})---------------------------------------------------------------------------

ValidationError                           Traceback (most recent call last)

<ipython-input-10-8373a2715e38> in <module>
      1 params = Params()
      2
----> 3 params.adjust({"b": "abc"})


~/Paramtools/paramtools/parameters.py in adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, clobber)
    253             least one existing value item's corresponding label values.
    254         """
--> 255         return self._adjust(
    256             params_or_path,
    257             ignore_warnings=ignore_warnings,


~/Paramtools/paramtools/parameters.py in _adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, is_deserialized, clobber)
    371             not ignore_warnings and has_warnings
    372         ):
--> 373             raise self.validation_error
    374
    375         # Update attrs for params that were adjusted.


ValidationError: {
    "errors": {
        "b": [
            "Not a valid number: abc."
        ]
    }
}Extend parameter values using label definitionsExtend values usinglabel_to_extend:params=Params(label_to_extend="date")params.sel["a"]Values([
  {'date': datetime.date(2020, 1, 1), 'value': 2},
  {'date': datetime.date(2020, 2, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 3, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 4, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 5, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 6, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 7, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 8, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 9, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 10, 1), 'value': 8},
  {'date': datetime.date(2020, 11, 1), 'value': 8, '_auto': True},
  {'date': datetime.date(2020, 12, 1), 'value': 8, '_auto': True},
  {'date': datetime.date(2021, 1, 1), 'value': 8, '_auto': True},
])Updates to values are carried through to future dates:params.adjust({"a":[{"date":"2020-4-01","value":9}]})params.sel["a"]Values([
  {'date': datetime.date(2020, 1, 1), 'value': 2},
  {'date': datetime.date(2020, 2, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 3, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 4, 1), 'value': 9},
  {'date': datetime.date(2020, 5, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 6, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 7, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 8, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 9, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 10, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 11, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 12, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2021, 1, 1), 'value': 9, '_auto': True},
])Useclobberto only update values that were set automatically:params=Params(label_to_extend="date")params.adjust({"a":[{"date":"2020-4-01","value":9}]},clobber=False,)# Sort parameter values by date for nicer outputparams.sort_values()params.sel["a"]Values([
  {'date': datetime.date(2020, 1, 1), 'value': 2},
  {'date': datetime.date(2020, 2, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 3, 1), 'value': 2, '_auto': True},
  {'date': datetime.date(2020, 4, 1), 'value': 9},
  {'date': datetime.date(2020, 5, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 6, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 7, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 8, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 9, 1), 'value': 9, '_auto': True},
  {'date': datetime.date(2020, 10, 1), 'value': 8},
  {'date': datetime.date(2020, 11, 1), 'value': 8, '_auto': True},
  {'date': datetime.date(2020, 12, 1), 'value': 8, '_auto': True},
  {'date': datetime.date(2021, 1, 1), 'value': 8, '_auto': True},
])NumPy integrationAccess values as NumPy arrays witharray_first:params=Params(label_to_extend="date",array_first=True)params.aarray([2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 8, 8])params.a*params.barray([21., 21., 21., 21., 21., 21., 21., 21., 21., 84., 84., 84., 84.])Only get the values that you want:arr=params.to_array("a",date=["2020-01-01","2020-11-01"])arrarray([2, 8])Go back to a list of dictionaries:params.from_array("a",arr,date=["2020-01-01","2020-11-01"])[{'date': datetime.date(2020, 1, 1), 'value': 2},
 {'date': datetime.date(2020, 11, 1), 'value': 8}]DocumentationFull documentation available atparamtools.dev.ContributingContributions are welcome! CheckoutCONTRIBUTING.mdto get started.CreditsParamTools is built on top of the excellentmarshmallowJSON schema and validation framework. I encourage everyone to check out their repo and documentation. ParamTools was modeled off ofTax-Calculator'sparameter processing and validation engine due to its maturity and sophisticated capabilities.