Skip to content

Commit

Permalink
Add new skills in stats (#666)
Browse files Browse the repository at this point in the history
  • Loading branch information
hboisgon authored Dec 1, 2023
1 parent 3285054 commit 96e23a0
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ Statistics and performance metrics

stats.skills.bias
stats.skills.percentual_bias
stats.skills.volumetric_error
stats.skills.nashsutcliffe
stats.skills.lognashsutcliffe
stats.skills.pearson_correlation
Expand All @@ -840,6 +841,7 @@ Statistics and performance metrics
stats.skills.rsquared
stats.skills.mse
stats.skills.rmse
stats.skills.rsr

Extreme Value Analysis
=======================
Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Unreleased
Added
-----
- Export CLI now also accepts time tuples (#660)
- New stats.skills VE and RSR (#666)

Changed
-------
Expand All @@ -23,6 +24,7 @@ Fixed
-----
- Bug in `raster.set_crs` if input_crs is of type CRS. (#659)
- Export CLI now actually parses provided geoms. (#660)
- Bug in stats.skills for computation of pbias and MSE / RMSE. (#666)

Deprecated
----------
Expand Down
78 changes: 76 additions & 2 deletions hydromt/stats/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,35 @@ def percentual_bias(sim, obs, dim="time"):
return pbias


def volumetric_error(sim, obs, dim="time"):
r"""Return the volumetric error between two time series.
.. math::
VE= 1 - \\frac{\\sum_{i=1}^{N}(|sim_{i}-obs_{i}|)}{\\sum_{i=1}^{N}(obs_{i})}
Parameters
----------
sim : xarray DataArray
simulations time series
obs : xarray DataArray
observations time series
dim : str, optional
name of time dimension in sim and obs (the default is 'time')
Returns
-------
xarray DataArray
volumetric error
"""
# wrap numpy function
kwargs = dict(
input_core_dims=[[dim], [dim]], dask="parallelized", output_dtypes=[float]
)
ve = xr.apply_ufunc(_ve, sim, obs, **kwargs)
ve.name = "ve"
return ve


def nashsutcliffe(sim, obs, dim="time"):
r"""Return the Nash-Sutcliffe model efficiency.
Expand Down Expand Up @@ -351,6 +380,33 @@ def rmse(sim, obs, dim="time"):
return rmse


def rsr(sim, obs, dim="time"):
r"""Return the RMSE-observations standard deviation (RSR) between two time series.
.. math::
RSR=\\frac{RMSE}{STDEVobs}
Parameters
----------
sim : xarray DataArray
simulations time series
obs : xarray DataArray
observations time series
Returns
-------
xarray DataArray
the root squared error
"""
# wrap numpy function
kwargs = dict(
input_core_dims=[[dim], [dim]], dask="parallelized", output_dtypes=[float]
)
rsr = xr.apply_ufunc(_rsr, sim, obs, **kwargs)
rsr.name = "rsr"
return rsr


def kge(sim, obs, dim="time"):
r"""Return the Kling-Gupta Efficiency (KGE) of two time series.
Expand Down Expand Up @@ -458,15 +514,33 @@ def _bias(sim, obs, axis=-1):

def _pbias(sim, obs, axis=-1):
"""Percentual bias."""
return np.nansum(sim - obs, axis=axis) / np.nansum(obs, axis=axis)
return 100 * np.nansum(sim - obs, axis=axis) / np.nansum(obs, axis=axis)


def _ve(sim, obs, axis=-1):
"""Volumetric Error."""
ve = 1 - np.nansum(np.absolute(sim - obs), axis=axis) / np.nansum(obs, axis=axis)
return ve


def _mse(sim, obs, axis=-1):
"""Mean squared error."""
mse = np.nansum((obs - sim) ** 2, axis=axis)
mse = np.nansum((obs - sim) ** 2, axis=axis) / np.nansum(
np.isfinite(obs), axis=axis
)
return mse


def _rsr(sim, obs, axis=-1):
"""RMSE-observations standard deviation."""
rmse = np.sqrt(_mse(sim, obs, axis=axis))
stdev = np.sqrt(
np.nansum((obs - np.nanmean(obs, axis=axis)) ** 2, axis=axis)
/ np.nansum(np.isfinite(obs), axis=axis)
)
return rmse / stdev


def _nse(sim, obs, axis=-1):
"""nash-sutcliffe efficiency."""
obs_mean = np.nanmean(obs, axis=axis)
Expand Down
8 changes: 5 additions & 3 deletions tests/test_stats_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
def test_skills(obsda):
simda = obsda + 5.0
assert np.isclose(skills.bias(simda, obsda).values, 5.0)
assert np.isclose(skills.percentual_bias(simda, obsda).values, 0.1033)
assert np.isclose(skills.percentual_bias(simda, obsda).values, 10.3299)
assert np.isclose(skills.volumetric_error(simda, obsda).values, 0.8967)
assert np.isclose(skills.nashsutcliffe(simda, obsda).values, 0.97015)
assert np.isclose(skills.lognashsutcliffe(simda, obsda).values, 0.8517)
assert np.isclose(skills.pearson_correlation(simda, obsda).values, 1.0)
Expand All @@ -21,5 +22,6 @@ def test_skills(obsda):
skills.kge_non_parametric_flood(simda, obsda)["kge_np_flood"].values, 0.8939
)
assert np.isclose(skills.rsquared(simda, obsda).values, 1.0)
assert np.isclose(skills.mse(simda, obsda).values, 9125.0)
assert np.isclose(skills.rmse(simda, obsda).values, 95.5249)
assert np.isclose(skills.mse(simda, obsda).values, 25.0)
assert np.isclose(skills.rmse(simda, obsda).values, 5.0)
assert np.isclose(skills.rsr(simda, obsda).values, 0.1727659)

0 comments on commit 96e23a0

Please sign in to comment.