Commit 26c502ff authored by Thibault Hallouin's avatar Thibault Hallouin
Browse files

add new API parameters events/seed/c_lvl

1 merge request!1release v0.1.0.0
Showing with 64 additions and 24 deletions
+64 -24
...@@ -17,7 +17,8 @@ def evald(q_obs: NDArray[dtype('float64')], ...@@ -17,7 +17,8 @@ def evald(q_obs: NDArray[dtype('float64')],
t_msk: NDArray[dtype('bool')] = None, t_msk: NDArray[dtype('bool')] = None,
m_cdt: ArrayLike = None, m_cdt: ArrayLike = None,
bootstrap: Dict[str, int] = None, bootstrap: Dict[str, int] = None,
dts: ArrayLike = None) -> List[NDArray[dtype('float64')]]: dts: ArrayLike = None,
seed: int = None) -> List[NDArray[dtype('float64')]]:
"""Function to evaluate deterministic streamflow predictions""" """Function to evaluate deterministic streamflow predictions"""
# required arguments # required arguments
...@@ -43,5 +44,7 @@ def evald(q_obs: NDArray[dtype('float64')], ...@@ -43,5 +44,7 @@ def evald(q_obs: NDArray[dtype('float64')],
kwargs['bootstrap'] = bootstrap kwargs['bootstrap'] = bootstrap
if dts is not None: if dts is not None:
kwargs['dts'] = dts kwargs['dts'] = dts
if seed is not None:
kwargs['seed'] = seed
return _evald(**kwargs) return _evald(**kwargs)
...@@ -12,10 +12,13 @@ def evalp(q_obs: NDArray[dtype('float64')], ...@@ -12,10 +12,13 @@ def evalp(q_obs: NDArray[dtype('float64')],
q_prd: NDArray[dtype('float64')], q_prd: NDArray[dtype('float64')],
metrics: List[str], metrics: List[str],
q_thr: NDArray[dtype('float64')] = None, q_thr: NDArray[dtype('float64')] = None,
events: str = None,
c_lvl: NDArray[dtype('float64')] = None,
t_msk: NDArray[dtype('bool')] = None, t_msk: NDArray[dtype('bool')] = None,
m_cdt: ArrayLike = None, m_cdt: ArrayLike = None,
bootstrap: Dict[str, int] = None, bootstrap: Dict[str, int] = None,
dts: ArrayLike = None) -> List[NDArray[dtype('float64')]]: dts: ArrayLike = None,
seed: int = None) -> List[NDArray[dtype('float64')]]:
"""Function to evaluate probabilistic streamflow predictions""" """Function to evaluate probabilistic streamflow predictions"""
# required arguments # required arguments
...@@ -28,6 +31,10 @@ def evalp(q_obs: NDArray[dtype('float64')], ...@@ -28,6 +31,10 @@ def evalp(q_obs: NDArray[dtype('float64')],
# optional arguments # optional arguments
if q_thr is not None: if q_thr is not None:
kwargs['q_thr'] = q_thr kwargs['q_thr'] = q_thr
if events is not None:
kwargs['events'] = events
if c_lvl is not None:
kwargs['c_lvl'] = c_lvl
if t_msk is not None: if t_msk is not None:
kwargs['t_msk'] = t_msk kwargs['t_msk'] = t_msk
if m_cdt is not None: if m_cdt is not None:
...@@ -36,5 +43,7 @@ def evalp(q_obs: NDArray[dtype('float64')], ...@@ -36,5 +43,7 @@ def evalp(q_obs: NDArray[dtype('float64')],
kwargs['bootstrap'] = bootstrap kwargs['bootstrap'] = bootstrap
if dts is not None: if dts is not None:
kwargs['dts'] = dts kwargs['dts'] = dts
if seed is not None:
kwargs['seed'] = seed
return _evalp(**kwargs) return _evalp(**kwargs)
...@@ -24,10 +24,11 @@ auto evald( ...@@ -24,10 +24,11 @@ auto evald(
std::optional<std::string> transform, std::optional<std::string> transform,
std::optional<double> exponent, std::optional<double> exponent,
std::optional<double> epsilon, std::optional<double> epsilon,
const xt::pytensor<bool, 2>& t_msk, const xt::pytensor<bool, 3>& t_msk,
const xt::pytensor<std::array<char, 32>, 1>& m_cdt, const xt::pytensor<std::array<char, 32>, 2>& m_cdt,
std::optional<std::unordered_map<std::string, int>> bootstrap, std::optional<std::unordered_map<std::string, int>> bootstrap,
const std::vector<std::string>& dts const std::vector<std::string>& dts,
std::optional<int> seed
) )
{ {
return evalhyd::evald( return evalhyd::evald(
...@@ -42,7 +43,8 @@ auto evald( ...@@ -42,7 +43,8 @@ auto evald(
(bootstrap.has_value()) (bootstrap.has_value())
? bootstrap.value() ? bootstrap.value()
: xtl::missing<std::unordered_map<std::string, int>>(), : xtl::missing<std::unordered_map<std::string, int>>(),
dts dts,
(seed.has_value()) ? seed.value() : xtl::missing<int>()
); );
} }
...@@ -51,10 +53,13 @@ auto evalp( ...@@ -51,10 +53,13 @@ auto evalp(
const xt::pytensor<double, 4>& q_prd, const xt::pytensor<double, 4>& q_prd,
const std::vector<std::string>& metrics, const std::vector<std::string>& metrics,
const xt::pytensor<double, 2>& q_thr, const xt::pytensor<double, 2>& q_thr,
std::optional<std::string> events,
const std::vector<double>& c_lvl,
const xt::pytensor<bool, 4>& t_msk, const xt::pytensor<bool, 4>& t_msk,
const xt::pytensor<std::array<char, 32>, 2>& m_cdt, const xt::pytensor<std::array<char, 32>, 2>& m_cdt,
std::optional<std::unordered_map<std::string, int>> bootstrap, std::optional<std::unordered_map<std::string, int>> bootstrap,
const std::vector<std::string>& dts const std::vector<std::string>& dts,
std::optional<int> seed
) )
{ {
return evalhyd::evalp( return evalhyd::evalp(
...@@ -62,12 +67,15 @@ auto evalp( ...@@ -62,12 +67,15 @@ auto evalp(
q_prd, q_prd,
metrics, metrics,
q_thr, q_thr,
(events.has_value()) ? events.value() : xtl::missing<std::string>(),
c_lvl,
t_msk, t_msk,
m_cdt, m_cdt,
(bootstrap.has_value()) (bootstrap.has_value())
? bootstrap.value() ? bootstrap.value()
: xtl::missing<std::unordered_map<std::string, int>>(), : xtl::missing<std::unordered_map<std::string, int>>(),
dts dts,
(seed.has_value()) ? seed.value() : xtl::missing<int>()
); );
} }
...@@ -89,10 +97,11 @@ PYBIND11_MODULE(_evalhyd, m) ...@@ -89,10 +97,11 @@ PYBIND11_MODULE(_evalhyd, m)
py::arg("transform") = py::none(), py::arg("transform") = py::none(),
py::arg("exponent") = py::none(), py::arg("exponent") = py::none(),
py::arg("epsilon") = py::none(), py::arg("epsilon") = py::none(),
py::arg("t_msk") = xt::pytensor<bool, 2>({0}), py::arg("t_msk") = xt::pytensor<bool, 3>({0}),
py::arg("m_cdt") = xt::pytensor<std::array<char, 32>, 1>({}), py::arg("m_cdt") = xt::pytensor<std::array<char, 32>, 2>({0}),
py::arg("bootstrap") = py::none(), py::arg("bootstrap") = py::none(),
py::arg("dts") = py::list() py::arg("dts") = py::list(),
py::arg("seed") = py::none()
); );
// probabilistic evaluation // probabilistic evaluation
...@@ -104,10 +113,13 @@ PYBIND11_MODULE(_evalhyd, m) ...@@ -104,10 +113,13 @@ PYBIND11_MODULE(_evalhyd, m)
py::arg("q_prd"), py::arg("q_prd"),
py::arg("metrics"), py::arg("metrics"),
py::arg("q_thr") = xt::pytensor<double, 2>({0}), py::arg("q_thr") = xt::pytensor<double, 2>({0}),
py::arg("events") = py::none(),
py::arg("c_lvl") = py::list(),
py::arg("t_msk") = xt::pytensor<bool, 4>({0}), py::arg("t_msk") = xt::pytensor<bool, 4>({0}),
py::arg("m_cdt") = xt::pytensor<std::array<char, 32>, 2>({0}), py::arg("m_cdt") = xt::pytensor<std::array<char, 32>, 2>({0}),
py::arg("bootstrap") = py::none(), py::arg("bootstrap") = py::none(),
py::arg("dts") = py::list() py::arg("dts") = py::list(),
py::arg("seed") = py::none()
); );
#ifdef VERSION_INFO #ifdef VERSION_INFO
......
...@@ -88,7 +88,8 @@ class TestTransform(unittest.TestCase): ...@@ -88,7 +88,8 @@ class TestTransform(unittest.TestCase):
class TestMasking(unittest.TestCase): class TestMasking(unittest.TestCase):
def test_masks(self): def test_masks(self):
msk = numpy.ones(_obs.shape, dtype=bool) msk = numpy.ones(_prd.shape, dtype=bool)
msk = msk[:, numpy.newaxis, :]
msk[..., :99] = False msk[..., :99] = False
# TODO: figure out why passing views would not work # TODO: figure out why passing views would not work
...@@ -102,7 +103,12 @@ class TestMasking(unittest.TestCase): ...@@ -102,7 +103,12 @@ class TestMasking(unittest.TestCase):
def test_conditions(self): def test_conditions(self):
with self.subTest(conditions="observed streamflow values"): with self.subTest(conditions="observed streamflow values"):
cdt = numpy.array(["q_obs{<2000,>3000}"], dtype='|S32') cdt = numpy.array([["q_obs{<2000,>3000}"],
["q_obs{<2000,>3000}"],
["q_obs{<2000,>3000}"],
["q_obs{<2000,>3000}"],
["q_obs{<2000,>3000}"]],
dtype='|S32')
msk = (_obs[0] < 2000) | (_obs[0] > 3000) msk = (_obs[0] < 2000) | (_obs[0] > 3000)
...@@ -116,7 +122,12 @@ class TestMasking(unittest.TestCase): ...@@ -116,7 +122,12 @@ class TestMasking(unittest.TestCase):
) )
with self.subTest(conditions="observed streamflow statistics"): with self.subTest(conditions="observed streamflow statistics"):
cdt = numpy.array(["q_obs{>=median}"], dtype='|S32') cdt = numpy.array([["q_obs{>=median}"],
["q_obs{>=median}"],
["q_obs{>=median}"],
["q_obs{>=median}"],
["q_obs{>=median}"]],
dtype='|S32')
msk = _obs[0] >= numpy.median(_obs) msk = _obs[0] >= numpy.median(_obs)
......
...@@ -44,7 +44,7 @@ class TestMetrics(unittest.TestCase): ...@@ -44,7 +44,7 @@ class TestMetrics(unittest.TestCase):
for metric in self.expected_thr.keys(): for metric in self.expected_thr.keys():
with self.subTest(metric=metric): with self.subTest(metric=metric):
numpy.testing.assert_almost_equal( numpy.testing.assert_almost_equal(
evalhyd.evalp(_obs, _prd, [metric], thr)[0], evalhyd.evalp(_obs, _prd, [metric], thr, "high")[0],
self.expected_thr[metric] self.expected_thr[metric]
) )
...@@ -61,16 +61,16 @@ class TestDecomposition(unittest.TestCase): ...@@ -61,16 +61,16 @@ class TestDecomposition(unittest.TestCase):
def test_brier_calibration_refinement(self): def test_brier_calibration_refinement(self):
thr = numpy.array([[690, 534, 445]]) thr = numpy.array([[690, 534, 445]])
bs, = evalhyd.evalp(_obs, _prd, ["BS"], thr) bs, = evalhyd.evalp(_obs, _prd, ["BS"], thr, "high")
bs_crd, = evalhyd.evalp(_obs, _prd, ["BS_CRD"], thr) bs_crd, = evalhyd.evalp(_obs, _prd, ["BS_CRD"], thr, "high")
numpy.testing.assert_almost_equal( numpy.testing.assert_almost_equal(
bs, bs_crd[..., 0] - bs_crd[..., 1] + bs_crd[..., 2] bs, bs_crd[..., 0] - bs_crd[..., 1] + bs_crd[..., 2]
) )
def test_brier_likelihood_base_rate(self): def test_brier_likelihood_base_rate(self):
thr = numpy.array([[690, 534, 445]]) thr = numpy.array([[690, 534, 445]])
bs, = evalhyd.evalp(_obs, _prd, ["BS"], thr) bs, = evalhyd.evalp(_obs, _prd, ["BS"], thr, "high")
bs_lbd, = evalhyd.evalp(_obs, _prd, ["BS_LBD"], thr) bs_lbd, = evalhyd.evalp(_obs, _prd, ["BS_LBD"], thr, "high")
numpy.testing.assert_almost_equal( numpy.testing.assert_almost_equal(
bs, bs_lbd[..., 0] - bs_lbd[..., 1] + bs_lbd[..., 2] bs, bs_lbd[..., 0] - bs_lbd[..., 1] + bs_lbd[..., 2]
) )
...@@ -79,7 +79,8 @@ class TestDecomposition(unittest.TestCase): ...@@ -79,7 +79,8 @@ class TestDecomposition(unittest.TestCase):
class TestMasking(unittest.TestCase): class TestMasking(unittest.TestCase):
def test_masks(self): def test_masks(self):
msk = numpy.ones((_prd.shape[0], _prd.shape[1], 1, _prd.shape[3]), dtype=bool) msk = numpy.ones((_prd.shape[0], _prd.shape[1], 1, _prd.shape[3]),
dtype=bool)
msk[..., :99] = False msk[..., :99] = False
# TODO: figure out why passing views would not work # TODO: figure out why passing views would not work
...@@ -136,7 +137,8 @@ class TestMissingData(unittest.TestCase): ...@@ -136,7 +137,8 @@ class TestMissingData(unittest.TestCase):
[4.3, 4.2, 4.7, 4.3, numpy.nan], [4.3, 4.2, 4.7, 4.3, numpy.nan],
[5.3, 5.2, 5.7, 2.3, numpy.nan]]]], [5.3, 5.2, 5.7, 2.3, numpy.nan]]]],
[metric], [metric],
thr thr,
"high"
)[0], )[0],
# missing data pairwise deleted from series # missing data pairwise deleted from series
evalhyd.evalp( evalhyd.evalp(
...@@ -145,7 +147,8 @@ class TestMissingData(unittest.TestCase): ...@@ -145,7 +147,8 @@ class TestMissingData(unittest.TestCase):
[4.3, 4.7, 4.3], [4.3, 4.7, 4.3],
[5.3, 5.7, 2.3]]]], [5.3, 5.7, 2.3]]]],
[metric], [metric],
thr thr,
"high"
)[0] )[0]
) )
...@@ -178,6 +181,7 @@ class TestUncertainty(unittest.TestCase): ...@@ -178,6 +181,7 @@ class TestUncertainty(unittest.TestCase):
prd_1yr[numpy.newaxis, numpy.newaxis], prd_1yr[numpy.newaxis, numpy.newaxis],
[metric], [metric],
q_thr=thr, q_thr=thr,
events="high",
bootstrap={ bootstrap={
"n_samples": 10, "len_sample": 3, "summary": 0 "n_samples": 10, "len_sample": 3, "summary": 0
}, },
...@@ -189,7 +193,8 @@ class TestUncertainty(unittest.TestCase): ...@@ -189,7 +193,8 @@ class TestUncertainty(unittest.TestCase):
obs_3yrs[numpy.newaxis], obs_3yrs[numpy.newaxis],
prd_3yrs[numpy.newaxis, numpy.newaxis], prd_3yrs[numpy.newaxis, numpy.newaxis],
[metric], [metric],
q_thr=thr q_thr=thr,
events="high"
)[0] )[0]
) )
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment