An error occurred while loading the file. Please try again.
-
Thibault Hallouin authoredb1554f4b
#include <unordered_map>
#include <xtensor/xmath.hpp>
#include <xtensor/xview.hpp>
#include <xtensor/xoperation.hpp>
#include "evaluator.h"
namespace eh = evalhyd;
// NOTE ------------------------------------------------------------------------
// All equations in metrics below are following notations from
// "Wilks, D. S. (2011). Statistical methods in the atmospheric sciences.
// Amsterdam; Boston: Elsevier Academic Press. ISBN: 9780123850225".
// In particular, pp. 302-303, 332-333.
// -----------------------------------------------------------------------------
namespace evalhyd
{
namespace probabilist
{
// Compute the Brier score (BS).
//
// \require o_k:
// Observed event outcome.
// shape: (thresholds, time)
// \require y_k:
// Event probability forecast.
// shape: (thresholds, time)
// \assign bs:
// 2D array of Brier Score for each threshold.
// shape: (thresholds, 1)
void Evaluator::calc_bs()
{
// return computed Brier score(s)
// $BS = \frac{1}{n} \sum_{k=1}^{n} (o_k - y_k)^2$
bs = xt::mean(xt::square(o_k - y_k), -1, xt::keep_dims);
}
// Compute the calibration-refinement decomposition of the Brier score
// into reliability, resolution, and uncertainty.
//
// BS = reliability - resolution + uncertainty
//
// \require q_thr:
// 1D array of streamflow exceedance threshold(s).
// shape: (thresholds,)
// \require o_k:
// Observed event outcome.
// shape: (thresholds, time)
// \require y_k:
// Event probability forecast.
// shape: (thresholds, time)
// \assign bs_crd:
// 2D array of Brier score components (reliability, resolution,
// uncertainty) for each threshold.
// shape: (thresholds, components)
void Evaluator::calc_bs_crd()
{
// declare internal variables
// shape: (bins, thresholds, time)
xt::xtensor<double, 3> msk_bins;
// shape: (thresholds,)
xt::xtensor<double, 1> bar_o;
// shape: (bins, thresholds)
xt::xtensor<double, 2> N_i, bar_o_i;
// shape: (bins,)
xt::xtensor<double, 1> y_i;
// define some dimensions
std::size_t n = o_k.shape(1);
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
std::size_t n_thr = o_k.shape(0);
std::size_t n_mbr = q_frc.shape(0);
std::size_t n_cmp = 3;
// initialise output variable
// shape: (components, thresholds)
bs_crd = xt::zeros<double>({n_thr, n_cmp});
// compute range of forecast values $y_i$
y_i = xt::arange<double>(double(n_mbr + 1)) / n_mbr;
// compute mask to subsample time steps belonging to same forecast bin
// (where bins are defined as the range of forecast values)
msk_bins = xt::equal(
y_k, xt::view(y_i, xt::all(), xt::newaxis(), xt::newaxis())
);
// compute number of forecasts in each forecast bin $N_i$
N_i = xt::sum(msk_bins, -1);
// compute subsample relative frequency
// $\bar{o_i} = \frac{1}{N_i} \sum_{k \in N_i} o_k$
bar_o_i = xt::where(
N_i > 0,
xt::sum(
xt::where(
msk_bins,
xt::view(o_k, xt::newaxis(),
xt::all(), xt::all()),
0.
),
-1
) / N_i,
0.
);
// compute mean "climatology" relative frequency of the event
// $\bar{o} = \frac{1}{n} \sum_{k=1}^{n} o_k$
bar_o = xt::mean(o_k, -1);
// calculate reliability =
// $\frac{1}{n} \sum_{i=1}^{I} N_i (y_i - \bar{o_i})^2$
xt::col(bs_crd, 0) =
xt::sum(
xt::square(
xt::view(y_i, xt::all(), xt::newaxis())
- bar_o_i
) * N_i,
0
) / n;
// calculate resolution =
// $\frac{1}{n} \sum_{i=1}^{I} N_i (\bar{o_i} - \bar{o})^2$
xt::col(bs_crd, 1) =
xt::sum(
xt::square(
bar_o_i - bar_o
) * N_i,
0
) / n;
// calculate uncertainty = $\bar{o} (1 - \bar{o})$
xt::col(bs_crd, 2) = bar_o * (1 - bar_o);
}
// Compute the likelihood-base rate decomposition of the Brier score
// into type 2 bias, discrimination, and sharpness (a.k.a. refinement).
//
// BS = type 2 bias - discrimination + sharpness
//
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
// \require o_k:
// Observed event outcome.
// shape: (thresholds, time)
// \require y_k:
// Event probability forecast.
// shape: (thresholds, time)
// \return bs_lbd:
// 2D array of Brier score components (type 2 bias, discrimination,
// sharpness) for each threshold.
// shape: (thresholds, components)
void Evaluator::calc_bs_lbd()
{
// declare internal variables
// shape: (bins, thresholds, time)
xt::xtensor<double, 3> msk_bins;
// shape: (thresholds,)
xt::xtensor<double, 1> bar_y;
// shape: (bins, thresholds)
xt::xtensor<double, 2> M_j, bar_y_j;
// shape: (bins,)
xt::xtensor<double, 1> o_j;
// define some dimensions
std::size_t n = o_k.shape(1);
std::size_t n_thr = o_k.shape(0);
std::size_t n_cmp = 3;
// declare and initialise output variable
// shape: (components, thresholds)
bs_lbd = xt::zeros<double>({n_thr, n_cmp});
// set the range of observed values $o_j$
o_j = {0., 1.};
// compute mask to subsample time steps belonging to same observation bin
// (where bins are defined as the range of forecast values)
msk_bins = xt::equal(
o_k, xt::view(o_j, xt::all(), xt::newaxis(), xt::newaxis())
);
// compute number of observations in each observation bin $M_j$
M_j = xt::sum(msk_bins, -1);
// compute subsample relative frequency
// $\bar{y_j} = \frac{1}{M_j} \sum_{k \in M_j} y_k$
bar_y_j = xt::where(
M_j > 0,
xt::sum(
xt::where(
msk_bins,
xt::view(y_k, xt::newaxis(),
xt::all(), xt::all()),
0.
),
-1
) / M_j,
0.
);
// compute mean "climatology" forecast probability
// $\bar{y} = \frac{1}{n} \sum_{k=1}^{n} y_k$
bar_y = xt::mean(y_k, -1);
// calculate type 2 bias =
// $\frac{1}{n} \sum_{j=1}^{J} M_j (o_j - \bar{y_j})^2$
xt::col(bs_lbd, 0) =
xt::sum(
xt::square(
xt::view(o_j, xt::all(), xt::newaxis())
- bar_y_j
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
) * M_j,
0
) / n;
// calculate discrimination =
// $\frac{1}{n} \sum_{j=1}^{J} M_j (\bar{y_j} - \bar{y})^2$
xt::col(bs_lbd, 1) =
xt::sum(
xt::square(
bar_y_j - bar_y
) * M_j,
0
) / n;
// calculate sharpness =
// $\frac{1}{n} \sum_{k=1}^{n} (\bar{y_k} - \bar{y})^2$
xt::col(bs_lbd, 2) =
xt::sum(
xt::square(
y_k -
xt::view(bar_y, xt::all(), xt::newaxis())
),
-1
) / n;
}
// Compute the Brier skill score (BSS).
//
// \require o_k:
// Observed event outcome.
// shape: (thresholds, time)
// \require bs:
// Brier score(s).
// shape: (thresholds,)
// \assign bss:
// 2D array of Brier skill score for each threshold.
// shape: (thresholds, 1)
void Evaluator::calc_bss()
{
// calculate mean observed event outcome
// $\bar{o_k} = \frac{1}{n} \sum_{k=1}^{n} o_k$
xt::xtensor<double, 2> bar_o_k = xt::mean(o_k, -1, xt::keep_dims);
// calculate reference Brier score(s)
// $BS_{ref} = \frac{1}{n} \sum_{k=1}^{n} (o_k - \bar{o_k})^2$
xt::xtensor<double, 2> bs_ref = xt::mean(
xt::square(o_k - bar_o_k), -1, xt::keep_dims
);
// return computed Brier skill score(s)
// $BSS = 1 - \frac{BS}{BS_{ref}}
bss = 1 - (bs / bs_ref);
}
}
}