evalhyd-python.cpp 13.97 KiB
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <array>
#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
#define FORCE_IMPORT_ARRAY
#include <xtensor-python/pytensor.hpp>
#include "evalhyd/evald.hpp"
#include "evalhyd/evalp.hpp"
namespace py = pybind11;
// Python Module and Docstrings
PYBIND11_MODULE(evalhyd, m)
    xt::import_numpy();
    m.doc() = R"pbdoc(
        Utility for evaluation of streamflow predictions.
    )pbdoc";
    // deterministic evaluation
    m.def(
        "evald", evalhyd::evald<1>,
        R"pbdoc(
            Function to evaluate deterministic streamflow predictions.
            :Parameters:
                q_obs: `numpy.ndarray`
                    1D array of streamflow observations. Time steps with
                    missing observations must be assigned `numpy.nan`
                    values. Those time steps will be ignored both in
                    the observations and in the predictions before the
                    *metrics* are computed.
                    shape: (time,)
                q_prd: `numpy.ndarray`
                    1D array of streamflow predictions. Time steps with
                    missing predictions must be assigned `numpy.nan`
                    values. Those time steps will be ignored both in
                    the observations and in the predictions before the
                    *metrics* are computed.
                    shape: (time,)
                metrics: `List[str]`
                    The sequence of evaluation metrics to be computed.
                transform: `str`, optional
                   The transformation to apply to both streamflow observations
                   and predictions prior to the calculation of the *metrics*.
                   The options are listed in the table below.
                   ========================  ==================================
                   transformations           details
                   ========================  ==================================
                   ``"sqrt"``                The square root function
                                             **f(Q) = √Q** is applied.
                   ``"pow"``                 The power function
                                             **f(Q) = Qⁿ** is applied (where
                                             the power **n** can be set through
                                             the *exponent* parameter).
                   ``"inv"``                 The reciprocal function
                                             **f(Q) = 1/Q** is applied.
                   ``"log"``                 The natural logarithm function
                                             **f(Q) = ln(Q)** is applied.
                   ========================  ==================================
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
exponent: `float`, optional The value of the exponent n to use when the *transform* is the power function. If not provided (or set to default value 1), the streamflow observations and predictions remain untransformed. epsilon: `float`, optional The value of the small constant ε to add to both the streamflow observations and predictions prior to the calculation of the *metrics* when the *transform* is the reciprocal function, the natural logarithm, or the power function with a negative exponent (since none are defined for 0). If not provided (or set to default value -9), one hundredth of the mean of the streamflow observations is used as value for epsilon, as recommended by `Pushpalatha et al. (2012) <https://doi.org/10.1016/j.jhydrol.2011.11.055>`_. t_msk: `numpy.ndarray`, optional 1D array of mask(s) used to generate temporal subsets of the whole streamflow time series (where True/False is used for the time steps to include/discard in a given subset). If not provided and neither is *m_cdt*, no subset is performed. If provided, masks must feature the same number of dimensions as observations and predictions, and it must broadcastable with both of them. shape: (time,) m_cdt: `numpy.ndarray`, optional 1D array of masking condition(s) to use to generate temporal subsets. Each condition consists in a string and can be specified on observed streamflow values or on time indices. If provided in combination with *t_msk*, the latter takes precedence. If not provided and neither is *t_msk*, no subset is performed. If provided, only one condition per time series of observations can be provided. shape: (1,) :Returns: `List[numpy.ndarray]` The sequence of evaluation metrics computed in the same order as given in *metrics*. shape: [(components,)+] )pbdoc", py::arg("q_obs"), py::arg("q_prd"), py::arg("metrics"), py::arg("transform") = "none", py::arg("exponent") = 1, py::arg("epsilon") = -9, py::arg("t_msk") = xt::pytensor<bool, 1>({}), py::arg("m_cdt") = xt::pytensor<std::array<char, 32>, 1>({}) ); m.def( "evald", evalhyd::evald<2>, R"pbdoc( Function to evaluate deterministic streamflow predictions. :Parameters: q_obs: `numpy.ndarray` 2D array of streamflow observations. Time steps with missing observations must be assigned `numpy.nan` values. Those time steps will be ignored both in the observations and in the predictions before the *metrics* are computed. shape: (1+, time) q_prd: `numpy.ndarray` 2D array of streamflow predictions. Time steps with missing predictions must be assigned `numpy.nan`
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
values. Those time steps will be ignored both in the observations and in the predictions before the *metrics* are computed. shape: (1+, time) metrics: `List[str]` The sequence of evaluation metrics to be computed. transform: `str`, optional The transformation to apply to both streamflow observations and predictions prior to the calculation of the *metrics*. The options are listed in the table below. ======================== ================================== transformations details ======================== ================================== ``"sqrt"`` The square root function **f(Q) = √Q** is applied. ``"pow"`` The power function **f(Q) = Qⁿ** is applied (where the power **n** can be set through the *exponent* parameter). ``"inv"`` The reciprocal function **f(Q) = 1/Q** is applied. ``"log"`` The natural logarithm function **f(Q) = ln(Q)** is applied. ======================== ================================== exponent: `float`, optional The value of the exponent n to use when the *transform* is the power function. If not provided (or set to default value 1), the streamflow observations and predictions remain untransformed. epsilon: `float`, optional The value of the small constant ε to add to both the streamflow observations and predictions prior to the calculation of the *metrics* when the *transform* is the reciprocal function, the natural logarithm, or the power function with a negative exponent (since none are defined for 0). If not provided (or set to default value -9), one hundredth of the mean of the streamflow observations is used as value for epsilon, as recommended by `Pushpalatha et al. (2012) <https://doi.org/10.1016/j.jhydrol.2011.11.055>`_. t_msk: `numpy.ndarray`, optional 2D array of mask(s) used to generate temporal subsets of the whole streamflow time series (where True/False is used for the time steps to include/discard in a given subset). If not provided and neither is *m_cdt*, no subset is performed. If provided, masks must feature the same number of dimensions as observations and predictions, and it must broadcastable with both of them. shape: (1+, time) m_cdt: `numpy.ndarray`, optional 2D array of masking condition(s) to use to generate temporal subsets. Each condition consists in a string and can be specified on observed streamflow values or on time indices. If provided in combination with *t_msk*, the latter takes precedence. If not provided and neither is *t_msk*, no subset is performed. If provided, only one condition per time series of observations can be provided. shape: (1+, 1) :Returns: `List[numpy.ndarray]` The sequence of evaluation metrics computed
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
in the same order as given in *metrics*. shape: [(1+, components), ...] )pbdoc", py::arg("q_obs"), py::arg("q_prd"), py::arg("metrics"), py::arg("transform") = "none", py::arg("exponent") = 1, py::arg("epsilon") = -9, py::arg("t_msk") = xt::pytensor<bool, 2>({0}), py::arg("m_cdt") = xt::pytensor<std::array<char, 32>, 2>({0}) ); // probabilistic evaluation m.def( "evalp", evalhyd::evalp, R"pbdoc( Function to evaluate probabilistic streamflow predictions. :Parameters: q_obs: `numpy.ndarray` 2D array of streamflow observations. Time steps with missing observations must be assigned `numpy.nan` values. Those time steps will be ignored both in the observations and in the predictions before the *metrics* are computed. shape: (sites, time) q_prd: `numpy.ndarray` 4D array of streamflow predictions. Time steps with missing predictions must be assigned `numpy.nan` values. Those time steps will be ignored both in the observations and in the predictions before the *metrics* are computed. shape: (sites, lead times, members, time) metrics: `List[str]` The sequence of evaluation metrics to be computed. q_thr: `List[float]`, optional The streamflow threshold(s) to consider for the *metrics* assessing the prediction of exceedance events. If not provided, set to default value as an empty `list`. shape: (thresholds,) t_msk: `numpy.ndarray`, optional 4D array of masks to generate temporal subsets of the whole streamflow time series (where True/False is used for the time steps to include/discard in a given subset). If not provided, no subset is performed and only one set of metrics is returned corresponding to the whole time series. If provided, as many sets of metrics are returned as they are masks provided. shape: (sites, lead times, subsets, time) m_cdt: `numpy.ndarray`, optional 2D array of conditions to generate temporal subsets. Each condition consists in a string and can be specified on observed streamflow values or on time indices. If provided in combination with t_msk, the latter takes precedence. If not provided and neither is t_msk, no subset is performed and only one set of metrics is returned corresponding to the whole time series. If provided, as many sets of metrics are returned as they are conditions provided. shape: (sites, subsets) :Returns: `List[numpy.ndarray]` The sequence of evaluation metrics computed in the same order as given in *metrics*. shape: [(sites, lead times, subsets, {quantiles,} {thresholds,} {components}), ...]
281282283284285286287288289290291292293294
)pbdoc", py::arg("q_obs"), py::arg("q_prd"), py::arg("metrics"), py::arg("q_thr") = xt::pytensor<double, 2>({0}), py::arg("t_msk") = xt::pytensor<bool, 4>({0}), py::arg("m_cdt") = xt::pytensor<std::array<char, 32>, 2>({0}) ); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif }