Skip to main content

Library for parameter processing and validation with a focus on computational modeling projects

Project description

ParamTools

Define, update, and validate your model's parameters.

Install using pip:

pip install paramtools

Install using conda:

conda install -c conda-forge paramtools

Usage

Subclass paramtools.Parameters and define your model's parameters:

import paramtools


class Params(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 values

Access 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:

from datetime import date

result = params.sel["a"]["date"] == date(2020, 1, 1)
result
QueryResult([
  {'date': datetime.date(2020, 1, 1), 'value': 2}
])
result.isel[0]["value"]
2

Adjust and validate parameter values

Add 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 definitions

Extend values using label_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},
])

Use clobber to 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 output
params.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 integration

Access values as NumPy arrays with array_first:

params = Params(label_to_extend="date", array_first=True)

params.a
array([2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 8, 8])
params.a * params.b
array([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"])
arr
array([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}]

Documentation

Full documentation available at paramtools.dev.

Contributing

Contributions are welcome! Checkout CONTRIBUTING.md to get started.

Credits

ParamTools is built on top of the excellent marshmallow JSON schema and validation framework. I encourage everyone to check out their repo and documentation. ParamTools was modeled off of Tax-Calculator's parameter processing and validation engine due to its maturity and sophisticated capabilities.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

paramtools-0.18.1.tar.gz (51.5 kB view hashes)

Uploaded Source

Built Distribution

paramtools-0.18.1-py3-none-any.whl (57.1 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page