Custom Adjustments#
The ParamTools adjustment format and logic can be augmented significantly. This is helpful for projects that need to support a pre-existing data format or require custom adjustment logic. Projects should customize their adjustments by writing their own adjust
method and then calling the default adjust
method from there:
import paramtools
class Params(paramtools.Parameters):
def adjust(self, params_or_path, **kwargs):
params = self.read_params(params_or_path)
# ... custom logic here
# call default adjust method.
return super().adjust(params, **kwargs)
Example#
Some projects may find it convenient to use CSVs for their adjustment format. That’s no problem for ParamTools as long as the CSV is converted to a JSON file or Python dictionary that meets the ParamTools criteria.
import io
import os
import pandas as pd
import paramtools
class CSVParams(paramtools.Parameters):
defaults = {
"schema": {
"labels": {
"year": {
"type": "int",
"validators": {"range": {"min": 2000, "max": 2005}}
}
}
},
"a": {
"title": "A",
"description": "a param",
"type": "int",
"value": [
{"year": 2000, "value": 1},
{"year": 2001, "value": 2},
]
},
"b": {
"title": "B",
"description": "b param",
"type": "int",
"value": [
{"year": 2000, "value": 3},
{"year": 2001, "value": 4},
]
}
}
def adjust(self, params_or_path, **kwargs):
"""
A custom adjust method that converts CSV files to
ParamTools compliant Python dictionaries.
"""
if os.path.exists(params_or_path):
paramsdf = pd.read_csv(params_or_path, index_col="year")
else:
paramsdf = pd.read_csv(io.StringIO(params_or_path), index_col="year")
dfdict = paramsdf.to_dict()
params = {"a": [], "b": []}
for label in params:
for year, value in dfdict[label].items():
params[label] += [{"year": year, "value": value}]
# call adjust method on paramtools.Parameters which will
# call _adjust to actually do the update.
return super().adjust(params, **kwargs)
Now we create an example CSV file. To keep the example self-contained, the CSV is just a string, but this example works with CSV files, too. The values of “A” are updated to 5 in 2000 and 6 in 2001, and the values of “B” are updated to 6 in 2000 and 7 in 2001.
# this could also be a path to a CSV file.
csv_string = """
year,a,b
2000,5,6\n
2001,6,7\n
"""
params = CSVParams()
params.adjust(csv_string)
OrderedDict([('a',
[OrderedDict([('value', 5), ('year', 2000)]),
OrderedDict([('value', 6), ('year', 2001)])]),
('b',
[OrderedDict([('value', 6), ('year', 2000)]),
OrderedDict([('value', 7), ('year', 2001)])])])
params.a
[OrderedDict([('value', 5), ('year', 2000)]),
OrderedDict([('value', 6), ('year', 2001)])]
params.b
[OrderedDict([('value', 6), ('year', 2000)]),
OrderedDict([('value', 7), ('year', 2001)])]
Now, if we use array_first
and label_to_extend
, the params instance can be loaded into a Pandas
DataFrame like this:
csv_string = """
year,a,b
2000,5,6\n
2001,6,7\n
"""
params = CSVParams(array_first=True, label_to_extend="year")
params.adjust(csv_string)
params_df = pd.DataFrame.from_dict(params.to_dict())
params_df
a | b | |
---|---|---|
0 | 5 | 6 |
1 | 6 | 7 |
2 | 6 | 7 |
3 | 6 | 7 |
4 | 6 | 7 |
5 | 6 | 7 |
params_df["year"] = params.label_grid["year"]
params_df.set_index("year")
a | b | |
---|---|---|
year | ||
2000 | 5 | 6 |
2001 | 6 | 7 |
2002 | 6 | 7 |
2003 | 6 | 7 |
2004 | 6 | 7 |
2005 | 6 | 7 |