Last active
August 29, 2015 14:17
-
-
Save johnDorian/773aa58633d6c4261a24 to your computer and use it in GitHub Desktop.
A fast (Rcpp) version of the Nash–Sutcliffe model efficiency coefficient
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Load the libraries | |
library(Rcpp) | |
library(hydroGOF) | |
# Load the source cpp file with a few gof values. | |
sourceCpp("gof.cpp") | |
# Create some data to test and compare the two functions. | |
obs <- rnorm(10) | |
sim <- rnorm(10) | |
# Compare the two functions | |
NSE_fast(sim,obs) | |
hydroGOF::NSE(sim,obs) | |
## And now a more realistic situation with many simulations (50k). | |
sim_results <- matrix(rnorm(365*50000), 365, 50000) | |
obs <- rnorm(365) | |
## Wrap the functions in a system.time function to have a look at the time taken to evaluate each call. | |
## Using an apply statement to go across the columns of the simulated values. | |
system.time(nse_fast_values <- apply(sim_results, 2, NSE_fast, obs=obs)) | |
## A comparison with the hydroGOF library | |
system.time(nse_hydroGOF_values <- apply(sim_results, 2, NSE, obs=obs)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [[Rcpp::depends(RcppArmadillo)]] | |
#include <RcppArmadillo.h> | |
using namespace Rcpp; | |
/* These functions are Rcpp versions of the goodness of fit functions from | |
the hydroGOF package see ?hydroGOF::gof for more info. | |
*/ | |
// This is r2 * b (slope) | |
// [[Rcpp::export]] | |
double br2_fast(arma::vec sim, arma::vec obs) { | |
arma::uvec non_nas = arma::find_finite(sim); | |
arma::colvec coef = arma::solve(obs.elem(non_nas), sim.elem(non_nas)); | |
double slope = coef(0); | |
arma::mat temp = cor(sim.elem(non_nas), obs.elem(non_nas)); | |
double r2 = pow(temp(0,0),2); | |
double res; | |
if(std::abs(slope)<=1){ | |
res = std::abs(slope)*r2; | |
} else { | |
res = r2/std::abs(slope); | |
} | |
return res; | |
} | |
// This should be r2 | |
// [[Rcpp::export]] | |
double r2_fast(arma::vec sim, arma::vec obs) { | |
// this locates all real numbers (ie non NAs) | |
arma::uvec non_nas = arma::find_finite(sim); | |
// this finds the coefficients of the linear relationship (not realy needed) | |
arma::colvec coef = arma::solve(obs.elem(non_nas), sim.elem(non_nas)); | |
// this gets the correlation between the two | |
arma::mat temp = cor(sim.elem(non_nas), obs.elem(non_nas)); | |
// then a simply square the correlation to get the r2 value | |
double r2 = pow(temp(0,0),2); | |
return r2; | |
} | |
//Volumetric efficiency | |
// [[Rcpp::export]] | |
double VE_fast(arma::vec sim, arma::vec obs) { | |
arma::uvec non_nas = arma::find_finite(sim); | |
return 1-accu(abs(obs.elem(non_nas)-sim.elem(non_nas)))/accu(obs.elem(non_nas)); | |
} | |
double minWONA(NumericVector x) { | |
int n = x.size(); | |
int count = 0; | |
for(int i = 0; i<n; i++){ | |
if(NumericVector::is_na(x(i))){ | |
count++; | |
} | |
} | |
if(count==n){ | |
return NA_REAL; | |
} else { | |
return(min(na_omit(x))); | |
} | |
} | |
// [[Rcpp::export]] | |
double NSE_fast(arma::vec sim, arma::vec obs) { | |
arma::uvec non_nas = arma::find_finite(sim); | |
return 1 - (accu(pow(obs.elem(non_nas)-sim.elem(non_nas), 2.0)))/ accu(pow(obs.elem(non_nas) - mean(obs.elem(non_nas)),2.0)); | |
} | |
// [[Rcpp::export]] | |
double tNSE(Rcpp::NumericVector sim, Rcpp::NumericVector obs, int w1= 5.0, int w2 = 5.0, int b=4){ | |
int n = sim.size(); | |
Rcpp::NumericMatrix res(n,n); | |
Rcpp::NumericMatrix c1(n,n); | |
Rcpp::NumericMatrix c2(n,n); | |
std::fill(res.begin(), res.end(), NA_REAL); | |
std::fill(c1.begin(), c1.end(), NA_REAL); | |
std::fill(c2.begin(), c2.end(), NA_REAL); | |
int k = 0; | |
for(int j = 0;j<(w1+1); j++){ | |
c1(j,k) = pow(sim(j)-obs(k),2) + pow(b,2)*pow(j-k,2); | |
res(j,k) = c1(j,k); | |
} | |
double cmin; | |
Rcpp::NumericVector tempVect2(2); | |
Rcpp::NumericVector tempVect4(4); | |
for(int k = 1; k<n;k++){ | |
for(int j = (k-w2); j<= (k+w1); j++){ | |
if(j < 0 || j >= n){ | |
} else { | |
c2(j,k) = pow(sim(j)-obs(k), 2) + pow(b, 2)* pow(j-k, 2) + c1(j,k-1); | |
if(j==0){ | |
cmin = NA_REAL; | |
} | |
if(j>0){ | |
tempVect2(0) = c1(j-1,k-1); | |
tempVect2(1) = c2(j-1,k-1); | |
cmin = minWONA(tempVect2); | |
} | |
if(j>1){ | |
tempVect4(0) = c1(j-1,k-1); | |
tempVect4(1) = c2(j-1,k-1); | |
tempVect4(2) = c1(j-2,k-1); | |
tempVect4(3) = c2(j-2,k-1); | |
cmin = minWONA(tempVect4); | |
} | |
if(NumericVector::is_na(cmin)){ | |
c1(j,k) = NA_REAL; | |
} else { | |
c1(j,k) = pow(sim(j)-obs(k), 2) + pow(b, 2)* pow(j-k, 2) + cmin; | |
} | |
} | |
} | |
} | |
Rcpp::NumericVector easy(2); | |
easy(0) = minWONA(c1(_, n-1)); | |
easy(1) = minWONA(c2(_, n-1)); | |
double tNSE = 1-(min(easy)/sum(pow(obs-mean(obs),2))); | |
return(tNSE); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment