diff --git a/include/evalhyd/detail/probabilist/evaluator.hpp b/include/evalhyd/detail/probabilist/evaluator.hpp index f8abf7ac1fafaa8f3cbc8f68d1cc697403c67a85..e39756fdecd371cda29fb0b097d8e12180131271 100644 --- a/include/evalhyd/detail/probabilist/evaluator.hpp +++ b/include/evalhyd/detail/probabilist/evaluator.hpp @@ -741,7 +741,7 @@ namespace evalhyd { WSS = metrics::calc_WSS( q_obs, get_c_lvl(), get_clim_bnds(), get_WS(), - n_sit, n_ldt, n_itv, n_msk, n_exp + t_msk, b_exp, n_sit, n_ldt, n_itv, n_msk, n_exp ); } return WSS.value(); diff --git a/include/evalhyd/detail/probabilist/intervals.hpp b/include/evalhyd/detail/probabilist/intervals.hpp index e46761f794db3c4d215a4f8706dc1a347e3129f1..9ccf2add4b15b937e84bade912c340ab07e4e0e5 100644 --- a/include/evalhyd/detail/probabilist/intervals.hpp +++ b/include/evalhyd/detail/probabilist/intervals.hpp @@ -7,6 +7,7 @@ #include <xtensor/xtensor.hpp> #include <xtensor/xview.hpp> +#include <xtensor/xindex_view.hpp> #include <xtensor/xsort.hpp> #include "../maths.hpp" @@ -220,21 +221,21 @@ namespace evalhyd { for (std::size_t i = 0; i < n_itv; i++) { + auto obs = xt::view(q_obs_masked_sampled, + s, l, xt::all()); + auto obs_filtered = + xt::filter(obs, !xt::isnan(obs)); + // lower bound xt::view(clim_bnds, s, l, m, e, i, 0) = evalhyd::maths::quantile( - xt::view(q_obs_masked_sampled, - s, l, xt::all()), - quantiles(i, 0) + obs_filtered, quantiles(i, 0) ); // upper bound xt::view(clim_bnds, s, l, m, e, i, 1) = evalhyd::maths::quantile( - xt::view(q_obs_masked_sampled, - s, l, xt::all()), - quantiles(i, 1) + obs_filtered, quantiles(i, 1) ); - } } } @@ -571,6 +572,12 @@ namespace evalhyd /// \param WS /// Winkler scores. /// shape: (sites, lead times, subsets, samples, intervals) + /// \param t_msk + /// Temporal subsets of the whole record. + /// shape: (sites, lead times, subsets, time) + /// \param b_exp + /// Boostrap samples. + /// shape: (samples, time slice) /// \param n_sit /// Number of sites. /// \param n_ldt @@ -590,6 +597,8 @@ namespace evalhyd const xt::xtensor<double, 1>& c_lvl, const xt::xtensor<double, 6>& clim_bnds, const xt::xtensor<double, 5>& WS, + const xt::xtensor<bool, 4>& t_msk, + const std::vector<xt::xkeep_slice<int>>& b_exp, std::size_t n_sit, std::size_t n_ldt, std::size_t n_itv, @@ -601,21 +610,38 @@ namespace evalhyd xt::xtensor<double, 5> WS_clim = xt::zeros<double>({n_sit, n_ldt, n_msk, n_exp, n_itv}); - for (std::size_t m = 0; m < n_msk; m++) + for (std::size_t l = 0; l < n_ldt; l++) { - for (std::size_t e = 0; e < n_exp; e++) + for (std::size_t m = 0; m < n_msk; m++) { - auto ws_clim = intermediate::calc_ws( - q_obs, c_lvl, - xt::view(clim_bnds, xt::all(), xt::all(), m, e, - xt::all(), xt::all(), xt::newaxis()) - ); + for (std::size_t e = 0; e < n_exp; e++) + { + auto ws_clim = intermediate::calc_ws( + q_obs, c_lvl, + xt::view(clim_bnds, xt::all(), l, xt::newaxis(), + m, e, xt::all(), xt::all(), + xt::newaxis()) + ); + + // apply the mask + // (using NaN workaround until reducers work on masked_view) + auto ws_clim_masked = xt::where( + xt::view(t_msk, xt::all(), l, m, xt::newaxis(), xt::all()), + xt::view(ws_clim, xt::all(), l, xt::all(), xt::all()), + NAN + ); + + // apply the bootstrap sampling + auto ws_clim_masked_sampled = + xt::view(ws_clim_masked, xt::all(), xt::all(), b_exp[e]); - xt::view(WS_clim, xt::all(), xt::all(), m, e, xt::all()) = - xt::nanmean(ws_clim, -1); + xt::view(WS_clim, xt::all(), l, m, e, xt::all()) = + xt::nanmean(ws_clim_masked_sampled, -1); + } } } + // compute the Winkler skill score return 1 - (WS / WS_clim); } }