evaluator.hpp 10.80 KiB
#ifndef EVALHYD_DETERMINIST_EVALUATOR_HPP
#define EVALHYD_DETERMINIST_EVALUATOR_HPP
#include <xtensor/xexpression.hpp>
#include <xtensor/xtensor.hpp>
namespace evalhyd
    namespace determinist
        template <class A>
        class Evaluator
        private:
            // members for input data
            const A& q_obs;
            const A& q_prd;
            // members for computational elements
            A mean_obs;
            A mean_prd;
            A quad_err;
            A quad_obs;
            A quad_prd;
            A r_pearson;
            A bias;
        public:
            // constructor method
            Evaluator(const xt::xexpression<A>& obs,
                      const xt::xexpression<A>& prd) :
                    q_obs{obs.derived_cast()},
                    q_prd{prd.derived_cast()}
                // check that data dimensions are compatible
                if (q_prd.dimension() != q_obs.dimension())
                    throw std::runtime_error(
                            "number of dimensions of 'sim' and 'obs' "
                            "must be identical"
            // members for evaluation metrics
            A RMSE;
            A NSE;
            A KGE;
            A KGEPRIME;
            // methods to compute elements
            void calc_mean_obs();
            void calc_mean_prd();
            void calc_quad_err();
            void calc_quad_obs();
            void calc_quad_prd();
            void calc_r_pearson();
            void calc_bias();
            // methods to compute metrics
            void calc_RMSE();
            void calc_NSE();
            void calc_KGE();
            void calc_KGEPRIME();
        // Compute the mean of the observations.
        // \require q_obs:
        //     Streamflow observations.
        //     shape: ({1, ...}, time)
        // \assign mean_obs:
        //     Mean observed streamflow.
        //     shape: ({1, ...}, 1)
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
template <class A> void Evaluator<A>::calc_mean_obs() { mean_obs = xt::mean(q_obs, -1, xt::keep_dims); } // Compute the mean of the predictions. // // \require q_prd: // Streamflow predictions. // shape: ({1+, ...}, time) // \assign mean_prd: // Mean predicted streamflow. // shape: ({1+, ...}, 1) template <class A> void Evaluator<A>::calc_mean_prd() { mean_prd = xt::mean(q_prd, -1, xt::keep_dims); } // Compute the quadratic error between observations and predictions. // // \require q_obs: // Streamflow observations. // shape: ({1, ...}, time) // \require q_prd: // Streamflow predictions. // shape: ({1+, ...}, time) // \assign quad_err: // Quadratic errors between observations and predictions. // shape: ({1+, ...}, time) template <class A> void Evaluator<A>::calc_quad_err() { quad_err = xt::square(q_obs - q_prd); } // Compute the quadratic error between observations and mean observation. // // \require q_obs: // Streamflow observations. // shape: ({1, ...}, time) // \require mean_obs: // Mean observed streamflow. // shape: ({1, ...}, 1) // \assign quad_obs: // Quadratic errors between observations and mean observation. // shape: ({1, ...}, time) template <class A> void Evaluator<A>::calc_quad_obs() { quad_obs = xt::square(q_obs - mean_obs); } // Compute the quadratic error between predictions and mean prediction. // // \require q_prd: // Streamflow predictions. // shape: ({1+, ...}, time) // \require mean_prd: // Mean predicted streamflow. // shape: ({1+, ...}, 1) // \assign quad_prd: // Quadratic errors between predictions and mean prediction. // shape: ({1+, ...}, time) template <class A> void Evaluator<A>::calc_quad_prd() { quad_prd = xt::square(q_prd - mean_prd); }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
// Compute the Pearson correlation coefficient. // // \require q_obs: // Streamflow observations. // shape: ({1, ...}, time) // \require mean_obs: // Mean observed streamflow. // shape: ({1, ...}, 1) // \require q_prd: // Streamflow predictions. // shape: ({1+, ...}, time) // \require mean_prd: // Mean predicted streamflow. // shape: ({1+, ...}, 1) // \require quad_obs: // Quadratic errors between observations and mean observation. // shape: ({1, ...}, time) // \require quad_prd: // Quadratic errors between predictions and mean prediction. // shape: ({1+, ...}, time) // \assign r_pearson: // Pearson correlation coefficients. // shape: ({1+, ...}, time) template <class A> void Evaluator<A>::calc_r_pearson() { // calculate error in timing and dynamics $r_{pearson}$ // (Pearson's correlation coefficient) auto r_num = xt::sum( (q_prd - mean_prd) * (q_obs - mean_obs), -1, xt::keep_dims ); auto r_den = xt::sqrt( xt::sum(quad_prd, -1, xt::keep_dims) * xt::sum(quad_obs, -1, xt::keep_dims) ); r_pearson = r_num / r_den; } // Compute the bias. // // \require q_obs: // Streamflow observations. // shape: ({1, ...}, time) // \require q_prd: // Streamflow predictions. // shape: ({1+, ...}, time) // \assign bias: // Biases. // shape: ({1+, ...}, 1) template <class A> void Evaluator<A>::calc_bias() { // calculate $bias$ bias = xt::sum(q_prd, -1, xt::keep_dims) / xt::sum(q_obs, -1, xt::keep_dims); } // Compute the root-mean-square error (RMSE). // // \require quad_err: // Quadratic errors between observations and predictions. // shape: ({1+, ...}, time) // \assign RMSE: // Root-mean-square errors. // shape: ({1+, ...}, 1) template <class A> void Evaluator<A>::calc_RMSE() {
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
// compute RMSE RMSE = xt::sqrt(xt::mean(quad_err, -1, xt::keep_dims)); } // Compute the Nash-Sutcliffe Efficiency (NSE). // // If multi-dimensional arrays are provided, the arrays of predictions // and observations must feature the same number of dimensions, they // must be broadcastable, and their temporal dimensions must be along // their last axis. // // \require quad_err: // Quadratic errors between observations and predictions. // shape: ({1+, ...}, time) // \require quad_obs: // Quadratic errors between observations and mean observation. // shape: ({1, ...}, time) // \assign NSE: // Nash-Sutcliffe efficiencies. // shape: ({1+, ...}, 1) template <class A> void Evaluator<A>::calc_NSE() { // compute squared errors operands A f_num = xt::sum(quad_err, -1, xt::keep_dims); A f_den = xt::sum(quad_obs, -1, xt::keep_dims); // compute NSE NSE = 1 - (f_num / f_den); } // Compute the Kling-Gupta Efficiency (KGE). // // If multi-dimensional arrays are provided, the arrays of predictions // and observations must feature the same number of dimensions, they // must be broadcastable, and their temporal dimensions must be along // their last axis. // // \require q_obs: // Streamflow observations. // shape: ({1, ...}, time) // \require q_prd: // Streamflow predictions. // shape: ({1+, ...}, time) // \require r_pearson: // Pearson correlation coefficients. // shape: ({1+, ...}, time) // \require bias: // Biases. // shape: ({1+, ...}, 1) // \assign KGE: // Kling-Gupta efficiencies. // shape: ({1+, ...}, 1) template <class A> void Evaluator<A>::calc_KGE() { // calculate error in spread of flow $alpha$ auto alpha = xt::stddev(q_prd, {q_prd.dimension() - 1}, xt::keep_dims) / xt::stddev(q_obs, {q_obs.dimension() - 1}, xt::keep_dims); // compute KGE KGE = 1 - xt::sqrt( xt::square(r_pearson - 1) + xt::square(alpha - 1) + xt::square(bias - 1) ); } // Compute the modified Kling-Gupta Efficiency (KGEPRIME).
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
// // If multi-dimensional arrays are provided, the arrays of predictions // and observations must feature the same number of dimensions, they // must be broadcastable, and their temporal dimensions must be along // their last axis. // // \require q_obs: // Streamflow observations. // shape: ({1, ...}, time) // \require mean_obs: // Mean observed streamflow. // shape: ({1, ...}, 1) // \require q_prd: // Streamflow predictions. // shape: ({1+, ...}, time) // \require mean_prd: // Mean predicted streamflow. // shape: ({1+, ...}, 1) // \require r_pearson: // Pearson correlation coefficients. // shape: ({1+, ...}, time) // \require bias: // Biases. // shape: ({1+, ...}, 1) // \assign KGEPRIME: // Modified Kling-Gupta efficiencies. // shape: ({1+, ...}, 1) template <class A> void Evaluator<A>::calc_KGEPRIME() { // calculate error in spread of flow $gamma$ auto gamma = (xt::stddev(q_prd, {q_prd.dimension() - 1}, xt::keep_dims) / mean_prd) / (xt::stddev(q_obs, {q_obs.dimension() - 1}, xt::keep_dims) / mean_obs); // compute KGE KGEPRIME = 1 - xt::sqrt( xt::square(r_pearson - 1) + xt::square(gamma - 1) + xt::square(bias - 1) ); } } } #endif //EVALHYD_DETERMINIST_EVALUATOR_HPP