Skip to content

Commit 663cc24

Browse files
authored
Add decorator to importers for postprocessing the precipitation data. (#148)
* Allow explicitly define the precipitation data type (dtype keyword) * Set invalid or missing data to a predefined value (fillna keyword) * Add "Other parameters" docstrings with the documentation of the keywords used by the decorator. * Make importer keywords arguments explicit (with the exception of the keywords handled by the decorator).
1 parent b778828 commit 663cc24

File tree

6 files changed

+291
-105
lines changed

6 files changed

+291
-105
lines changed

pysteps/decorators.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,99 @@
88
.. autosummary::
99
:toctree: ../generated/
1010
11+
postprocess_import
1112
check_motion_input_image
1213
"""
14+
import inspect
15+
from collections import defaultdict
1316
from functools import wraps
1417

1518
import numpy as np
1619

1720

21+
def postprocess_import(fillna=np.nan, dtype='double'):
22+
"""
23+
Postprocess the imported precipitation data.
24+
Operations:
25+
- Allow type casting (dtype keyword)
26+
- Set invalid or missing data to predefined value (fillna keyword)
27+
28+
This decorator replaces the text "{extra_kwargs}" in the function's
29+
docstring with the documentation of the keywords used in the postprocessing.
30+
The additional docstrings are added as "Other Parameters" in the importer function.
31+
32+
Parameters
33+
----------
34+
dtype : str
35+
Default data type for precipitation. Double precision by default.
36+
fillna : float or np.nan
37+
Default value used to represent the missing data ("No Coverage").
38+
By default, np.nan is used.
39+
"""
40+
41+
def _postprocess_import(importer):
42+
@wraps(importer)
43+
def _import_with_postprocessing(*args, **kwargs):
44+
45+
precip, *other_args = importer(*args, **kwargs)
46+
47+
_dtype = kwargs.get("dtype", dtype)
48+
49+
accepted_precisions = ["float32", "float64", "single", "double"]
50+
if _dtype not in accepted_precisions:
51+
raise ValueError(
52+
"The selected precision does not correspond to a valid value."
53+
"The accepted values are: " + str(accepted_precisions)
54+
)
55+
56+
if isinstance(precip, np.ma.MaskedArray):
57+
invalid_mask = np.ma.getmaskarray(precip)
58+
precip.data[invalid_mask] = fillna
59+
else:
60+
# If plain numpy arrays are used, the importers should indicate
61+
# the invalid values with np.nan.
62+
_fillna = kwargs.get("fillna", fillna)
63+
if _fillna is not np.nan:
64+
mask = ~np.isfinite(precip)
65+
precip[mask] = _fillna
66+
67+
return (precip.astype(_dtype),) + tuple(other_args)
68+
69+
extra_kwargs_doc = """
70+
Other Parameters
71+
----------------
72+
dtype : str
73+
Data-type to which the array is cast.
74+
Valid values: "float32", "float64", "single", and "double".
75+
fillna : float or np.nan
76+
Value used to represent the missing data ("No Coverage").
77+
By default, np.nan is used.
78+
"""
79+
80+
# Clean up indentation from docstrings for the
81+
# docstrings to be merged correctly.
82+
extra_kwargs_doc = inspect.cleandoc(extra_kwargs_doc)
83+
_import_with_postprocessing.__doc__ = inspect.cleandoc(_import_with_postprocessing.__doc__)
84+
85+
# Add extra kwargs docstrings
86+
_import_with_postprocessing.__doc__ = _import_with_postprocessing.__doc__.format_map(
87+
defaultdict(str, extra_kwargs_doc=extra_kwargs_doc))
88+
89+
return _import_with_postprocessing
90+
91+
return _postprocess_import
92+
93+
1894
def check_input_frames(minimum_input_frames=2,
1995
maximum_input_frames=np.inf,
2096
just_ndim=False):
2197
"""
2298
Check that the input_images used as inputs in the optical-flow
23-
methods has the correct shape (t, x, y ).
99+
methods have the correct shape (t, x, y ).
24100
"""
25101

26102
def _check_input_frames(motion_method_func):
103+
27104
@wraps(motion_method_func)
28105
def new_function(*args, **kwargs):
29106
"""

0 commit comments

Comments
 (0)