Commit f178ebb8 authored by Thibault Hallouin's avatar Thibault Hallouin
Browse files

enforce ndarray as input data type to be able to check array ranks

otherwise it is not very easy from the error message to deduce the
problem is due to a mismatch in number of dimensions
1 merge request!1release v0.1.0.0
Pipeline #43916 passed with stage
in 3 minutes and 51 seconds
Showing with 61 additions and 20 deletions
+61 -20
from typing import List, Dict from typing import List, Dict
from numpy import dtype from numpy import dtype
from numpy.typing import NDArray, ArrayLike from numpy.typing import NDArray
try: try:
from ._evalhyd import _evald from ._evalhyd import _evald
...@@ -15,9 +15,9 @@ def evald(q_obs: NDArray[dtype('float64')], ...@@ -15,9 +15,9 @@ def evald(q_obs: NDArray[dtype('float64')],
exponent: float = None, exponent: float = None,
epsilon: float = None, epsilon: float = None,
t_msk: NDArray[dtype('bool')] = None, t_msk: NDArray[dtype('bool')] = None,
m_cdt: ArrayLike = None, m_cdt: NDArray[dtype('|S32')] = None,
bootstrap: Dict[str, int] = None, bootstrap: Dict[str, int] = None,
dts: ArrayLike = None, dts: NDArray[dtype('|S32')] = None,
seed: int = None) -> List[NDArray[dtype('float64')]]: seed: int = None) -> List[NDArray[dtype('float64')]]:
"""Function to evaluate deterministic streamflow predictions""" """Function to evaluate deterministic streamflow predictions"""
...@@ -47,4 +47,22 @@ def evald(q_obs: NDArray[dtype('float64')], ...@@ -47,4 +47,22 @@ def evald(q_obs: NDArray[dtype('float64')],
if seed is not None: if seed is not None:
kwargs['seed'] = seed kwargs['seed'] = seed
# check array ranks
_expected = {
'q_obs': 2,
'q_prd': 2,
't_msk': 3,
'm_cdt': 2,
'dts': 1
}
for arg, val in _expected.items():
try:
if kwargs[arg].ndim != val:
raise RuntimeError(
f"'{arg}' must feature {val} {'axis' if val == 1 else 'axes'}"
)
except KeyError:
pass
return _evald(**kwargs) return _evald(**kwargs)
from typing import List, Dict from typing import List, Dict
from numpy import dtype from numpy import dtype
from numpy.typing import NDArray, ArrayLike from numpy.typing import NDArray
try: try:
from ._evalhyd import _evalp from ._evalhyd import _evalp
...@@ -15,9 +15,9 @@ def evalp(q_obs: NDArray[dtype('float64')], ...@@ -15,9 +15,9 @@ def evalp(q_obs: NDArray[dtype('float64')],
events: str = None, events: str = None,
c_lvl: NDArray[dtype('float64')] = 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: NDArray[dtype('|S32')] = None,
bootstrap: Dict[str, int] = None, bootstrap: Dict[str, int] = None,
dts: ArrayLike = None, dts: NDArray[dtype('|S32')] = None,
seed: int = None) -> List[NDArray[dtype('float64')]]: seed: int = None) -> List[NDArray[dtype('float64')]]:
"""Function to evaluate probabilistic streamflow predictions""" """Function to evaluate probabilistic streamflow predictions"""
...@@ -46,4 +46,24 @@ def evalp(q_obs: NDArray[dtype('float64')], ...@@ -46,4 +46,24 @@ def evalp(q_obs: NDArray[dtype('float64')],
if seed is not None: if seed is not None:
kwargs['seed'] = seed kwargs['seed'] = seed
# check array ranks
_expected = {
'q_obs': 2,
'q_prd': 4,
'q_thr': 2,
'c_lvl': 1,
't_msk': 4,
'm_cdt': 2,
'dts': 1
}
for arg, val in _expected.items():
try:
if kwargs[arg].ndim != val:
raise RuntimeError(
f"'{arg}' must feature {val} {'axis' if val == 1 else 'axes'}"
)
except KeyError:
pass
return _evalp(**kwargs) return _evalp(**kwargs)
...@@ -131,10 +131,11 @@ class TestMetrics(unittest.TestCase): ...@@ -131,10 +131,11 @@ class TestMetrics(unittest.TestCase):
) )
def test_intervals_metrics(self): def test_intervals_metrics(self):
lvl = numpy.array([30., 80.])
for metric in self.expected_itv.keys(): for metric in self.expected_itv.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], c_lvl=[30., 80.])[0], evalhyd.evalp(_obs, _prd, [metric], c_lvl=lvl)[0],
self.expected_itv[metric] self.expected_itv[metric]
) )
...@@ -176,7 +177,7 @@ class TestMasking(unittest.TestCase): ...@@ -176,7 +177,7 @@ 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}"]])
msk = (_obs[0] < 2000) | (_obs[0] > 3000) msk = (_obs[0] < 2000) | (_obs[0] > 3000)
...@@ -215,28 +216,29 @@ class TestMissingData(unittest.TestCase): ...@@ -215,28 +216,29 @@ class TestMissingData(unittest.TestCase):
continue continue
with self.subTest(metric=metric): with self.subTest(metric=metric):
lvl = numpy.array([30., 80.])
numpy.testing.assert_almost_equal( numpy.testing.assert_almost_equal(
# missing data flagged as NaN # missing data flagged as NaN
evalhyd.evalp( evalhyd.evalp(
[[4.7, numpy.nan, 5.5, 2.7, 4.1]], numpy.array([[4.7, numpy.nan, 5.5, 2.7, 4.1]]),
[[[[5.3, 4.2, 5.7, 2.3, numpy.nan], numpy.array([[[[5.3, 4.2, 5.7, 2.3, numpy.nan],
[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", "high",
[30., 80.] lvl
)[0], )[0],
# missing data pairwise deleted from series # missing data pairwise deleted from series
evalhyd.evalp( evalhyd.evalp(
[[4.7, 5.5, 2.7]], numpy.array([[4.7, 5.5, 2.7]]),
[[[[5.3, 5.7, 2.3], numpy.array([[[[5.3, 5.7, 2.3],
[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", "high",
[30., 80.] lvl
)[0] )[0]
) )
...@@ -265,6 +267,7 @@ class TestUncertainty(unittest.TestCase): ...@@ -265,6 +267,7 @@ class TestUncertainty(unittest.TestCase):
continue continue
with self.subTest(metric=metric): with self.subTest(metric=metric):
lvl = numpy.array([30., 80.])
numpy.testing.assert_almost_equal( numpy.testing.assert_almost_equal(
# bootstrap with only one year of data # bootstrap with only one year of data
# (compare last sample only to have matching dimensions) # (compare last sample only to have matching dimensions)
...@@ -278,7 +281,7 @@ class TestUncertainty(unittest.TestCase): ...@@ -278,7 +281,7 @@ class TestUncertainty(unittest.TestCase):
"n_samples": 10, "len_sample": 3, "summary": 0 "n_samples": 10, "len_sample": 3, "summary": 0
}, },
dts=dts_1yr, dts=dts_1yr,
c_lvl=[30., 80.] c_lvl=lvl
)[0][:, :, :, [0]], )[0][:, :, :, [0]],
# repeat year of data three times to correspond to a # repeat year of data three times to correspond to a
# bootstrap sample of length 3 # bootstrap sample of length 3
...@@ -288,7 +291,7 @@ class TestUncertainty(unittest.TestCase): ...@@ -288,7 +291,7 @@ class TestUncertainty(unittest.TestCase):
[metric], [metric],
q_thr=thr, q_thr=thr,
events="high", events="high",
c_lvl=[30., 80.] c_lvl=lvl
)[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