From 8e87dc5cbf8aa0d25de1b5c86789d12fc8fcd9a2 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 3 May 2019 14:26:03 +0200 Subject: [PATCH 01/46] Upload New File --- R/CST_EnsBiasCorrection.R | 250 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 R/CST_EnsBiasCorrection.R diff --git a/R/CST_EnsBiasCorrection.R b/R/CST_EnsBiasCorrection.R new file mode 100644 index 00000000..2d520bb3 --- /dev/null +++ b/R/CST_EnsBiasCorrection.R @@ -0,0 +1,250 @@ + +#' Ensemble Bias Correction based on adjustment of ensemble mean, and ensemble spread +#' +#'@author Bert Van Schaeybroeck, \email{bertvs@meteo.be} +#'@description This function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +#' +#'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data} +#'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. +#' +#'@return an object of class \code{s2dv_cube} containing the bias corrected forecasts in the element called \code{$data} with the same dimensions of the experimental data. +#' +#'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. +#' +#'@import s2dverification +#'@examples +#' +#'# Example +#'# Creation of sample s2dverification objects. These are not complete +#'# s2dverification objects though. The Load function returns complete objects. +#'mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) +#'dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) +#'obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) +#'dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, lat = 6, lon = 7) +#'lon <- seq(0, 30, 5) +#'lat <- seq(0, 25, 5) +#'exp <- list(data = mod1, lat = lat, lon = lon) +#'obs <- list(data = obs1, lat = lat, lon = lon) +#'attr(exp, 'class') <- 's2dv_cube' +#'attr(obs, 'class') <- 's2dv_cube' +#'a <- CST_EnsBiasCorrection(exp = exp, obs = obs) +#'str(a) +#'@export + +library(abind) + +spr <- function(x, amt.spr, dim = 1) { + if(is.vector(x)){ + amt.dims <- 1 + arr.out <- array(rep(x, amt.spr), c(length(x), amt.spr)) + } else if(is.array(x)) { + amt.dims <- length(dim(x)) + arr.out <- array(rep(as.vector(x), amt.spr), c(dim(x), amt.spr)) + } else { + stop("x is not array nor vector but is ", class(x)) + } + amt.dims.out <- amt.dims + 1 + dims.tmp <- seq(1, amt.dims.out) + dims.tmp[seq(dim, amt.dims.out)] <- c(amt.dims + 1, seq(dim,amt.dims.out-1)) + arr.out <- aperm(arr.out, dims.tmp) + return(arr.out) +} + +calc.crps.opt <- function(par, quant.obs.fc){ + return( + with(quant.obs.fc, + mean( + apply(abs(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + + ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev)), c(2), mean, na.rm = T) - + abs((par[3])^2 * spr.abs + par[4]) / 2. + , na.rm = T) + ) + ) +} + +calc.crps <- function(obs, fc){ + quant.obs.fc <- calc.obs.fc.quant(obs = obs, fc = fc) + return(with(quant.obs.fc, + mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = T) - spr.abs / 2., na.rm = T))) +} + +calc.obs.fc.quant <- function(obs, fc){ + amt.mbr <- dim(fc)[1] + obs.per.ens <- spr(obs, amt.mbr) + fc.ens.av <- apply(fc, c(2), mean, na.rm = T) + cor.obs.fc <- cor(fc.ens.av, obs, use = "complete.obs") + obs.av <- mean(obs, na.rm = T) + obs.sd <- sd(obs, na.rm = T) + return( + append( + calc.fc.quant(fc = fc), + list( + obs.per.ens = obs.per.ens, + cor.obs.fc = cor.obs.fc, + obs.av = obs.av, + obs.sd = obs.sd + ) + ) + ) +} + + +calc.fc.quant <- function(fc){ + amt.mbr <- dim(fc)[1] + fc.ens.av <- apply(fc, c(2), mean, na.rm = T) + fc.ens.av.av <- mean(fc.ens.av, na.rm = T) + fc.ens.av.per.ens <- spr(fc.ens.av, amt.mbr) + fc.ens.sd <- apply(fc, c(2), sd, na.rm = T) + fc.ens.sd.av <- mean(fc.ens.sd,na.rm = T) + fc.dev <- fc - fc.ens.av.per.ens + repmat1.tmp <- spr(fc, amt.mbr) + repmat2.tmp <- aperm(repmat1.tmp, c(2, 1, 3)) + spr.abs <- apply(abs(repmat1.tmp - repmat2.tmp), c(3), mean, na.rm = T) + spr.abs.per.ens <- spr(spr.abs, amt.mbr) + fc.av <- mean(fc, na.rm = T) + fc.sd <- sd(fc, na.rm = T) + return( + list( + fc.ens.av = fc.ens.av, + fc.ens.av.av = fc.ens.av.av, + fc.ens.av.per.ens = fc.ens.av.per.ens, + fc.ens.sd = fc.ens.sd, + fc.ens.sd.av = fc.ens.sd.av, + fc.dev = fc.dev, + spr.abs = spr.abs, + spr.abs.per.ens = spr.abs.per.ens, + fc.av = fc.av, + fc.sd = fc.sd + ) + ) +} + +calc.wer.cr.par <- function(quant.obs.fc){ + par.out <- rep(NA, 3) + par.out[3] <- with(quant.obs.fc, sqrt(obs.sd * sqrt(1 - cor.obs.fc^2) / mean(fc.ens.sd.av, na.rm = T))) + par.out[2] <- with(quant.obs.fc, cor.obs.fc * obs.sd / fc.sd) + par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = T) + return(par.out) +} + +correct.mbm.fc <- function(fc, par){ + quant.fc.mp <- calc.fc.quant(fc = fc) + return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) +} + +CST_EnsBiasCorrection <- function(fc, obs) { + if (!inherits(exp, "s2dv_cube") || !inherits(fc, "s2dv_cube")) { + stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", + "as output by CSTools::CST_Load.") + } + + if (dim(obs$data)["member"] != 1) { + stop("The length of the dimension 'member' in the component 'data' ", + "of the parameter 'obs' must be equal to 1.") + } + + fc$data <- EnsBiasCorrection(fc = fc$data, obs = obs$data) + fc$Datasets <- c(fc$Datasets, obs$Datasets) + fc$source_files <- c(fc$source_files, obs$source_files) + return(fc) +} + +EnsBiasCorrection <- function (fc, obs) { + + if (!all(c("member", "sdate") %in% names(dim(fc)))) { + stop("Parameter 'fc' must have the dimensions 'member' and 'sdate'.") + } + + if (!all(c("sdate") %in% names(dim(obs)))) { + stop("Parameter 'obs' must have the dimension 'sdate'.") + } + + if (any(is.na(fc))) { + warning("Parameter 'fc' contains NA values.") + } + + if (any(is.na(obs))) { + warning("Parameter 'obs' contains NA values.") + } + + target.dims <- c("member", "sdate") + EnsBiasCorrected <- apply.obs.fc(obs = obs, + fc = fc, + target.dims = target.dims, + FUN = .esbc) + + return(EnsBiasCorrected) +} + +apply.obs.fc <- function(obs, fc, target.dims, FUN, ...){ + dimnames.tmp <- dimnames(fc) + fc.dims.tmp <- dim(fc) + dims.out.tmp <- fc.dims.tmp[target.dims] + obs.fc <- combine.obs.fc(obs, fc) + names.dim <- names(dim(obs.fc)) + amt.dims <- length(names.dim) + margin.all <- seq(1, amt.dims) + margin.to.use <- margin.all[-match(target.dims, names.dim)] + arr.out <- apply(X = obs.fc, + MARGIN = margin.to.use, + FUN = FUN) + dims.tmp <- dim(arr.out) + names.dims.tmp <- names(dim(arr.out)) + dim(arr.out) <- c(dims.out.tmp, dims.tmp[-1]) + names(dim(arr.out)) <- c(target.dims, names.dims.tmp[c(-1)]) + pos <- match(names.dim, names(dim(arr.out))) + arr.out <- aperm(arr.out, pos) + dimnames(arr.out) <- dimnames.tmp + return(arr.out) +} + +combine.obs.fc <- function(obs,fc){ + names.dim.tmp <- names(dim(obs)) + members.dim <- which(names.dim.tmp == "member") + arr.out <- abind(obs, fc, along = members.dim) + dimnames.tmp <- dimnames(arr.out) + names(dim(arr.out)) <- names.dim.tmp + dimnames(arr.out) <- dimnames.tmp + names(dimnames(arr.out)) <- names.dim.tmp + return(arr.out) +} + +.esbc <- function(obs.fc) { + dims.tmp=dim(obs.fc) + amt.mbr <- dims.tmp["member"][]-1 + amt.sdate <- dims.tmp["sdate"][] + pos <- match(c("member","sdate"), names(dims.tmp)) + obs.fc <- aperm(obs.fc, pos) + var.obs <- asub(obs.fc, list(1),1) + var.fc <- asub(obs.fc, list(1+seq(1, amt.mbr)),1) + dims.fc <- dim(var.fc) + var.cor.fc <- NA * var.fc + + + for (i.fc in seq(1, amt.sdate)) { + # defining forecast,hindcast and observation in cross-validation + fc.ev <- var.fc[ , i.fc, drop = F] + fc.tr <- var.fc[ , -i.fc] + obs.tr <- var.obs[-i.fc, drop = F] + + #calculate ensemble and observational characteristics + quant.obs.fc.tr <- calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) + #calculate initial value for regression parameters + init.par <- c(calc.wer.cr.par(quant.obs.fc.tr), 0.001) + #calculate regression parameters on training dataset + optim.tmp <- optim(par = init.par, fn = calc.crps.opt + , quant.obs.fc = quant.obs.fc.tr) #, control = list(maxit = 2,trace = T) + mbm.par <- optim.tmp$par + var.cor.fc[ , i.fc] <- correct.mbm.fc(fc.ev , mbm.par) + } + + cat("CRPS uncorr/init/optim/eval: ", + calc.crps(var.obs, var.fc), + calc.crps(var.obs, var.cor.fc),"\n") + + names(dim(var.cor.fc)) <- c("member", "sdate") + return(var.cor.fc) +} + + + -- GitLab From e273aad49bf73ccf73eef9999f3943398831df9c Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 3 May 2019 15:10:44 +0200 Subject: [PATCH 02/46] Upload New File --- tests/testthat/test_CST_EnsBiasCorrection.R | 82 +++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/testthat/test_CST_EnsBiasCorrection.R diff --git a/tests/testthat/test_CST_EnsBiasCorrection.R b/tests/testthat/test_CST_EnsBiasCorrection.R new file mode 100644 index 00000000..a6b122c2 --- /dev/null +++ b/tests/testthat/test_CST_EnsBiasCorrection.R @@ -0,0 +1,82 @@ +rm(list=ls()) + +context("Generic tests") +test_that("Sanity checks", { + expect_error( + CST_EnsBiasCorrection(exp = 1), + paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", + "as output by CSTools::CST_Load.")) + + + amt.dataset <- 2 + lst.dataset <- paste0("dataset_",seq(1,amt.dataset)) + amt.member <- 10 + lst.member <- paste0("mbr_",seq(1,amt.member)) + amt.sdate <- 100 + lst.sdate <- paste0(1980+seq(1,amt.sdate),"-01-01") + amt.ftime <- 10 + lst.ftime <- paste0("month_",seq(1,amt.ftime)) + amt.lon <- 2 + lst.lon <- paste0("lon_",seq(1,amt.lon)) + amt.lat <- 3 + lst.lat <- paste0("lat_",seq(1,amt.lat)) + + + bias <- 1. #define some parameters that distinguish observations from forecast& + slope <- 2. + noise.level.fc <- 0.1 * slope + noise.level.obs <- 0.1 + + amt.data.pts <- amt.dataset*amt.sdate*amt.ftime*amt.lon*amt.lat + + obs1 <- array(data = rnorm(amt.data.pts), + dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), + dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) + + tmp.noise.obs <- array(data = rnorm(amt.data.pts, 0, noise.level.obs), + dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), + dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) + + mod1 <- array(data = rnorm(amt.data.pts*amt.member, 0, noise.level.fc), + dim = c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), + dimnames = list(dataset = lst.dataset, member = lst.member, sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) + + + for(i.mbr in seq(1, amt.member)){ + mod1[ , i.mbr, , , , ] <- (obs1[ , 1, , , , ] + mod1[ , i.mbr, , , , ] + bias) / slope + } + obs1 <- obs1 + tmp.noise.obs + + lon <- seq(0, 30, amt.lon) + lat <- seq(0, 25, amt.lat) + exp <- list(data = mod1, lat = lat, lon = lon) + obs <- list(data = obs1, lat = lat, lon = lon) + attr(exp, 'class') <- 's2dv_cube' + attr(obs, 'class') <- 's2dv_cube' + + bc <- CST_EnsBiasCorrection(fc = exp, obs = obs) + + expect_equal(length(bc), 3) + expect_equal(dim(bc$data), + c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, + lat = amt.lat, lon = amt.lon)) + expect_equal(bc$lat, lat) + expect_equal(bc$lon, lon) + + expect_error(CST_EnsBiasCorrection(exp = exp, obs = exp), + paste0("The length of the dimension 'member' in the component 'data' ", + "of the parameter 'obs' must be equal to 1.")) + + exp2 <- exp + exp2$data[1, 2, 1, 1, 1, 1] <- NA + expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs), + "Parameter 'exp' contains NA values.") + + obs2 <- obs + obs2$data[1, 1, 2, 1, 1, 1] <- NA + expect_warning(CST_EnsBiasCorrection(exp = exp, obs = obs2), + "Parameter 'obs' contains NA values.") + + expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs2), + "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") +}) -- GitLab From f785b80fc73147c8f37929593d29c8e1de4853ee Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 6 May 2019 08:25:29 +0200 Subject: [PATCH 03/46] Update CST_EnsBiasCorrection.R: remove 'exp'. --- R/CST_EnsBiasCorrection.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/CST_EnsBiasCorrection.R b/R/CST_EnsBiasCorrection.R index 2d520bb3..a403b61f 100644 --- a/R/CST_EnsBiasCorrection.R +++ b/R/CST_EnsBiasCorrection.R @@ -1,4 +1,3 @@ - #' Ensemble Bias Correction based on adjustment of ensemble mean, and ensemble spread #' #'@author Bert Van Schaeybroeck, \email{bertvs@meteo.be} @@ -133,8 +132,8 @@ correct.mbm.fc <- function(fc, par){ } CST_EnsBiasCorrection <- function(fc, obs) { - if (!inherits(exp, "s2dv_cube") || !inherits(fc, "s2dv_cube")) { - stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", + if (!inherits(fc, "s2dv_cube") || !inherits(obs, "s2dv_cube")) { + stop("Parameter 'fc' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") } -- GitLab From 39d3653f615c767cbe2ba95153d1c3ee9a4dd5d5 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 6 May 2019 09:17:55 +0200 Subject: [PATCH 04/46] Replace CST_EnsBiasCorrection.R --- R/CST_EnsBiasCorrection.R | 40 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/R/CST_EnsBiasCorrection.R b/R/CST_EnsBiasCorrection.R index a403b61f..c4f966b8 100644 --- a/R/CST_EnsBiasCorrection.R +++ b/R/CST_EnsBiasCorrection.R @@ -1,3 +1,4 @@ + #' Ensemble Bias Correction based on adjustment of ensemble mean, and ensemble spread #' #'@author Bert Van Schaeybroeck, \email{bertvs@meteo.be} @@ -131,9 +132,9 @@ correct.mbm.fc <- function(fc, par){ return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) } -CST_EnsBiasCorrection <- function(fc, obs) { - if (!inherits(fc, "s2dv_cube") || !inherits(obs, "s2dv_cube")) { - stop("Parameter 'fc' and 'obs' must be of the class 's2dv_cube', ", +CST_EnsBiasCorrection <- function(exp, obs) { + if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { + stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") } @@ -142,31 +143,27 @@ CST_EnsBiasCorrection <- function(fc, obs) { "of the parameter 'obs' must be equal to 1.") } - fc$data <- EnsBiasCorrection(fc = fc$data, obs = obs$data) - fc$Datasets <- c(fc$Datasets, obs$Datasets) - fc$source_files <- c(fc$source_files, obs$source_files) - return(fc) + exp$data <- EnsBiasCorrection(fc = exp$data, obs = obs$data) + exp$Datasets <- c(exp$Datasets, obs$Datasets) + exp$source_files <- c(exp$source_files, obs$source_files) + return(exp) } EnsBiasCorrection <- function (fc, obs) { - if (!all(c("member", "sdate") %in% names(dim(fc)))) { - stop("Parameter 'fc' must have the dimensions 'member' and 'sdate'.") + target.dims <- c("member", "sdate") + if (!all(target.dims %in% names(dim(fc)))) { + stop("Parameter 'exp' must have the dimensions 'member' and 'sdate'.") } - if (!all(c("sdate") %in% names(dim(obs)))) { stop("Parameter 'obs' must have the dimension 'sdate'.") } - if (any(is.na(fc))) { - warning("Parameter 'fc' contains NA values.") - } - + warning("Parameter 'exp' contains NA values.") + } if (any(is.na(obs))) { warning("Parameter 'obs' contains NA values.") } - - target.dims <- c("member", "sdate") EnsBiasCorrected <- apply.obs.fc(obs = obs, fc = fc, target.dims = target.dims, @@ -221,7 +218,7 @@ combine.obs.fc <- function(obs,fc){ for (i.fc in seq(1, amt.sdate)) { - # defining forecast,hindcast and observation in cross-validation + # defining training (tr) and evaluation (ev) subsets fc.ev <- var.fc[ , i.fc, drop = F] fc.tr <- var.fc[ , -i.fc] obs.tr <- var.obs[-i.fc, drop = F] @@ -232,14 +229,15 @@ combine.obs.fc <- function(obs,fc){ init.par <- c(calc.wer.cr.par(quant.obs.fc.tr), 0.001) #calculate regression parameters on training dataset optim.tmp <- optim(par = init.par, fn = calc.crps.opt - , quant.obs.fc = quant.obs.fc.tr) #, control = list(maxit = 2,trace = T) + , quant.obs.fc = quant.obs.fc.tr) mbm.par <- optim.tmp$par + #correct evaluation subset var.cor.fc[ , i.fc] <- correct.mbm.fc(fc.ev , mbm.par) } - cat("CRPS uncorr/init/optim/eval: ", - calc.crps(var.obs, var.fc), - calc.crps(var.obs, var.cor.fc),"\n") + #cat("CRPS uncorr/init/optim/eval: ", + # calc.crps(var.obs, var.fc), + # calc.crps(var.obs, var.cor.fc),"\n") names(dim(var.cor.fc)) <- c("member", "sdate") return(var.cor.fc) -- GitLab From 1a6c02580a84da8890afcfcd07fbc6ea7fe25884 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 6 May 2019 09:18:40 +0200 Subject: [PATCH 05/46] Replace test_CST_EnsBiasCorrection.R --- tests/testthat/test_CST_EnsBiasCorrection.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test_CST_EnsBiasCorrection.R b/tests/testthat/test_CST_EnsBiasCorrection.R index a6b122c2..60462c82 100644 --- a/tests/testthat/test_CST_EnsBiasCorrection.R +++ b/tests/testthat/test_CST_EnsBiasCorrection.R @@ -10,11 +10,11 @@ test_that("Sanity checks", { amt.dataset <- 2 lst.dataset <- paste0("dataset_",seq(1,amt.dataset)) - amt.member <- 10 + amt.member <- 5 lst.member <- paste0("mbr_",seq(1,amt.member)) - amt.sdate <- 100 + amt.sdate <- 30 lst.sdate <- paste0(1980+seq(1,amt.sdate),"-01-01") - amt.ftime <- 10 + amt.ftime <- 2 lst.ftime <- paste0("month_",seq(1,amt.ftime)) amt.lon <- 2 lst.lon <- paste0("lon_",seq(1,amt.lon)) @@ -54,12 +54,12 @@ test_that("Sanity checks", { attr(exp, 'class') <- 's2dv_cube' attr(obs, 'class') <- 's2dv_cube' - bc <- CST_EnsBiasCorrection(fc = exp, obs = obs) + bc <- CST_EnsBiasCorrection(exp = exp, obs = obs) expect_equal(length(bc), 3) expect_equal(dim(bc$data), c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, - lat = amt.lat, lon = amt.lon)) + lon = amt.lon, lat = amt.lat)) expect_equal(bc$lat, lat) expect_equal(bc$lon, lon) -- GitLab From 1c1b0c48494b4813488c211fda15706ad605c02b Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 6 May 2019 10:16:06 +0200 Subject: [PATCH 06/46] Update test_CST_EnsBiasCorrection.R --- tests/testthat/test_CST_EnsBiasCorrection.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/testthat/test_CST_EnsBiasCorrection.R b/tests/testthat/test_CST_EnsBiasCorrection.R index 60462c82..64f63acb 100644 --- a/tests/testthat/test_CST_EnsBiasCorrection.R +++ b/tests/testthat/test_CST_EnsBiasCorrection.R @@ -1,5 +1,3 @@ -rm(list=ls()) - context("Generic tests") test_that("Sanity checks", { expect_error( -- GitLab From bd0323862c10e4d714c949e0ed0076e7930f7250 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 6 May 2019 10:23:20 +0200 Subject: [PATCH 07/46] Update test_CST_EnsBiasCorrection.R --- tests/testthat/test_CST_EnsBiasCorrection.R | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testthat/test_CST_EnsBiasCorrection.R b/tests/testthat/test_CST_EnsBiasCorrection.R index 64f63acb..9c6e8c5d 100644 --- a/tests/testthat/test_CST_EnsBiasCorrection.R +++ b/tests/testthat/test_CST_EnsBiasCorrection.R @@ -1,3 +1,4 @@ +library(abind) context("Generic tests") test_that("Sanity checks", { expect_error( -- GitLab From 739098e7f193b44e02a77ed29ee5551408a7e8a0 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 6 May 2019 10:49:06 +0200 Subject: [PATCH 08/46] Update test_CST_EnsBiasCorrection.R --- tests/testthat/test_CST_EnsBiasCorrection.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test_CST_EnsBiasCorrection.R b/tests/testthat/test_CST_EnsBiasCorrection.R index 9c6e8c5d..9da3822a 100644 --- a/tests/testthat/test_CST_EnsBiasCorrection.R +++ b/tests/testthat/test_CST_EnsBiasCorrection.R @@ -56,9 +56,9 @@ test_that("Sanity checks", { bc <- CST_EnsBiasCorrection(exp = exp, obs = obs) expect_equal(length(bc), 3) - expect_equal(dim(bc$data), - c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, - lon = amt.lon, lat = amt.lat)) + expect_equal(as.numeric(dim(bc$data)), + as.numeric(c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, + lon = amt.lon, lat = amt.lat))) expect_equal(bc$lat, lat) expect_equal(bc$lon, lon) -- GitLab From 4b408bbf683fbe4bd346813c47dada9e9b869094 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 10 May 2019 12:19:49 +0200 Subject: [PATCH 09/46] changed filename --- tests/testthat/test_CST_BiasCorrection.R | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/testthat/test_CST_BiasCorrection.R diff --git a/tests/testthat/test_CST_BiasCorrection.R b/tests/testthat/test_CST_BiasCorrection.R new file mode 100644 index 00000000..c5525c57 --- /dev/null +++ b/tests/testthat/test_CST_BiasCorrection.R @@ -0,0 +1,45 @@ +context("Generic tests") +test_that("Sanity checks", { + expect_error( + CST_BiasCorrection(exp = 1), + paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", + "as output by CSTools::CST_Load.")) + + mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) + obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) + dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, + lat = 6, lon = 7) + dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, + lat = 6, lon = 7) + lon <- seq(0, 30, 5) + lat <- seq(0, 25, 5) + exp <- list(data = mod1, lat = lat, lon = lon) + obs <- list(data = obs1, lat = lat, lon = lon) + attr(exp, 'class') <- 's2dv_cube' + attr(obs, 'class') <- 's2dv_cube' + + bc <- CST_BiasCorrection(exp = exp, obs = obs) + expect_equal(length(bc), 3) + expect_equal(dim(bc$data), + c(dataset = 1, member = 3, sdate = 4, ftime = 5, + lat = 6, lon = 7)) + expect_equal(bc$lat, lat) + expect_equal(bc$lon, lon) + + expect_error(CST_BiasCorrection(exp = exp, obs = exp), + paste0("The length of the dimension 'member' in the component 'data' ", + "of the parameter 'obs' must be equal to 1.")) + + exp2 <- exp + exp2$data[1, 2, 1, 1, 1, 1] <- NA + expect_warning(CST_BiasCorrection(exp = exp2, obs = obs), + "Parameter 'exp' contains NA values.") + + obs2 <- obs + obs2$data[1, 1, 2, 1, 1, 1] <- NA + expect_warning(CST_BiasCorrection(exp = exp, obs = obs2), + "Parameter 'obs' contains NA values.") + + expect_warning(CST_BiasCorrection(exp = exp2, obs = obs2), + "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") +}) -- GitLab From f80ba9ea347f0b89f8026079fa106d3d488d2147 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 10 May 2019 12:21:01 +0200 Subject: [PATCH 10/46] Delete test-CST_BiasCorrection.R --- tests/testthat/test-CST_BiasCorrection.R | 45 ------------------------ 1 file changed, 45 deletions(-) delete mode 100644 tests/testthat/test-CST_BiasCorrection.R diff --git a/tests/testthat/test-CST_BiasCorrection.R b/tests/testthat/test-CST_BiasCorrection.R deleted file mode 100644 index c5525c57..00000000 --- a/tests/testthat/test-CST_BiasCorrection.R +++ /dev/null @@ -1,45 +0,0 @@ -context("Generic tests") -test_that("Sanity checks", { - expect_error( - CST_BiasCorrection(exp = 1), - paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", - "as output by CSTools::CST_Load.")) - - mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) - obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) - dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, - lat = 6, lon = 7) - dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, - lat = 6, lon = 7) - lon <- seq(0, 30, 5) - lat <- seq(0, 25, 5) - exp <- list(data = mod1, lat = lat, lon = lon) - obs <- list(data = obs1, lat = lat, lon = lon) - attr(exp, 'class') <- 's2dv_cube' - attr(obs, 'class') <- 's2dv_cube' - - bc <- CST_BiasCorrection(exp = exp, obs = obs) - expect_equal(length(bc), 3) - expect_equal(dim(bc$data), - c(dataset = 1, member = 3, sdate = 4, ftime = 5, - lat = 6, lon = 7)) - expect_equal(bc$lat, lat) - expect_equal(bc$lon, lon) - - expect_error(CST_BiasCorrection(exp = exp, obs = exp), - paste0("The length of the dimension 'member' in the component 'data' ", - "of the parameter 'obs' must be equal to 1.")) - - exp2 <- exp - exp2$data[1, 2, 1, 1, 1, 1] <- NA - expect_warning(CST_BiasCorrection(exp = exp2, obs = obs), - "Parameter 'exp' contains NA values.") - - obs2 <- obs - obs2$data[1, 1, 2, 1, 1, 1] <- NA - expect_warning(CST_BiasCorrection(exp = exp, obs = obs2), - "Parameter 'obs' contains NA values.") - - expect_warning(CST_BiasCorrection(exp = exp2, obs = obs2), - "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") -}) -- GitLab From 14659672d3ebe8679f649d57cda2082f0eb5cdae Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 10 May 2019 15:00:37 +0200 Subject: [PATCH 11/46] change name --- tests/testthat/test-CST_EnsBiasCorrection.R | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/testthat/test-CST_EnsBiasCorrection.R diff --git a/tests/testthat/test-CST_EnsBiasCorrection.R b/tests/testthat/test-CST_EnsBiasCorrection.R new file mode 100644 index 00000000..9da3822a --- /dev/null +++ b/tests/testthat/test-CST_EnsBiasCorrection.R @@ -0,0 +1,81 @@ +library(abind) +context("Generic tests") +test_that("Sanity checks", { + expect_error( + CST_EnsBiasCorrection(exp = 1), + paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", + "as output by CSTools::CST_Load.")) + + + amt.dataset <- 2 + lst.dataset <- paste0("dataset_",seq(1,amt.dataset)) + amt.member <- 5 + lst.member <- paste0("mbr_",seq(1,amt.member)) + amt.sdate <- 30 + lst.sdate <- paste0(1980+seq(1,amt.sdate),"-01-01") + amt.ftime <- 2 + lst.ftime <- paste0("month_",seq(1,amt.ftime)) + amt.lon <- 2 + lst.lon <- paste0("lon_",seq(1,amt.lon)) + amt.lat <- 3 + lst.lat <- paste0("lat_",seq(1,amt.lat)) + + + bias <- 1. #define some parameters that distinguish observations from forecast& + slope <- 2. + noise.level.fc <- 0.1 * slope + noise.level.obs <- 0.1 + + amt.data.pts <- amt.dataset*amt.sdate*amt.ftime*amt.lon*amt.lat + + obs1 <- array(data = rnorm(amt.data.pts), + dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), + dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) + + tmp.noise.obs <- array(data = rnorm(amt.data.pts, 0, noise.level.obs), + dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), + dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) + + mod1 <- array(data = rnorm(amt.data.pts*amt.member, 0, noise.level.fc), + dim = c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), + dimnames = list(dataset = lst.dataset, member = lst.member, sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) + + + for(i.mbr in seq(1, amt.member)){ + mod1[ , i.mbr, , , , ] <- (obs1[ , 1, , , , ] + mod1[ , i.mbr, , , , ] + bias) / slope + } + obs1 <- obs1 + tmp.noise.obs + + lon <- seq(0, 30, amt.lon) + lat <- seq(0, 25, amt.lat) + exp <- list(data = mod1, lat = lat, lon = lon) + obs <- list(data = obs1, lat = lat, lon = lon) + attr(exp, 'class') <- 's2dv_cube' + attr(obs, 'class') <- 's2dv_cube' + + bc <- CST_EnsBiasCorrection(exp = exp, obs = obs) + + expect_equal(length(bc), 3) + expect_equal(as.numeric(dim(bc$data)), + as.numeric(c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, + lon = amt.lon, lat = amt.lat))) + expect_equal(bc$lat, lat) + expect_equal(bc$lon, lon) + + expect_error(CST_EnsBiasCorrection(exp = exp, obs = exp), + paste0("The length of the dimension 'member' in the component 'data' ", + "of the parameter 'obs' must be equal to 1.")) + + exp2 <- exp + exp2$data[1, 2, 1, 1, 1, 1] <- NA + expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs), + "Parameter 'exp' contains NA values.") + + obs2 <- obs + obs2$data[1, 1, 2, 1, 1, 1] <- NA + expect_warning(CST_EnsBiasCorrection(exp = exp, obs = obs2), + "Parameter 'obs' contains NA values.") + + expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs2), + "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") +}) -- GitLab From 5c513d2888cf20af25f0fd21a75adccffe945300 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 10 May 2019 15:01:23 +0200 Subject: [PATCH 12/46] Add new file --- tests/testthat/test-CST_BiasCorrection.R | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/testthat/test-CST_BiasCorrection.R diff --git a/tests/testthat/test-CST_BiasCorrection.R b/tests/testthat/test-CST_BiasCorrection.R new file mode 100644 index 00000000..c5525c57 --- /dev/null +++ b/tests/testthat/test-CST_BiasCorrection.R @@ -0,0 +1,45 @@ +context("Generic tests") +test_that("Sanity checks", { + expect_error( + CST_BiasCorrection(exp = 1), + paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", + "as output by CSTools::CST_Load.")) + + mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) + obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) + dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, + lat = 6, lon = 7) + dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, + lat = 6, lon = 7) + lon <- seq(0, 30, 5) + lat <- seq(0, 25, 5) + exp <- list(data = mod1, lat = lat, lon = lon) + obs <- list(data = obs1, lat = lat, lon = lon) + attr(exp, 'class') <- 's2dv_cube' + attr(obs, 'class') <- 's2dv_cube' + + bc <- CST_BiasCorrection(exp = exp, obs = obs) + expect_equal(length(bc), 3) + expect_equal(dim(bc$data), + c(dataset = 1, member = 3, sdate = 4, ftime = 5, + lat = 6, lon = 7)) + expect_equal(bc$lat, lat) + expect_equal(bc$lon, lon) + + expect_error(CST_BiasCorrection(exp = exp, obs = exp), + paste0("The length of the dimension 'member' in the component 'data' ", + "of the parameter 'obs' must be equal to 1.")) + + exp2 <- exp + exp2$data[1, 2, 1, 1, 1, 1] <- NA + expect_warning(CST_BiasCorrection(exp = exp2, obs = obs), + "Parameter 'exp' contains NA values.") + + obs2 <- obs + obs2$data[1, 1, 2, 1, 1, 1] <- NA + expect_warning(CST_BiasCorrection(exp = exp, obs = obs2), + "Parameter 'obs' contains NA values.") + + expect_warning(CST_BiasCorrection(exp = exp2, obs = obs2), + "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") +}) -- GitLab From ec122bdb62853bd247337fe8cf44dfb6fcff34f4 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 10 May 2019 15:01:44 +0200 Subject: [PATCH 13/46] Delete test_CST_BiasCorrection.R --- tests/testthat/test_CST_BiasCorrection.R | 45 ----------------------- 1 file changed, 45 deletions(-) delete mode 100644 tests/testthat/test_CST_BiasCorrection.R diff --git a/tests/testthat/test_CST_BiasCorrection.R b/tests/testthat/test_CST_BiasCorrection.R deleted file mode 100644 index c5525c57..00000000 --- a/tests/testthat/test_CST_BiasCorrection.R +++ /dev/null @@ -1,45 +0,0 @@ -context("Generic tests") -test_that("Sanity checks", { - expect_error( - CST_BiasCorrection(exp = 1), - paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", - "as output by CSTools::CST_Load.")) - - mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) - obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) - dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, - lat = 6, lon = 7) - dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, - lat = 6, lon = 7) - lon <- seq(0, 30, 5) - lat <- seq(0, 25, 5) - exp <- list(data = mod1, lat = lat, lon = lon) - obs <- list(data = obs1, lat = lat, lon = lon) - attr(exp, 'class') <- 's2dv_cube' - attr(obs, 'class') <- 's2dv_cube' - - bc <- CST_BiasCorrection(exp = exp, obs = obs) - expect_equal(length(bc), 3) - expect_equal(dim(bc$data), - c(dataset = 1, member = 3, sdate = 4, ftime = 5, - lat = 6, lon = 7)) - expect_equal(bc$lat, lat) - expect_equal(bc$lon, lon) - - expect_error(CST_BiasCorrection(exp = exp, obs = exp), - paste0("The length of the dimension 'member' in the component 'data' ", - "of the parameter 'obs' must be equal to 1.")) - - exp2 <- exp - exp2$data[1, 2, 1, 1, 1, 1] <- NA - expect_warning(CST_BiasCorrection(exp = exp2, obs = obs), - "Parameter 'exp' contains NA values.") - - obs2 <- obs - obs2$data[1, 1, 2, 1, 1, 1] <- NA - expect_warning(CST_BiasCorrection(exp = exp, obs = obs2), - "Parameter 'obs' contains NA values.") - - expect_warning(CST_BiasCorrection(exp = exp2, obs = obs2), - "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") -}) -- GitLab From 5a691f3c5587c6c06db33389026e33674c584a57 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Fri, 10 May 2019 15:02:03 +0200 Subject: [PATCH 14/46] Delete test_CST_EnsBiasCorrection.R --- tests/testthat/test_CST_EnsBiasCorrection.R | 81 --------------------- 1 file changed, 81 deletions(-) delete mode 100644 tests/testthat/test_CST_EnsBiasCorrection.R diff --git a/tests/testthat/test_CST_EnsBiasCorrection.R b/tests/testthat/test_CST_EnsBiasCorrection.R deleted file mode 100644 index 9da3822a..00000000 --- a/tests/testthat/test_CST_EnsBiasCorrection.R +++ /dev/null @@ -1,81 +0,0 @@ -library(abind) -context("Generic tests") -test_that("Sanity checks", { - expect_error( - CST_EnsBiasCorrection(exp = 1), - paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", - "as output by CSTools::CST_Load.")) - - - amt.dataset <- 2 - lst.dataset <- paste0("dataset_",seq(1,amt.dataset)) - amt.member <- 5 - lst.member <- paste0("mbr_",seq(1,amt.member)) - amt.sdate <- 30 - lst.sdate <- paste0(1980+seq(1,amt.sdate),"-01-01") - amt.ftime <- 2 - lst.ftime <- paste0("month_",seq(1,amt.ftime)) - amt.lon <- 2 - lst.lon <- paste0("lon_",seq(1,amt.lon)) - amt.lat <- 3 - lst.lat <- paste0("lat_",seq(1,amt.lat)) - - - bias <- 1. #define some parameters that distinguish observations from forecast& - slope <- 2. - noise.level.fc <- 0.1 * slope - noise.level.obs <- 0.1 - - amt.data.pts <- amt.dataset*amt.sdate*amt.ftime*amt.lon*amt.lat - - obs1 <- array(data = rnorm(amt.data.pts), - dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), - dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) - - tmp.noise.obs <- array(data = rnorm(amt.data.pts, 0, noise.level.obs), - dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), - dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) - - mod1 <- array(data = rnorm(amt.data.pts*amt.member, 0, noise.level.fc), - dim = c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), - dimnames = list(dataset = lst.dataset, member = lst.member, sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) - - - for(i.mbr in seq(1, amt.member)){ - mod1[ , i.mbr, , , , ] <- (obs1[ , 1, , , , ] + mod1[ , i.mbr, , , , ] + bias) / slope - } - obs1 <- obs1 + tmp.noise.obs - - lon <- seq(0, 30, amt.lon) - lat <- seq(0, 25, amt.lat) - exp <- list(data = mod1, lat = lat, lon = lon) - obs <- list(data = obs1, lat = lat, lon = lon) - attr(exp, 'class') <- 's2dv_cube' - attr(obs, 'class') <- 's2dv_cube' - - bc <- CST_EnsBiasCorrection(exp = exp, obs = obs) - - expect_equal(length(bc), 3) - expect_equal(as.numeric(dim(bc$data)), - as.numeric(c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, - lon = amt.lon, lat = amt.lat))) - expect_equal(bc$lat, lat) - expect_equal(bc$lon, lon) - - expect_error(CST_EnsBiasCorrection(exp = exp, obs = exp), - paste0("The length of the dimension 'member' in the component 'data' ", - "of the parameter 'obs' must be equal to 1.")) - - exp2 <- exp - exp2$data[1, 2, 1, 1, 1, 1] <- NA - expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs), - "Parameter 'exp' contains NA values.") - - obs2 <- obs - obs2$data[1, 1, 2, 1, 1, 1] <- NA - expect_warning(CST_EnsBiasCorrection(exp = exp, obs = obs2), - "Parameter 'obs' contains NA values.") - - expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs2), - "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") -}) -- GitLab From 66ba9df2e4f6d9e165f91e21d2b2f2af2ff7e8d5 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Mon, 13 May 2019 14:44:52 +0200 Subject: [PATCH 15/46] filename change of tests --- .../{test-CST_BiasCorrection.R => test-CST_BiasCorrection.R} | 0 ...test-CST_EnsBiasCorrection.R => test-CST_EnsBiasCorrection.R} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/testthat/{test-CST_BiasCorrection.R => test-CST_BiasCorrection.R} (100%) rename tests/testthat/{test-CST_EnsBiasCorrection.R => test-CST_EnsBiasCorrection.R} (100%) diff --git a/tests/testthat/test-CST_BiasCorrection.R b/tests/testthat/test-CST_BiasCorrection.R similarity index 100% rename from tests/testthat/test-CST_BiasCorrection.R rename to tests/testthat/test-CST_BiasCorrection.R diff --git a/tests/testthat/test-CST_EnsBiasCorrection.R b/tests/testthat/test-CST_EnsBiasCorrection.R similarity index 100% rename from tests/testthat/test-CST_EnsBiasCorrection.R rename to tests/testthat/test-CST_EnsBiasCorrection.R -- GitLab From c7a8ab938fe8f4bba94dfd59de0a4fbb3e10412f Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Mon, 13 May 2019 15:04:12 +0200 Subject: [PATCH 16/46] vanalles --- DESCRIPTION | 5 ++-- NAMESPACE | 1 + R/CST_EnsBiasCorrection.R | 35 +++++++++++++------------- man/CST_Anomaly.Rd | 7 +++--- man/CST_BiasCorrection.Rd | 7 +++--- man/CST_Calibration.Rd | 7 +++--- man/CST_EnsBiasCorrection.Rd | 43 ++++++++++++++++++++++++++++++++ man/CST_Load.Rd | 1 - man/CST_MultiMetric.Rd | 9 +++---- man/CST_MultivarRMSE.Rd | 7 +++--- man/CST_RFSlope.Rd | 1 - man/CST_RFWeights.Rd | 7 +++--- man/CST_RainFARM.Rd | 7 +++--- man/PlotForecastPDF.Rd | 10 ++++---- man/PlotMostLikelyQuantileMap.Rd | 7 +++--- man/RFSlope.Rd | 1 - man/RainFARM.Rd | 8 +++--- man/areave_data.Rd | 1 - man/lonlat_data.Rd | 1 - man/lonlat_prec.Rd | 1 - 20 files changed, 98 insertions(+), 68 deletions(-) mode change 100644 => 100755 DESCRIPTION mode change 100644 => 100755 R/CST_EnsBiasCorrection.R create mode 100644 man/CST_EnsBiasCorrection.Rd diff --git a/DESCRIPTION b/DESCRIPTION old mode 100644 new mode 100755 index 5b527779..d3245289 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -14,7 +14,7 @@ Authors@R: c( person("Deborah", "Verfaillie", , "deborah.verfaillie@bsc.es", role = "aut"), person("Lauriane", "Batte", , "lauriane.batte@meteo.fr", role = "ctb"), person("Jesus", "Peña", , "jesus.pena@bsc.es", role = "ctb"), - person("Bert", "van Schaeybroeck", , "bertvs@meteo.be", role = "ctb")) + person("Bert", "van Schaeybroeck", , "bertvs@meteo.be", role = "aut")) Description: Exploits dynamical seasonal forecasts in order to provide information relevant to stakeholders at the seasonal timescale. The package contains process-based methods for forecast calibration, bias correction, @@ -27,6 +27,7 @@ Description: Exploits dynamical seasonal forecasts in order to provide Terzago et al. (2018) . Torralba et al. (2017) . D'Onofrio et al. (2014) . + Van Schaeybroeck et al. (2015) . Depends: R (>= 3.2.0), maps @@ -53,4 +54,4 @@ VignetteBuilder: knitr License: Apache License 2.0 Encoding: UTF-8 LazyData: true -RoxygenNote: 5.0.0 +RoxygenNote: 6.1.1 diff --git a/NAMESPACE b/NAMESPACE index dae37853..364a3652 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,7 @@ export(CST_Anomaly) export(CST_BiasCorrection) export(CST_Calibration) +export(CST_EnsBiasCorrection) export(CST_Load) export(CST_MultiMetric) export(CST_MultivarRMSE) diff --git a/R/CST_EnsBiasCorrection.R b/R/CST_EnsBiasCorrection.R old mode 100644 new mode 100755 index c4f966b8..6addb205 --- a/R/CST_EnsBiasCorrection.R +++ b/R/CST_EnsBiasCorrection.R @@ -12,11 +12,13 @@ #'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. #' #'@import s2dverification +#'@import abind #'@examples #' #'# Example #'# Creation of sample s2dverification objects. These are not complete #'# s2dverification objects though. The Load function returns complete objects. +#'library(abind) #'mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) #'dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) #'obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) @@ -30,8 +32,22 @@ #'a <- CST_EnsBiasCorrection(exp = exp, obs = obs) #'str(a) #'@export +CST_EnsBiasCorrection <- function(exp, obs) { + if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { + stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", + "as output by CSTools::CST_Load.") + } + + if (dim(obs$data)["member"] != 1) { + stop("The length of the dimension 'member' in the component 'data' ", + "of the parameter 'obs' must be equal to 1.") + } -library(abind) + exp$data <- EnsBiasCorrection(fc = exp$data, obs = obs$data) + exp$Datasets <- c(exp$Datasets, obs$Datasets) + exp$source_files <- c(exp$source_files, obs$source_files) + return(exp) +} spr <- function(x, amt.spr, dim = 1) { if(is.vector(x)){ @@ -132,23 +148,6 @@ correct.mbm.fc <- function(fc, par){ return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) } -CST_EnsBiasCorrection <- function(exp, obs) { - if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { - stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", - "as output by CSTools::CST_Load.") - } - - if (dim(obs$data)["member"] != 1) { - stop("The length of the dimension 'member' in the component 'data' ", - "of the parameter 'obs' must be equal to 1.") - } - - exp$data <- EnsBiasCorrection(fc = exp$data, obs = obs$data) - exp$Datasets <- c(exp$Datasets, obs$Datasets) - exp$source_files <- c(exp$source_files, obs$source_files) - return(exp) -} - EnsBiasCorrection <- function (fc, obs) { target.dims <- c("member", "sdate") diff --git a/man/CST_Anomaly.Rd b/man/CST_Anomaly.Rd index e1c31f0c..b214a31a 100644 --- a/man/CST_Anomaly.Rd +++ b/man/CST_Anomaly.Rd @@ -53,13 +53,12 @@ str(anom3) anom4 <- CST_Anomaly(exp = exp, obs = obs, cross = FALSE, memb = FALSE) str(anom4) +} +\seealso{ +\code{\link[s2dverification]{Ano_CrossValid}}, \code{\link[s2dverification]{Clim}} and \code{\link{CST_Load}} } \author{ Perez-Zanon Nuria, \email{nuria.perez@bsc.es} Pena Jesus, \email{jesus.pena@bsc.es} } -\seealso{ -\code{\link[s2dverification]{Ano_CrossValid}}, \code{\link[s2dverification]{Clim}} and \code{\link{CST_Load}} -} - diff --git a/man/CST_BiasCorrection.Rd b/man/CST_BiasCorrection.Rd index 485199ea..a1b415fb 100644 --- a/man/CST_BiasCorrection.Rd +++ b/man/CST_BiasCorrection.Rd @@ -35,10 +35,9 @@ attr(obs, 'class') <- 's2dv_cube' a <- CST_BiasCorrection(exp = exp, obs = obs) str(a) } -\author{ -Verónica Torralba, \email{veronica.torralba@bsc.es} -} \references{ Torralba, V., F.J. Doblas-Reyes, D. MacLeod, I. Christel and M. Davis (2017). Seasonal climate prediction: a new source of information for the management of wind energy resources. Journal of Applied Meteorology and Climatology, 56, 1231-1247, doi:10.1175/JAMC-D-16-0204.1. (CLIM4ENERGY, EUPORIAS, NEWA, RESILIENCE, SPECS) } -\encoding{UTF-8} +\author{ +Verónica Torralba, \email{veronica.torralba@bsc.es} +} diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd index c8419076..58b12617 100644 --- a/man/CST_Calibration.Rd +++ b/man/CST_Calibration.Rd @@ -25,13 +25,12 @@ c(exp, obs) \%<-\% areave_data exp_calibrated <- CST_Calibration(exp = exp, obs = obs) str(exp_calibrated) } -\author{ -Verónica Torralba, \email{veronica.torralba@bsc.es} -} \references{ Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x } \seealso{ \code{\link{CST_Load}} } -\encoding{UTF-8} +\author{ +Verónica Torralba, \email{veronica.torralba@bsc.es} +} diff --git a/man/CST_EnsBiasCorrection.Rd b/man/CST_EnsBiasCorrection.Rd new file mode 100644 index 00000000..0c066bfa --- /dev/null +++ b/man/CST_EnsBiasCorrection.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/CST_EnsBiasCorrection.R +\name{CST_EnsBiasCorrection} +\alias{CST_EnsBiasCorrection} +\title{Ensemble Bias Correction based on adjustment of ensemble mean, and ensemble spread} +\usage{ +CST_EnsBiasCorrection(exp, obs) +} +\arguments{ +\item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}} + +\item{obs}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}.} +} +\value{ +an object of class \code{s2dv_cube} containing the bias corrected forecasts in the element called \code{$data} with the same dimensions of the experimental data. +} +\description{ +This function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +} +\examples{ + +# Example +# Creation of sample s2dverification objects. These are not complete +# s2dverification objects though. The Load function returns complete objects. +mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) +dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) +obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) +dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, lat = 6, lon = 7) +lon <- seq(0, 30, 5) +lat <- seq(0, 25, 5) +exp <- list(data = mod1, lat = lat, lon = lon) +obs <- list(data = obs1, lat = lat, lon = lon) +attr(exp, 'class') <- 's2dv_cube' +attr(obs, 'class') <- 's2dv_cube' +a <- CST_EnsBiasCorrection(exp = exp, obs = obs) +str(a) +} +\references{ +Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. +} +\author{ +Bert Van Schaeybroeck, \email{bertvs@meteo.be} +} diff --git a/man/CST_Load.Rd b/man/CST_Load.Rd index 1fee022c..bf03ba42 100644 --- a/man/CST_Load.Rd +++ b/man/CST_Load.Rd @@ -47,4 +47,3 @@ obs <- CSTools::lonlat_data$obs \author{ Nicolau Manubens, \email{nicolau.manubens@bsc.es} } - diff --git a/man/CST_MultiMetric.Rd b/man/CST_MultiMetric.Rd index 079a5588..8e3ce593 100644 --- a/man/CST_MultiMetric.Rd +++ b/man/CST_MultiMetric.Rd @@ -37,15 +37,14 @@ c(ano_exp, ano_obs) \%<-\% CST_Anomaly(exp = exp, obs = obs, cross = TRUE, memb a <- CST_MultiMetric(exp = ano_exp, obs = ano_obs) str(a) } -\author{ -Mishra Niti, \email{niti.mishra@bsc.es} - -Perez-Zanon Nuria, \email{nuria.perez@bsc.es} -} \references{ Mishra, N., Prodhomme, C., & Guemas, V. (n.d.). Multi-Model Skill Assessment of Seasonal Temperature and Precipitation Forecasts over Europe, 29-31.\url{http://link.springer.com/10.1007/s00382-018-4404-z} } \seealso{ \code{\link[s2dverification]{Corr}}, \code{\link[s2dverification]{RMS}}, \code{\link[s2dverification]{RMSSS}} and \code{\link{CST_Load}} } +\author{ +Mishra Niti, \email{niti.mishra@bsc.es} +Perez-Zanon Nuria, \email{nuria.perez@bsc.es} +} diff --git a/man/CST_MultivarRMSE.Rd b/man/CST_MultivarRMSE.Rd index 685eaf77..24af608c 100644 --- a/man/CST_MultivarRMSE.Rd +++ b/man/CST_MultivarRMSE.Rd @@ -56,10 +56,9 @@ weight <- c(1, 2) a <- CST_MultivarRMSE(exp = ano_exp, obs = ano_obs, weight = weight) str(a) } -\author{ -Deborah Verfaillie, \email{deborah.verfaillie@bsc.es} -} \seealso{ \code{\link[s2dverification]{RMS}} and \code{\link{CST_Load}} } - +\author{ +Deborah Verfaillie, \email{deborah.verfaillie@bsc.es} +} diff --git a/man/CST_RFSlope.Rd b/man/CST_RFSlope.Rd index d2b5aec0..0c4e1671 100644 --- a/man/CST_RFSlope.Rd +++ b/man/CST_RFSlope.Rd @@ -50,4 +50,3 @@ slopes \author{ Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} } - diff --git a/man/CST_RFWeights.Rd b/man/CST_RFWeights.Rd index ffd700bd..ff625b56 100644 --- a/man/CST_RFWeights.Rd +++ b/man/CST_RFWeights.Rd @@ -47,9 +47,6 @@ nf <- 8 ww <- CST_RFWeights("./worldclim.nc", nf, lon, lat, fsmooth = TRUE) } } -\author{ -Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} -} \references{ Terzago, S., Palazzi, E., & von Hardenberg, J. (2018). Stochastic downscaling of precipitation in complex orography: @@ -57,4 +54,6 @@ A simple method to reproduce a realistic fine-scale climatology. Natural Hazards and Earth System Sciences, 18(11), 2825-2840. http://doi.org/10.5194/nhess-18-2825-2018 . } - +\author{ +Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} +} diff --git a/man/CST_RainFARM.Rd b/man/CST_RainFARM.Rd index ca32e2d8..8df8f1f4 100644 --- a/man/CST_RainFARM.Rd +++ b/man/CST_RainFARM.Rd @@ -92,12 +92,11 @@ dim(res$data) # dataset member sdate ftime lat lon realization # 1 2 3 4 64 64 3 } -\author{ -Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} -} \references{ Terzago, S. et al. (2018). NHESS 18(11), 2825-2840. http://doi.org/10.5194/nhess-18-2825-2018 ; D'Onofrio et al. (2014), J of Hydrometeorology 15, 830-843; Rebora et. al. (2006), JHM 7, 724. } - +\author{ +Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} +} diff --git a/man/PlotForecastPDF.Rd b/man/PlotForecastPDF.Rd index bed0bd31..c1959ee8 100644 --- a/man/PlotForecastPDF.Rd +++ b/man/PlotForecastPDF.Rd @@ -4,10 +4,11 @@ \alias{PlotForecastPDF} \title{Plot one or multiple ensemble forecast pdfs for the same event} \usage{ -PlotForecastPDF(fcst, tercile.limits, extreme.limits = NULL, obs = NULL, - plotfile = NULL, title = "Set a title", var.name = "Varname (units)", - fcst.names = NULL, add.ensmemb = c("above", "below", "no"), - color.set = c("ggplot", "s2s4e", "hydro")) +PlotForecastPDF(fcst, tercile.limits, extreme.limits = NULL, + obs = NULL, plotfile = NULL, title = "Set a title", + var.name = "Varname (units)", fcst.names = NULL, + add.ensmemb = c("above", "below", "no"), color.set = c("ggplot", + "s2s4e", "hydro")) } \arguments{ \item{fcst}{a dataframe or array containing all the ensember members for each frecast. If \code{'fcst'} is an array, it should have two labelled dimensions, and one of them should be \code{'members'}. If \code{'fcsts'} is a data.frame, each column shoul be a separate forecast, with the rows beeing the different ensemble members.} @@ -49,4 +50,3 @@ PlotForecastPDF(fcsts2, c(-0.66, 0.66), extreme.limits = c(-1.2, 1.2), \author{ Llorenç Lledó \email{llledo@bsc.es} } -\encoding{UTF-8} diff --git a/man/PlotMostLikelyQuantileMap.Rd b/man/PlotMostLikelyQuantileMap.Rd index 6c92850e..0d984ede 100644 --- a/man/PlotMostLikelyQuantileMap.Rd +++ b/man/PlotMostLikelyQuantileMap.Rd @@ -109,11 +109,10 @@ PlotMostLikelyQuantileMap(bins, lons, lats, mask = 1 - (w1 + w2 / max(c(w1, w2))), brks = 20, width = 10, height = 8) -} -\author{ -Veronica Torralba, \email{veronica.torralba@bsc.es}, Nicolau Manubens, \email{nicolau.manubens@bsc.es} } \seealso{ \code{PlotCombinedMap} and \code{PlotEquiMap} } - +\author{ +Veronica Torralba, \email{veronica.torralba@bsc.es}, Nicolau Manubens, \email{nicolau.manubens@bsc.es} +} diff --git a/man/RFSlope.Rd b/man/RFSlope.Rd index 09a24ff5..5308ef8c 100644 --- a/man/RFSlope.Rd +++ b/man/RFSlope.Rd @@ -60,4 +60,3 @@ slopes \author{ Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} } - diff --git a/man/RainFARM.Rd b/man/RainFARM.Rd index 3ef2a2f6..35e7c2a5 100644 --- a/man/RainFARM.Rd +++ b/man/RainFARM.Rd @@ -4,9 +4,10 @@ \alias{RainFARM} \title{RainFARM stochastic precipitation downscaling (reduced version)} \usage{ -RainFARM(data, lon, lat, nf, weights = 1, nens = 1, slope = 0, kmin = 1, - fglob = FALSE, fsmooth = TRUE, time_dim = NULL, lon_dim = "lon", - lat_dim = "lat", drop_realization_dim = FALSE, verbose = FALSE) +RainFARM(data, lon, lat, nf, weights = 1, nens = 1, slope = 0, + kmin = 1, fglob = FALSE, fsmooth = TRUE, time_dim = NULL, + lon_dim = "lon", lat_dim = "lat", drop_realization_dim = FALSE, + verbose = FALSE) } \arguments{ \item{data}{Precipitation array to downscale. @@ -113,4 +114,3 @@ dim(res$data) \author{ Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} } - diff --git a/man/areave_data.Rd b/man/areave_data.Rd index cc79c85c..a772220a 100644 --- a/man/areave_data.Rd +++ b/man/areave_data.Rd @@ -41,4 +41,3 @@ areave_data <- Nicolau Manubens \email{nicolau.manubens@bsc.es} } \keyword{data} - diff --git a/man/lonlat_data.Rd b/man/lonlat_data.Rd index eca7abac..0c6ee30f 100644 --- a/man/lonlat_data.Rd +++ b/man/lonlat_data.Rd @@ -41,4 +41,3 @@ lonlat_data <- Nicolau Manubens \email{nicolau.manubens@bsc.es} } \keyword{data} - diff --git a/man/lonlat_prec.Rd b/man/lonlat_prec.Rd index 69cb94e8..345e3cab 100644 --- a/man/lonlat_prec.Rd +++ b/man/lonlat_prec.Rd @@ -29,4 +29,3 @@ lonlat_prec <- CST_Load('prlr', exp = list(infile), obs = NULL, Jost von Hardenberg \email{j.vonhardenberg@isac.cnr.it} } \keyword{data} - -- GitLab From e3a3c2e3aaa20fd409fa0c6a14ece333784980ef Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Mon, 13 May 2019 15:27:18 +0200 Subject: [PATCH 17/46] documentation updated --- NAMESPACE | 1 + man/CST_EnsBiasCorrection.Rd | 1 + 2 files changed, 2 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index 364a3652..0911ef37 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,6 +14,7 @@ export(PlotForecastPDF) export(PlotMostLikelyQuantileMap) export(RFSlope) export(RainFARM) +import(abind) import(ggplot2) import(multiApply) import(ncdf4) diff --git a/man/CST_EnsBiasCorrection.Rd b/man/CST_EnsBiasCorrection.Rd index 0c066bfa..2239eb0c 100644 --- a/man/CST_EnsBiasCorrection.Rd +++ b/man/CST_EnsBiasCorrection.Rd @@ -22,6 +22,7 @@ This function applies a member-by-member ensemble bias correction described in V # Example # Creation of sample s2dverification objects. These are not complete # s2dverification objects though. The Load function returns complete objects. +library(abind) mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) -- GitLab From 62eb437b8858cdf811ecdf11f1fb3aaa30fe90d4 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Tue, 14 May 2019 12:19:40 +0200 Subject: [PATCH 18/46] example changed --- R/CST_EnsBiasCorrection.R | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/R/CST_EnsBiasCorrection.R b/R/CST_EnsBiasCorrection.R index 6addb205..eea2384a 100755 --- a/R/CST_EnsBiasCorrection.R +++ b/R/CST_EnsBiasCorrection.R @@ -71,9 +71,9 @@ calc.crps.opt <- function(par, quant.obs.fc){ with(quant.obs.fc, mean( apply(abs(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + - ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev)), c(2), mean, na.rm = T) - + ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev)), c(2), mean, na.rm = TRUE) - abs((par[3])^2 * spr.abs + par[4]) / 2. - , na.rm = T) + , na.rm = TRUE) ) ) } @@ -81,16 +81,16 @@ calc.crps.opt <- function(par, quant.obs.fc){ calc.crps <- function(obs, fc){ quant.obs.fc <- calc.obs.fc.quant(obs = obs, fc = fc) return(with(quant.obs.fc, - mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = T) - spr.abs / 2., na.rm = T))) + mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = TRUE) - spr.abs / 2., na.rm = TRUE))) } calc.obs.fc.quant <- function(obs, fc){ amt.mbr <- dim(fc)[1] obs.per.ens <- spr(obs, amt.mbr) - fc.ens.av <- apply(fc, c(2), mean, na.rm = T) + fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) cor.obs.fc <- cor(fc.ens.av, obs, use = "complete.obs") - obs.av <- mean(obs, na.rm = T) - obs.sd <- sd(obs, na.rm = T) + obs.av <- mean(obs, na.rm = TRUE) + obs.sd <- sd(obs, na.rm = TRUE) return( append( calc.fc.quant(fc = fc), @@ -107,18 +107,18 @@ calc.obs.fc.quant <- function(obs, fc){ calc.fc.quant <- function(fc){ amt.mbr <- dim(fc)[1] - fc.ens.av <- apply(fc, c(2), mean, na.rm = T) - fc.ens.av.av <- mean(fc.ens.av, na.rm = T) + fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) + fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) fc.ens.av.per.ens <- spr(fc.ens.av, amt.mbr) - fc.ens.sd <- apply(fc, c(2), sd, na.rm = T) - fc.ens.sd.av <- mean(fc.ens.sd,na.rm = T) + fc.ens.sd <- apply(fc, c(2), sd, na.rm = TRUE) + fc.ens.sd.av <- mean(fc.ens.sd,na.rm = TRUE) fc.dev <- fc - fc.ens.av.per.ens repmat1.tmp <- spr(fc, amt.mbr) repmat2.tmp <- aperm(repmat1.tmp, c(2, 1, 3)) - spr.abs <- apply(abs(repmat1.tmp - repmat2.tmp), c(3), mean, na.rm = T) + spr.abs <- apply(abs(repmat1.tmp - repmat2.tmp), c(3), mean, na.rm = TRUE) spr.abs.per.ens <- spr(spr.abs, amt.mbr) - fc.av <- mean(fc, na.rm = T) - fc.sd <- sd(fc, na.rm = T) + fc.av <- mean(fc, na.rm = TRUE) + fc.sd <- sd(fc, na.rm = TRUE) return( list( fc.ens.av = fc.ens.av, @@ -137,9 +137,9 @@ calc.fc.quant <- function(fc){ calc.wer.cr.par <- function(quant.obs.fc){ par.out <- rep(NA, 3) - par.out[3] <- with(quant.obs.fc, sqrt(obs.sd * sqrt(1 - cor.obs.fc^2) / mean(fc.ens.sd.av, na.rm = T))) + par.out[3] <- with(quant.obs.fc, sqrt(obs.sd * sqrt(1 - cor.obs.fc^2) / mean(fc.ens.sd.av, na.rm = TRUE))) par.out[2] <- with(quant.obs.fc, cor.obs.fc * obs.sd / fc.sd) - par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = T) + par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = TRUE) return(par.out) } @@ -218,9 +218,9 @@ combine.obs.fc <- function(obs,fc){ for (i.fc in seq(1, amt.sdate)) { # defining training (tr) and evaluation (ev) subsets - fc.ev <- var.fc[ , i.fc, drop = F] + fc.ev <- var.fc[ , i.fc, drop = FALSE] fc.tr <- var.fc[ , -i.fc] - obs.tr <- var.obs[-i.fc, drop = F] + obs.tr <- var.obs[-i.fc, drop = FALSE] #calculate ensemble and observational characteristics quant.obs.fc.tr <- calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) -- GitLab From e58463fa5d06ab8c6f749158aa84328c371041db Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 16 May 2019 09:34:13 +0200 Subject: [PATCH 19/46] manual added --- man/manual.pdf | Bin 0 -> 147881 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 man/manual.pdf diff --git a/man/manual.pdf b/man/manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4f5512918fead76fa41a65f8aed983f1f9a74e1c GIT binary patch literal 147881 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�Rf-kuew)XF~%LU(Kq~1elJ< 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%0A%HOY3bO2Aw|> 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
#b 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 z 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 UT?@<1#=*hH1WQgXt{?&XZ>7I!$^ZZW literal 0 HcmV?d00001 -- GitLab From 0f07ac09c3c96c1bc4dc0c26865ed27d82e8b1f6 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 16 May 2019 11:31:10 +0200 Subject: [PATCH 20/46] abind removed from examples --- R/CST_EnsBiasCorrection.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/CST_EnsBiasCorrection.R b/R/CST_EnsBiasCorrection.R index eea2384a..61d50ba8 100755 --- a/R/CST_EnsBiasCorrection.R +++ b/R/CST_EnsBiasCorrection.R @@ -18,7 +18,6 @@ #'# Example #'# Creation of sample s2dverification objects. These are not complete #'# s2dverification objects though. The Load function returns complete objects. -#'library(abind) #'mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) #'dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) #'obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) -- GitLab From 266bbb28aff848362c89dad9d0b81eef18e45337 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 16 May 2019 11:32:36 +0200 Subject: [PATCH 21/46] abind removed from examples --- man/CST_EnsBiasCorrection.Rd | 1 - 1 file changed, 1 deletion(-) diff --git a/man/CST_EnsBiasCorrection.Rd b/man/CST_EnsBiasCorrection.Rd index 2239eb0c..0c066bfa 100644 --- a/man/CST_EnsBiasCorrection.Rd +++ b/man/CST_EnsBiasCorrection.Rd @@ -22,7 +22,6 @@ This function applies a member-by-member ensemble bias correction described in V # Example # Creation of sample s2dverification objects. These are not complete # s2dverification objects though. The Load function returns complete objects. -library(abind) mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) -- GitLab From ca79309580798d874c58ac90f1d129de809d5b08 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 16 May 2019 12:05:42 +0200 Subject: [PATCH 22/46] PlotPdf test removed --- tests/testthat/test-PlotForecastPDF.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-PlotForecastPDF.R b/tests/testthat/test-PlotForecastPDF.R index 95c1e0be..e75a46e5 100644 --- a/tests/testthat/test-PlotForecastPDF.R +++ b/tests/testthat/test-PlotForecastPDF.R @@ -13,7 +13,7 @@ test_that("some checks", { expect_equal( length(PlotForecastPDF(fcsts2, c(-0.66, 0.66), extreme.limits = c(-1.2, 1.2), fcst.names = paste0("random fcst ", 1 : 5), obs = 0.7)[[5]]), - 61) + 59) }) test_that("Sanity checks", { -- GitLab From f734d6a06dfb73b0beff05575f048d4983a261ef Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 9 Sep 2019 08:39:55 +0200 Subject: [PATCH 23/46] Delete test-PlotForecastPDF.R --- tests/testthat/test-PlotForecastPDF.R | 40 --------------------------- 1 file changed, 40 deletions(-) delete mode 100644 tests/testthat/test-PlotForecastPDF.R diff --git a/tests/testthat/test-PlotForecastPDF.R b/tests/testthat/test-PlotForecastPDF.R deleted file mode 100644 index e75a46e5..00000000 --- a/tests/testthat/test-PlotForecastPDF.R +++ /dev/null @@ -1,40 +0,0 @@ -context("Generic tests") -test_that("some checks", { - fcsts2 <- array(1:100, dim = c(members = 20, fcst = 5)) - expect_equal( - length(PlotForecastPDF(fcsts2, c(-0.66, 0.66), extreme.limits = c(-1.2, 1.2), - fcst.names = paste0("random fcst ", 1 : 5), obs = 0.7)), - 10) - expect_equal( - names(PlotForecastPDF(fcsts2, c(-0.66, 0.66), extreme.limits = c(-1.2, 1.2), - fcst.names = paste0("random fcst ", 1 : 5), obs = 0.7)), - c("data", "layers", "scales", "mapping", "theme", "coordinates", "facet", - "plot_env", "labels", "guides")) - expect_equal( - length(PlotForecastPDF(fcsts2, c(-0.66, 0.66), extreme.limits = c(-1.2, 1.2), - fcst.names = paste0("random fcst ", 1 : 5), obs = 0.7)[[5]]), - 59) -}) - -test_that("Sanity checks", { - expect_error( - PlotForecastPDF(fcst, tercile.limits), - "object 'tercile.limits' not found") - expect_error( - PlotForecastPDF(fcst, tercile.limits = c(0.25, 0.55)), - "object 'fcst' not found") - expect_error( - PlotForecastPDF(fcst, tercile.limits = 10), - "Parameter 'tercile.limits' should be an array with two limits for delimiting tercile categories") - expect_error( - PlotForecastPDF(fcst, tercile.limits = c(10, 20)), - "object 'fcst' not found") - fcsts2 <- array(rnorm(100),dim = c(members = 20, fcst = 5)) - expect_error( - PlotForecastPDF(fcst = fcsts2, tercile.limits), - "object 'tercile.limits' not found") - expect_error( - PlotForecastPDF(fcst = fcsts2, tercile.limits = c(-0.5, 0.5), extreme.limits = NA), - "Parameter 'extreme.limits' should be an array with two limits for delimiting extreme categories") -}) - -- GitLab From 1d459094c41adc6cbe465de5728ec133ea011a42 Mon Sep 17 00:00:00 2001 From: Bert Van Schaeybroeck Date: Mon, 9 Sep 2019 08:46:59 +0200 Subject: [PATCH 24/46] Add new file --- tests/testthat/test-PlotForecastPDF.R | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/testthat/test-PlotForecastPDF.R diff --git a/tests/testthat/test-PlotForecastPDF.R b/tests/testthat/test-PlotForecastPDF.R new file mode 100644 index 00000000..dfc35c4d --- /dev/null +++ b/tests/testthat/test-PlotForecastPDF.R @@ -0,0 +1,22 @@ +context("Generic tests") +test_that("Sanity checks", { + expect_error( + PlotForecastPDF(fcst, tercile.limits), + "object 'tercile.limits' not found") + expect_error( + PlotForecastPDF(fcst, tercile.limits = c(0.25, 0.55)), + "object 'fcst' not found") + expect_error( + PlotForecastPDF(fcst, tercile.limits = 10), + "Parameter 'tercile.limits' should be an array with two limits for delimiting tercile categories") + expect_error( + PlotForecastPDF(fcst, tercile.limits = c(10, 20)), + "object 'fcst' not found") + fcsts2 <- array(rnorm(100),dim = c(members = 20, fcst = 5)) + expect_error( + PlotForecastPDF(fcst = fcsts2, tercile.limits), + "object 'tercile.limits' not found") + expect_error( + PlotForecastPDF(fcst = fcsts2, tercile.limits = c(-0.5, 0.5), extreme.limits = NA), + "Parameter 'extreme.limits' should be an array with two limits for delimiting extreme categories") +}) -- GitLab From 0cb18a6a009d49e5d638efffceb867a27d02c6cb Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Tue, 10 Sep 2019 15:46:25 +0200 Subject: [PATCH 25/46] Calibration functions merged --- R/CST_Calibration.R | 443 ++++++++++++++++++++++++++++++-------- R/CST_EnsBiasCorrection.R | 245 --------------------- 2 files changed, 355 insertions(+), 333 deletions(-) delete mode 100755 R/CST_EnsBiasCorrection.R diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index 5115f1e8..feb4f7cb 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -1,55 +1,124 @@ -#'Forecast Calibration based on the ensemble inflation -#' -#'@author Verónica Torralba, \email{veronica.torralba@bsc.es} -#'@description This function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. -#' -#'@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x -#' -#'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. -#'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. -#' -#'@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. -#' -#'@import s2dverification -#'@import multiApply -#' -#'@seealso \code{\link{CST_Load}} -#' -#'@examples -#'# Example -#'# Load data using CST_Load or use the sample data provided: -#'library(zeallot) -#'c(exp, obs) %<-% areave_data -#'exp_calibrated <- CST_Calibration(exp = exp, obs = obs) -#'str(exp_calibrated) -#'@export -CST_Calibration <- function(exp, obs) { - if (!inherits(exp, 's2dv_cube') || !inherits(exp, 's2dv_cube')) { +#"Forecast Calibration based on the ensemble inflation +#" +#"@author Verónica Torralba, \email{veronica.torralba@bsc.es} +#"@description This function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +#" +#"@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x +#" +#"@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. +#"@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. +#"@param cal.method is the calibration method used, can be either "bias", "cal" or "mbm_cal". Default value is "bias". +#"@param eval.method is the sampling method used, can be either "in-sample" or "take-one-out". Default value is "take-one-out". +#"@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. +#" +#"@import s2dverification +#"@import multiApply +#" +#"@seealso \code{\link{CST_Load}} +#" +#"@examples +#"# Example +#"# Load data using CST_Load or use the sample data provided: +#"library(zeallot) +#"c(exp, obs) %<-% areave_data +#"exp_calibrated <- CST_Calibration(exp = exp, obs = obs) +#"str(exp_calibrated) +#"@export + + +CST_Calibration <- function(exp, obs, ...) { + if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") } - if (dim(obs$data)['member'] != 1) { + if (dim(obs$data)["member"] != 1) { stop("The length of the dimension 'member' in the component 'data' ", "of the parameter 'obs' must be equal to 1.") } - dimnames <- names(dim(exp$data)) - Calibrated <- Calibration(exp = exp$data, obs = obs$data) - pos <- match(dimnames, names(dim(Calibrated))) - Calibrated <- aperm(Calibrated, pos) - names(dim(Calibrated)) <- dimnames - exp$data <- Calibrated + + exp$data <- .calibration(exp = exp$data, obs = obs$data, ...) exp$Datasets <- c(exp$Datasets, obs$Datasets) exp$source_files <- c(exp$source_files, obs$source_files) return(exp) + } -Calibration <- function(exp, obs) { - if (!all(c('member', 'sdate') %in% names(dim(exp)))) { +make.eval.train.dexeses <- function(eval.method, amt.points){ + if(amt.points < 10 & eval.method != "in-sample"){ + cat("Too few points, so sample method will necessarily be in-sample") + eval.method <- "in-sample" + } + if(eval.method == "take-one-out"){ + dexes.lst <- lapply(seq(1, amt.points), function(x) return(list(eval.dexes = x, train.dexes = seq(1, amt.points)[-x]))) + } else if (eval.method == "in-sample"){ + dexes.lst <- list(list(eval.dexes = seq(1, amt.points), train.dexes = seq(1, amt.points))) + } else { + stop(paste0("unknown sampling method: ",eval.method)) + } + return(dexes.lst) +} + +.cal <- function(obs.fc, cal.method = "bias", eval.method = "take-one-out", ...) { + dims.tmp=dim(obs.fc) + amt.mbr <- dims.tmp["member"][] - 1 + amt.sdate <- dims.tmp["sdate"][] + pos <- match(c("member","sdate"), names(dims.tmp)) + obs.fc <- aperm(obs.fc, pos) + var.obs <- asub(obs.fc, list(1),1) + var.fc <- asub(obs.fc, list(1+seq(1, amt.mbr)),1) + dims.fc <- dim(var.fc) + var.cor.fc <- NA * var.fc + + eval.train.dexeses <- make.eval.train.dexeses(eval.method, amt.points = amt.sdate) + amt.resamples <- length(eval.train.dexeses) + for (i.sample in seq(1, amt.resamples)) { + # defining training (tr) and evaluation (ev) subsets + eval.dexes <- eval.train.dexeses[[i.sample]]$eval.dexes + train.dexes <- eval.train.dexeses[[i.sample]]$train.dexes + + fc.ev <- var.fc[ , eval.dexes, drop = FALSE] + fc.tr <- var.fc[ , train.dexes] + obs.tr <- var.obs[train.dexes , drop = FALSE] + + #calculate ensemble and observational characteristics + if(cal.method == "bias"){ + var.cor.fc[ , eval.dexes] <- fc.ev + mean(obs.tr, na.rm = TRUE) - mean(fc.tr, na.rm = TRUE) + } else if (cal.method == "cal"){ + quant.obs.fc.tr <- calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) + #calculate value for regression parameters + init.par <- c(calc.cal.par(quant.obs.fc.tr), 0.) + #correct evaluation subset + var.cor.fc[ , eval.dexes] <- correct.cal.fc(fc.ev , init.par) + } else if (cal.method == "mbm_cal"){ + quant.obs.fc.tr <- calc.obs.fc.quant.ext(obs = obs.tr, fc = fc.tr) + #calculate initial value for regression parameters + init.par <- c(calc.cal.par(quant.obs.fc.tr), 0.001) + init.par[3] <- sqrt(init.par[3]) + #calculate regression parameters on training dataset + optim.tmp <- optim(par = init.par, fn = calc.crps.opt, + gr = calc.crps.grad.opt, + quant.obs.fc = quant.obs.fc.tr) #, method = "CG" + mbm.par <- optim.tmp$par + #correct evaluation subset + var.cor.fc[ , eval.dexes] <- correct.mbm.fc(fc.ev , mbm.par) + } else { + stop("unknown calibration method: ",cal.method) + } + } + names(dim(var.cor.fc)) <- c("member", "sdate") + return(var.cor.fc) +} + + + +.calibration <- function(exp, obs, ...) { + target.dims <- c("member", "sdate") + if (!all(target.dims %in% names(dim(exp)))) { stop("Parameter 'exp' must have the dimensions 'member' and 'sdate'.") } - if (!all(c('sdate') %in% names(dim(obs)))) { + if (!all(c("sdate") %in% names(dim(obs)))) { stop("Parameter 'obs' must have the dimension 'sdate'.") } @@ -61,64 +130,262 @@ Calibration <- function(exp, obs) { warning("Parameter 'obs' contains NA values.") } - target_dims_obs <- 'sdate' - if ('member' %in% names(dim(obs))) { - target_dims_obs <- c('member', target_dims_obs) + target_dims_obs <- "sdate" + if ("member" %in% names(dim(obs))) { + target_dims_obs <- c("member", target_dims_obs) } + + amt.member=dim(exp)["member"] + amt.sdate=dim(exp)["sdate"] + target.dims <- c("member", "sdate") - Calibrated <- Apply(data = list(var_obs = obs, var_exp = exp), - target_dims = list(target_dims_obs, c('member', 'sdate')), - fun = .cal)$output1 - - return(Calibrated) + return.feat <- list(dim = c(amt.member, amt.sdate)) + return.feat$name <- c("member", "sdate") + return.feat$dim.name <- list(dimnames(exp)[["member"]],dimnames(exp)[["sdate"]]) + + ptm <- proc.time() + calibrated <- .apply.obs.fc(obs = obs, + fc = exp, + target.dims = target.dims, + return.feat = return.feat, + FUN = .cal, ...) + + return(calibrated) } -.cal <- function(var_obs, var_exp) { - ntime <- dim(var_exp)[which(names(dim(var_exp)) == 'sdate')][] - nmembers <- dim(var_exp)[which(names(dim(var_exp)) == 'member')][] - if (all(names(dim(var_exp)) != c('member','sdate'))) { - var_exp <- t(var_exp) + +.apply.obs.fc <- function(obs, fc, target.dims, FUN, return.feat, ...){ + dimnames.tmp <- dimnames(fc) + fc.dims.tmp <- dim(fc) + dims.out.tmp <- return.feat$dim + + obs.fc <- .combine.obs.fc(obs, fc) + names.dim <- names(dim(obs.fc)) + amt.dims <- length(names.dim) + margin.all <- seq(1, amt.dims) + matched.dims <- match(target.dims, names.dim) + margin.to.use <- margin.all[-matched.dims] + arr.out <- apply(X = obs.fc, + MARGIN = margin.to.use, + FUN = FUN, + ...) + dims.tmp <- dim(arr.out) + names.dims.tmp <- names(dim(arr.out)) + if(prod(return.feat$dim) != dims.tmp[1]){ + stop("apply.obs.fc: returned dimensions not as expected: ", prod(return.feat$dim), " and ", dims.tmp[1]) } - climObs <- NA * var_obs - climPred <- NA * var_obs - for (t in 1 : length(var_obs)) - { - climObs[t] <- mean(var_obs[ , -t]) - climPred[t] <- mean(var_exp[ , -t]) + dim(arr.out) <- c(dims.out.tmp, dims.tmp[-1]) + names(dim(arr.out)) <- c(return.feat$name, names.dims.tmp[c(-1)]) + names.dim[matched.dims] <- return.feat$name + pos <- match(names.dim, names(dim(arr.out))) + pos_inv <- match(names(dim(arr.out)), names.dim) + arr.out <- aperm(arr.out, pos) + for (i.item in seq(1,length(return.feat$name))){ + dimnames.tmp[[pos_inv[i.item]]] <- return.feat$dim.name[[i.item]] } - var_obs <- var_obs - climObs - - calibrated <- NA * var_exp - for (t in 1 : ntime) { - # defining forecast,hindcast and observation in cross-validation - fcst <- NA * var_exp[ , t] - hcst <- NA * var_exp[ , -t] - for (i in 1 : nmembers) { - fcst[i] <- var_exp[i, t] - climPred[t] - hcst[i, ] <- var_exp[i, -t]- climPred[t] - } - obs <- var_obs[-t] - #coefficients - em_fcst <- mean(fcst) - em_hcst <- apply(hcst, c(2), mean) - corr <- cor(em_hcst, obs) - sd_obs <- sd(obs) - sd_em_hcst <- sd(em_hcst) - - fcst_diff <- fcst - em_fcst - hcst_diff <- NA * hcst - for (n in 1 : nmembers) { - hcst_diff[n,] <- hcst[n,] - em_hcst - } - sd_hcst_diff <- sd(hcst_diff) - - a <- corr * (sd_obs / sd_em_hcst) - b <- (sd_obs / sd_hcst_diff) * sqrt(1 - (corr ^ 2)) - - calibrated[, t] <- (a * em_fcst) + (b * fcst_diff) + climObs[t] + dimnames(arr.out) <- dimnames.tmp + return(arr.out) +} + + + +.combine.obs.fc <- function(obs,fc){ + names.dim.tmp <- names(dim(obs)) + members.dim <- which(names.dim.tmp == "member") + arr.out <- abind(obs, fc, along = members.dim) + dimnames.tmp <- dimnames(arr.out) + names(dim(arr.out)) <- names.dim.tmp + dimnames(arr.out) <- dimnames.tmp + names(dimnames(arr.out)) <- names.dim.tmp + return(arr.out) +} + + + +calc.obs.fc.quant <- function(obs, fc){ + amt.mbr <- dim(fc)[1] + obs.per.ens <- .spr(obs, amt.mbr) + fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) + cor.obs.fc <- cor(fc.ens.av, obs, use = "complete.obs") + obs.av <- mean(obs, na.rm = TRUE) + obs.sd <- sd(obs, na.rm = TRUE) + return( + append( + calc.fc.quant(fc = fc), + list( + obs.per.ens = obs.per.ens, + cor.obs.fc = cor.obs.fc, + obs.av = obs.av, + obs.sd = obs.sd + ) + ) + ) +} + +calc.obs.fc.quant.ext <- function(obs, fc){ + amt.mbr <- dim(fc)[1] + obs.per.ens <- .spr(obs, amt.mbr) + fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) + cor.obs.fc <- cor(fc.ens.av, obs, use = "complete.obs") + obs.av <- mean(obs, na.rm = TRUE) + obs.sd <- sd(obs, na.rm = TRUE) + return( + append( + calc.fc.quant.ext(fc = fc), + list( + obs.per.ens = obs.per.ens, + cor.obs.fc = cor.obs.fc, + obs.av = obs.av, + obs.sd = obs.sd + ) + ) + ) +} + + +calc.fc.quant <- function(fc){ + amt.mbr <- dim(fc)[1] + fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) + fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) + fc.ens.av.sd <- sd(fc.ens.av, na.rm = TRUE) + fc.ens.av.per.ens <- .spr(fc.ens.av, amt.mbr) + fc.ens.sd <- apply(fc, c(2), sd, na.rm = TRUE) + fc.ens.sd.av <- sqrt(mean(fc.ens.sd^2,na.rm = TRUE)) + fc.dev <- fc - fc.ens.av.per.ens + fc.av <- mean(fc, na.rm = TRUE) + fc.sd <- sd(fc, na.rm = TRUE) + return( + list( + fc.ens.av = fc.ens.av, + fc.ens.av.av = fc.ens.av.av, + fc.ens.av.sd = fc.ens.av.sd, + fc.ens.av.per.ens = fc.ens.av.per.ens, + fc.ens.sd = fc.ens.sd, + fc.ens.sd.av = fc.ens.sd.av, + fc.dev = fc.dev, + fc.av = fc.av, + fc.sd = fc.sd + ) + ) +} + +calc.fc.quant.ext <- function(fc){ + amt.mbr <- dim(fc)[1] + fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) + fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) + fc.ens.av.sd <- sd(fc.ens.av, na.rm = TRUE) + fc.ens.av.per.ens <- .spr(fc.ens.av, amt.mbr) + fc.ens.sd <- apply(fc, c(2), sd, na.rm = TRUE) + fc.ens.sd.av <- sqrt(mean(fc.ens.sd^2, na.rm = TRUE)) + fc.dev <- fc - fc.ens.av.per.ens + repmat1.tmp <- .spr(fc, amt.mbr) + repmat2.tmp <- aperm(repmat1.tmp, c(2, 1, 3)) + spr.abs <- apply(abs(repmat1.tmp - repmat2.tmp), c(3), mean, na.rm = TRUE) + spr.abs.per.ens <- .spr(spr.abs, amt.mbr) + fc.av <- mean(fc, na.rm = TRUE) + fc.sd <- sd(fc, na.rm = TRUE) + return( + list( + fc.ens.av = fc.ens.av, + fc.ens.av.av = fc.ens.av.av, + fc.ens.av.sd = fc.ens.av.sd, + fc.ens.av.per.ens = fc.ens.av.per.ens, + fc.ens.sd = fc.ens.sd, + fc.ens.sd.av = fc.ens.sd.av, + fc.dev = fc.dev, + spr.abs = spr.abs, + spr.abs.per.ens = spr.abs.per.ens, + fc.av = fc.av, + fc.sd = fc.sd + ) + ) +} + + + +calc.cal.par <- function(quant.obs.fc){ + par.out <- rep(NA, 3) + par.out[3] <- with(quant.obs.fc, obs.sd * sqrt(1 - cor.obs.fc^2) / fc.ens.sd.av) + par.out[2] <- with(quant.obs.fc, cor.obs.fc * obs.sd / fc.ens.av.sd) + par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = TRUE) + return(par.out) +} + + + +correct.mbm.fc <- function(fc, par){ + quant.fc.mp <- calc.fc.quant.ext(fc = fc) + return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) +} + +correct.cal.fc <- function(fc, par){ + quant.fc.mp <- calc.fc.quant(fc = fc) + return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * par[3])) +} + + +calc.crps <- function(obs, fc){ + quant.obs.fc <- calc.obs.fc.quant.ext(obs = obs, fc = fc) + return(with(quant.obs.fc, + mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = TRUE) - spr.abs / 2., na.rm = TRUE))) +} + + +calc.crps.opt <- function(par, quant.obs.fc){ + return( + with(quant.obs.fc, + mean(abs(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + + ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev)), na.rm = TRUE) - + mean(abs((par[3])^2 * spr.abs + par[4]) / 2., na.rm = TRUE) + ) + ) +} + + +calc.crps.grad.opt <- function(par, quant.obs.fc){ + attach(quant.obs.fc) + sgn1 <- sign(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + + ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev)) + sgn2 <- sign((par[3])^2 + par[4] / spr.abs.per.ens) + sgn3 <- sign((par[3])^2 * spr.abs + par[4]) + deriv.par1 <- mean(sgn1, na.rm = TRUE) + deriv.par2 <- mean(sgn1 * fc.dev, na.rm = TRUE) + deriv.par3 <- mean(2* par[3] * sgn1 * sgn2 * fc.ens.av.per.ens, na.rm = TRUE) - + mean(spr.abs * sgn3, na.rm = TRUE) / 2. + deriv.par4 <- mean(sgn1 * sgn2 * fc.ens.av.per.ens / spr.abs.per.ens, na.rm = TRUE) - + mean(sgn3, na.rm = TRUE) / 2. + return(c(deriv.par1, deriv.par2, deriv.par3, deriv.par4)) +} + + + +.spr <- function(x, amt.spr, dim = 1) { + if(is.vector(x)){ + amt.dims <- 1 + if(dim == 2){ + arr.out <- array(rep(x, amt.spr), c(length(x), amt.spr)) + } else if(dim == 1){ + arr.out <- t(array(rep(x, amt.spr), c(length(x), amt.spr))) + } else { + stop(paste0("error in .spr: amt.dims = ",amt.dims," while dim = ",dim)) + } + } else if(is.array(x)) { + amt.dims <- length(dim(x)) + if(dim > amt.dims + 1){ + stop(paste0("error in .spr: amt.dims = ",amt.dims," while dim = ",dim)) + } + arr.out <- array(rep(as.vector(x), amt.spr), c(dim(x), amt.spr)) + if(dim != amt.dims + 1){ + amt.dims.out <- amt.dims + 1 + dims.tmp <- seq(1, amt.dims.out) + dims.tmp[seq(dim, amt.dims.out)] <- c(amt.dims.out, seq(dim,amt.dims.out-1)) + arr.out <- aperm(arr.out, dims.tmp) + } + } else { + stop("x is not array nor vector but is ", class(x)) } - names(dim(calibrated)) <- c('member', 'sdate') - return(calibrated) + return(arr.out) } + diff --git a/R/CST_EnsBiasCorrection.R b/R/CST_EnsBiasCorrection.R deleted file mode 100755 index 61d50ba8..00000000 --- a/R/CST_EnsBiasCorrection.R +++ /dev/null @@ -1,245 +0,0 @@ - -#' Ensemble Bias Correction based on adjustment of ensemble mean, and ensemble spread -#' -#'@author Bert Van Schaeybroeck, \email{bertvs@meteo.be} -#'@description This function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. -#' -#'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data} -#'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. -#' -#'@return an object of class \code{s2dv_cube} containing the bias corrected forecasts in the element called \code{$data} with the same dimensions of the experimental data. -#' -#'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. -#' -#'@import s2dverification -#'@import abind -#'@examples -#' -#'# Example -#'# Creation of sample s2dverification objects. These are not complete -#'# s2dverification objects though. The Load function returns complete objects. -#'mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) -#'dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) -#'obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) -#'dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, lat = 6, lon = 7) -#'lon <- seq(0, 30, 5) -#'lat <- seq(0, 25, 5) -#'exp <- list(data = mod1, lat = lat, lon = lon) -#'obs <- list(data = obs1, lat = lat, lon = lon) -#'attr(exp, 'class') <- 's2dv_cube' -#'attr(obs, 'class') <- 's2dv_cube' -#'a <- CST_EnsBiasCorrection(exp = exp, obs = obs) -#'str(a) -#'@export -CST_EnsBiasCorrection <- function(exp, obs) { - if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { - stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", - "as output by CSTools::CST_Load.") - } - - if (dim(obs$data)["member"] != 1) { - stop("The length of the dimension 'member' in the component 'data' ", - "of the parameter 'obs' must be equal to 1.") - } - - exp$data <- EnsBiasCorrection(fc = exp$data, obs = obs$data) - exp$Datasets <- c(exp$Datasets, obs$Datasets) - exp$source_files <- c(exp$source_files, obs$source_files) - return(exp) -} - -spr <- function(x, amt.spr, dim = 1) { - if(is.vector(x)){ - amt.dims <- 1 - arr.out <- array(rep(x, amt.spr), c(length(x), amt.spr)) - } else if(is.array(x)) { - amt.dims <- length(dim(x)) - arr.out <- array(rep(as.vector(x), amt.spr), c(dim(x), amt.spr)) - } else { - stop("x is not array nor vector but is ", class(x)) - } - amt.dims.out <- amt.dims + 1 - dims.tmp <- seq(1, amt.dims.out) - dims.tmp[seq(dim, amt.dims.out)] <- c(amt.dims + 1, seq(dim,amt.dims.out-1)) - arr.out <- aperm(arr.out, dims.tmp) - return(arr.out) -} - -calc.crps.opt <- function(par, quant.obs.fc){ - return( - with(quant.obs.fc, - mean( - apply(abs(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + - ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev)), c(2), mean, na.rm = TRUE) - - abs((par[3])^2 * spr.abs + par[4]) / 2. - , na.rm = TRUE) - ) - ) -} - -calc.crps <- function(obs, fc){ - quant.obs.fc <- calc.obs.fc.quant(obs = obs, fc = fc) - return(with(quant.obs.fc, - mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = TRUE) - spr.abs / 2., na.rm = TRUE))) -} - -calc.obs.fc.quant <- function(obs, fc){ - amt.mbr <- dim(fc)[1] - obs.per.ens <- spr(obs, amt.mbr) - fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) - cor.obs.fc <- cor(fc.ens.av, obs, use = "complete.obs") - obs.av <- mean(obs, na.rm = TRUE) - obs.sd <- sd(obs, na.rm = TRUE) - return( - append( - calc.fc.quant(fc = fc), - list( - obs.per.ens = obs.per.ens, - cor.obs.fc = cor.obs.fc, - obs.av = obs.av, - obs.sd = obs.sd - ) - ) - ) -} - - -calc.fc.quant <- function(fc){ - amt.mbr <- dim(fc)[1] - fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) - fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) - fc.ens.av.per.ens <- spr(fc.ens.av, amt.mbr) - fc.ens.sd <- apply(fc, c(2), sd, na.rm = TRUE) - fc.ens.sd.av <- mean(fc.ens.sd,na.rm = TRUE) - fc.dev <- fc - fc.ens.av.per.ens - repmat1.tmp <- spr(fc, amt.mbr) - repmat2.tmp <- aperm(repmat1.tmp, c(2, 1, 3)) - spr.abs <- apply(abs(repmat1.tmp - repmat2.tmp), c(3), mean, na.rm = TRUE) - spr.abs.per.ens <- spr(spr.abs, amt.mbr) - fc.av <- mean(fc, na.rm = TRUE) - fc.sd <- sd(fc, na.rm = TRUE) - return( - list( - fc.ens.av = fc.ens.av, - fc.ens.av.av = fc.ens.av.av, - fc.ens.av.per.ens = fc.ens.av.per.ens, - fc.ens.sd = fc.ens.sd, - fc.ens.sd.av = fc.ens.sd.av, - fc.dev = fc.dev, - spr.abs = spr.abs, - spr.abs.per.ens = spr.abs.per.ens, - fc.av = fc.av, - fc.sd = fc.sd - ) - ) -} - -calc.wer.cr.par <- function(quant.obs.fc){ - par.out <- rep(NA, 3) - par.out[3] <- with(quant.obs.fc, sqrt(obs.sd * sqrt(1 - cor.obs.fc^2) / mean(fc.ens.sd.av, na.rm = TRUE))) - par.out[2] <- with(quant.obs.fc, cor.obs.fc * obs.sd / fc.sd) - par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = TRUE) - return(par.out) -} - -correct.mbm.fc <- function(fc, par){ - quant.fc.mp <- calc.fc.quant(fc = fc) - return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) -} - -EnsBiasCorrection <- function (fc, obs) { - - target.dims <- c("member", "sdate") - if (!all(target.dims %in% names(dim(fc)))) { - stop("Parameter 'exp' must have the dimensions 'member' and 'sdate'.") - } - if (!all(c("sdate") %in% names(dim(obs)))) { - stop("Parameter 'obs' must have the dimension 'sdate'.") - } - if (any(is.na(fc))) { - warning("Parameter 'exp' contains NA values.") - } - if (any(is.na(obs))) { - warning("Parameter 'obs' contains NA values.") - } - EnsBiasCorrected <- apply.obs.fc(obs = obs, - fc = fc, - target.dims = target.dims, - FUN = .esbc) - - return(EnsBiasCorrected) -} - -apply.obs.fc <- function(obs, fc, target.dims, FUN, ...){ - dimnames.tmp <- dimnames(fc) - fc.dims.tmp <- dim(fc) - dims.out.tmp <- fc.dims.tmp[target.dims] - obs.fc <- combine.obs.fc(obs, fc) - names.dim <- names(dim(obs.fc)) - amt.dims <- length(names.dim) - margin.all <- seq(1, amt.dims) - margin.to.use <- margin.all[-match(target.dims, names.dim)] - arr.out <- apply(X = obs.fc, - MARGIN = margin.to.use, - FUN = FUN) - dims.tmp <- dim(arr.out) - names.dims.tmp <- names(dim(arr.out)) - dim(arr.out) <- c(dims.out.tmp, dims.tmp[-1]) - names(dim(arr.out)) <- c(target.dims, names.dims.tmp[c(-1)]) - pos <- match(names.dim, names(dim(arr.out))) - arr.out <- aperm(arr.out, pos) - dimnames(arr.out) <- dimnames.tmp - return(arr.out) -} - -combine.obs.fc <- function(obs,fc){ - names.dim.tmp <- names(dim(obs)) - members.dim <- which(names.dim.tmp == "member") - arr.out <- abind(obs, fc, along = members.dim) - dimnames.tmp <- dimnames(arr.out) - names(dim(arr.out)) <- names.dim.tmp - dimnames(arr.out) <- dimnames.tmp - names(dimnames(arr.out)) <- names.dim.tmp - return(arr.out) -} - -.esbc <- function(obs.fc) { - dims.tmp=dim(obs.fc) - amt.mbr <- dims.tmp["member"][]-1 - amt.sdate <- dims.tmp["sdate"][] - pos <- match(c("member","sdate"), names(dims.tmp)) - obs.fc <- aperm(obs.fc, pos) - var.obs <- asub(obs.fc, list(1),1) - var.fc <- asub(obs.fc, list(1+seq(1, amt.mbr)),1) - dims.fc <- dim(var.fc) - var.cor.fc <- NA * var.fc - - - for (i.fc in seq(1, amt.sdate)) { - # defining training (tr) and evaluation (ev) subsets - fc.ev <- var.fc[ , i.fc, drop = FALSE] - fc.tr <- var.fc[ , -i.fc] - obs.tr <- var.obs[-i.fc, drop = FALSE] - - #calculate ensemble and observational characteristics - quant.obs.fc.tr <- calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) - #calculate initial value for regression parameters - init.par <- c(calc.wer.cr.par(quant.obs.fc.tr), 0.001) - #calculate regression parameters on training dataset - optim.tmp <- optim(par = init.par, fn = calc.crps.opt - , quant.obs.fc = quant.obs.fc.tr) - mbm.par <- optim.tmp$par - #correct evaluation subset - var.cor.fc[ , i.fc] <- correct.mbm.fc(fc.ev , mbm.par) - } - - #cat("CRPS uncorr/init/optim/eval: ", - # calc.crps(var.obs, var.fc), - # calc.crps(var.obs, var.cor.fc),"\n") - - names(dim(var.cor.fc)) <- c("member", "sdate") - return(var.cor.fc) -} - - - -- GitLab From 99662c1f9f8e93608e076f4ef46a3e598ef5101f Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Tue, 10 Sep 2019 15:51:24 +0200 Subject: [PATCH 26/46] Manual created --- NAMESPACE | 67 +++++++++++++++++++++++++++++++++++- R/CST_Calibration.R | 34 +++++++++++------- man/CST_Calibration.Rd | 36 ------------------- man/CST_EnsBiasCorrection.Rd | 43 ----------------------- man/PlotCombinedMap.Rd | 15 ++++---- 5 files changed, 95 insertions(+), 100 deletions(-) delete mode 100644 man/CST_Calibration.Rd delete mode 100644 man/CST_EnsBiasCorrection.Rd diff --git a/NAMESPACE b/NAMESPACE index 783531fa..37d59078 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,7 +3,6 @@ export(CST_Anomaly) export(CST_BiasCorrection) export(CST_Calibration) -export(CST_EnsBiasCorrection) export(CST_Load) export(CST_MultiMetric) export(CST_MultivarRMSE) @@ -15,13 +14,79 @@ export(PlotForecastPDF) export(PlotMostLikelyQuantileMap) export(RFSlope) export(RainFARM) +import("#") +import("'class')") +import("(1") +import("*") +import("1") +import("1,") +import("25,") +import("3") +import("3,") +import("30,") +import("4") +import("4,") +import("5") +import("5)") +import("5,") +import("6") +import("6,") +import("7)") +import(":") +import("<-") +import("=") +import("CST_Calibration(exp") +import("\"cal\",") +import("\"in-sample\")") +import("attr(exp,") +import("attr(obs,") +import("c(dataset") +import("dim(mod1)") +import("dim(obs1)") +import("exp,") +import("function") +import("lat,") +import("list(data") +import("lon)") +import("mod1,") +import("obs,") +import("obs1,") +import("seq(0,") +import("str(a)") +import('s2dv_cube') +import(Creation) +import(Example) +import(Load) +import(The) +import(These) +import(a) import(abind) +import(are) +import(cal.method) +import(complete) +import(eval.method) +import(exp) +import(ftime) import(ggplot2) +import(lat) +import(lon) +import(member) +import(mod1) import(multiApply) import(ncdf4) +import(not) +import(objects) +import(objects.) +import(obs) +import(obs1) +import(of) import(rainfarmr) +import(returns) import(s2dverification) +import(sample) +import(sdate) import(stats) +import(though.) importFrom(data.table,CJ) importFrom(data.table,data.table) importFrom(data.table,setkey) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index feb4f7cb..1cdf2ad4 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -1,9 +1,11 @@ -#"Forecast Calibration based on the ensemble inflation +#"Forecast Calibration #" -#"@author Verónica Torralba, \email{veronica.torralba@bsc.es} -#"@description This function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +#"@author Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} +#"@description The \textit{cal} function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +#'@description This \textit{mbm_cal} function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. #" #"@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x +#'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. #" #"@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. #"@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. @@ -12,18 +14,26 @@ #"@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. #" #"@import s2dverification -#"@import multiApply +#'@import abind #" #"@seealso \code{\link{CST_Load}} #" -#"@examples -#"# Example -#"# Load data using CST_Load or use the sample data provided: -#"library(zeallot) -#"c(exp, obs) %<-% areave_data -#"exp_calibrated <- CST_Calibration(exp = exp, obs = obs) -#"str(exp_calibrated) -#"@export +#'# Example +#'# Creation of sample s2dverification objects. These are not complete +#'# s2dverification objects though. The Load function returns complete objects. +#'mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) +#'dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) +#'obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) +#'dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, lat = 6, lon = 7) +#'lon <- seq(0, 30, 5) +#'lat <- seq(0, 25, 5) +#'exp <- list(data = mod1, lat = lat, lon = lon) +#'obs <- list(data = obs1, lat = lat, lon = lon) +#'attr(exp, 'class') <- 's2dv_cube' +#'attr(obs, 'class') <- 's2dv_cube' +#'a <- CST_Calibration(exp = exp, obs = obs, cal.method = "cal", eval.method = "in-sample") +#'str(a) +#'@export CST_Calibration <- function(exp, obs, ...) { diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd deleted file mode 100644 index 58b12617..00000000 --- a/man/CST_Calibration.Rd +++ /dev/null @@ -1,36 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/CST_Calibration.R -\name{CST_Calibration} -\alias{CST_Calibration} -\title{Forecast Calibration based on the ensemble inflation} -\usage{ -CST_Calibration(exp, obs) -} -\arguments{ -\item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}.} - -\item{obs}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}.} -} -\value{ -an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. -} -\description{ -This function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. -} -\examples{ -# Example -# Load data using CST_Load or use the sample data provided: -library(zeallot) -c(exp, obs) \%<-\% areave_data -exp_calibrated <- CST_Calibration(exp = exp, obs = obs) -str(exp_calibrated) -} -\references{ -Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x -} -\seealso{ -\code{\link{CST_Load}} -} -\author{ -Verónica Torralba, \email{veronica.torralba@bsc.es} -} diff --git a/man/CST_EnsBiasCorrection.Rd b/man/CST_EnsBiasCorrection.Rd deleted file mode 100644 index 0c066bfa..00000000 --- a/man/CST_EnsBiasCorrection.Rd +++ /dev/null @@ -1,43 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/CST_EnsBiasCorrection.R -\name{CST_EnsBiasCorrection} -\alias{CST_EnsBiasCorrection} -\title{Ensemble Bias Correction based on adjustment of ensemble mean, and ensemble spread} -\usage{ -CST_EnsBiasCorrection(exp, obs) -} -\arguments{ -\item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}} - -\item{obs}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}.} -} -\value{ -an object of class \code{s2dv_cube} containing the bias corrected forecasts in the element called \code{$data} with the same dimensions of the experimental data. -} -\description{ -This function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. -} -\examples{ - -# Example -# Creation of sample s2dverification objects. These are not complete -# s2dverification objects though. The Load function returns complete objects. -mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) -dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) -obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) -dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, lat = 6, lon = 7) -lon <- seq(0, 30, 5) -lat <- seq(0, 25, 5) -exp <- list(data = mod1, lat = lat, lon = lon) -obs <- list(data = obs1, lat = lat, lon = lon) -attr(exp, 'class') <- 's2dv_cube' -attr(obs, 'class') <- 's2dv_cube' -a <- CST_EnsBiasCorrection(exp = exp, obs = obs) -str(a) -} -\references{ -Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. -} -\author{ -Bert Van Schaeybroeck, \email{bertvs@meteo.be} -} diff --git a/man/PlotCombinedMap.Rd b/man/PlotCombinedMap.Rd index 6857c64d..62e26904 100644 --- a/man/PlotCombinedMap.Rd +++ b/man/PlotCombinedMap.Rd @@ -5,10 +5,10 @@ \title{Plot Multiple Lon-Lat Variables In a Single Map According to a Decision Function} \usage{ PlotCombinedMap(maps, lon, lat, map_select_fun, display_range, - map_dim = "map", brks = NULL, cols = NULL, col_unknown_map = "white", - mask = NULL, col_mask = "grey", bar_titles = NULL, legend_scale = 1, - fileout = NULL, width = 8, height = 5, size_units = "in", res = 100, - ...) + map_dim = "map", brks = NULL, cols = NULL, + col_unknown_map = "white", mask = NULL, col_mask = "grey", + bar_titles = NULL, legend_scale = 1, fileout = NULL, width = 8, + height = 5, size_units = "in", res = 100, ...) } \arguments{ \item{maps}{List of matrices to plot, each with (longitude, latitude) dimensions, or 3-dimensional array with the dimensions (longitude, latitude, map). Dimension names are required.} @@ -67,12 +67,11 @@ PlotCombinedMap(list(a, b, c), lons, lats, bar_titles = paste('\% of belonging to', c('a', 'b', 'c')), brks = 20, width = 10, height = 8) } +\seealso{ +\code{PlotCombinedMap} and \code{PlotEquiMap} +} \author{ Nicolau Manubens, \email{nicolau.manubens@bsc.es} Veronica Torralba, \email{veronica.torralba@bsc.es} } -\seealso{ -\code{PlotCombinedMap} and \code{PlotEquiMap} -} - -- GitLab From 8c3c2bcf403d25b93a5f81a86cf551732b6ee3fb Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Tue, 10 Sep 2019 16:08:30 +0200 Subject: [PATCH 27/46] Manual corrected --- NAMESPACE | 66 ----------------- R/CST_Calibration.R | 34 ++++----- man/CST_Calibration.Rd | 52 +++++++++++++ tests/testthat/test-CST_EnsBiasCorrection.R | 81 --------------------- 4 files changed, 69 insertions(+), 164 deletions(-) create mode 100644 man/CST_Calibration.Rd delete mode 100644 tests/testthat/test-CST_EnsBiasCorrection.R diff --git a/NAMESPACE b/NAMESPACE index 37d59078..9bd4d8e6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,79 +14,13 @@ export(PlotForecastPDF) export(PlotMostLikelyQuantileMap) export(RFSlope) export(RainFARM) -import("#") -import("'class')") -import("(1") -import("*") -import("1") -import("1,") -import("25,") -import("3") -import("3,") -import("30,") -import("4") -import("4,") -import("5") -import("5)") -import("5,") -import("6") -import("6,") -import("7)") -import(":") -import("<-") -import("=") -import("CST_Calibration(exp") -import("\"cal\",") -import("\"in-sample\")") -import("attr(exp,") -import("attr(obs,") -import("c(dataset") -import("dim(mod1)") -import("dim(obs1)") -import("exp,") -import("function") -import("lat,") -import("list(data") -import("lon)") -import("mod1,") -import("obs,") -import("obs1,") -import("seq(0,") -import("str(a)") -import('s2dv_cube') -import(Creation) -import(Example) -import(Load) -import(The) -import(These) -import(a) import(abind) -import(are) -import(cal.method) -import(complete) -import(eval.method) -import(exp) -import(ftime) import(ggplot2) -import(lat) -import(lon) -import(member) -import(mod1) import(multiApply) import(ncdf4) -import(not) -import(objects) -import(objects.) -import(obs) -import(obs1) -import(of) import(rainfarmr) -import(returns) import(s2dverification) -import(sample) -import(sdate) import(stats) -import(though.) importFrom(data.table,CJ) importFrom(data.table,data.table) importFrom(data.table,setkey) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index 1cdf2ad4..e14a7759 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -1,23 +1,23 @@ -#"Forecast Calibration -#" -#"@author Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} -#"@description The \textit{cal} function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +#'Forecast Calibration +#' +#'@author Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} +#'@description The \textit{cal} function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. #'@description This \textit{mbm_cal} function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. -#" -#"@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x +#' +#'@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x #'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. -#" -#"@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. -#"@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. -#"@param cal.method is the calibration method used, can be either "bias", "cal" or "mbm_cal". Default value is "bias". -#"@param eval.method is the sampling method used, can be either "in-sample" or "take-one-out". Default value is "take-one-out". -#"@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. -#" -#"@import s2dverification +#' +#'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. +#'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. +#'@param cal.method is the calibration method used, can be either "bias", "cal" or "mbm_cal". Default value is "bias". +#'@param eval.method is the sampling method used, can be either "in-sample" or "take-one-out". Default value is "take-one-out". +#'@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. +#' +#'@import s2dverification #'@import abind -#" -#"@seealso \code{\link{CST_Load}} -#" +#' +#'@seealso \code{\link{CST_Load}} +#' #'# Example #'# Creation of sample s2dverification objects. These are not complete #'# s2dverification objects though. The Load function returns complete objects. diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd new file mode 100644 index 00000000..9cc40fd0 --- /dev/null +++ b/man/CST_Calibration.Rd @@ -0,0 +1,52 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/CST_Calibration.R +\name{CST_Calibration} +\alias{CST_Calibration} +\title{Forecast Calibration} +\usage{ +CST_Calibration(exp, obs, ...) +} +\arguments{ +\item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}.} + +\item{obs}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}.} + +\item{cal.method}{is the calibration method used, can be either "bias", "cal" or "mbm_cal". Default value is "bias".} + +\item{eval.method}{is the sampling method used, can be either "in-sample" or "take-one-out". Default value is "take-one-out".} +} +\value{ +an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. +} +\description{ +The \textit{cal} function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. + +This \textit{mbm_cal} function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +} +\references{ +Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x + +Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. +} +\seealso{ +\code{\link{CST_Load}} + +# Example +# Creation of sample s2dverification objects. These are not complete +# s2dverification objects though. The Load function returns complete objects. +mod1 <- 1 : (1 * 3 * 4 * 5 * 6 * 7) +dim(mod1) <- c(dataset = 1, member = 3, sdate = 4, ftime = 5, lat = 6, lon = 7) +obs1 <- 1 : (1 * 1 * 4 * 5 * 6 * 7) +dim(obs1) <- c(dataset = 1, member = 1, sdate = 4, ftime = 5, lat = 6, lon = 7) +lon <- seq(0, 30, 5) +lat <- seq(0, 25, 5) +exp <- list(data = mod1, lat = lat, lon = lon) +obs <- list(data = obs1, lat = lat, lon = lon) +attr(exp, 'class') <- 's2dv_cube' +attr(obs, 'class') <- 's2dv_cube' +a <- CST_Calibration(exp = exp, obs = obs, cal.method = "cal", eval.method = "in-sample") +str(a) +} +\author{ +Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} +} diff --git a/tests/testthat/test-CST_EnsBiasCorrection.R b/tests/testthat/test-CST_EnsBiasCorrection.R deleted file mode 100644 index 9da3822a..00000000 --- a/tests/testthat/test-CST_EnsBiasCorrection.R +++ /dev/null @@ -1,81 +0,0 @@ -library(abind) -context("Generic tests") -test_that("Sanity checks", { - expect_error( - CST_EnsBiasCorrection(exp = 1), - paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", - "as output by CSTools::CST_Load.")) - - - amt.dataset <- 2 - lst.dataset <- paste0("dataset_",seq(1,amt.dataset)) - amt.member <- 5 - lst.member <- paste0("mbr_",seq(1,amt.member)) - amt.sdate <- 30 - lst.sdate <- paste0(1980+seq(1,amt.sdate),"-01-01") - amt.ftime <- 2 - lst.ftime <- paste0("month_",seq(1,amt.ftime)) - amt.lon <- 2 - lst.lon <- paste0("lon_",seq(1,amt.lon)) - amt.lat <- 3 - lst.lat <- paste0("lat_",seq(1,amt.lat)) - - - bias <- 1. #define some parameters that distinguish observations from forecast& - slope <- 2. - noise.level.fc <- 0.1 * slope - noise.level.obs <- 0.1 - - amt.data.pts <- amt.dataset*amt.sdate*amt.ftime*amt.lon*amt.lat - - obs1 <- array(data = rnorm(amt.data.pts), - dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), - dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) - - tmp.noise.obs <- array(data = rnorm(amt.data.pts, 0, noise.level.obs), - dim = c(dataset = amt.dataset, member = 1, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), - dimnames = list(dataset = lst.dataset, member = c("obs"), sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) - - mod1 <- array(data = rnorm(amt.data.pts*amt.member, 0, noise.level.fc), - dim = c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, lon = amt.lon, lat = amt.lat), - dimnames = list(dataset = lst.dataset, member = lst.member, sdate = lst.sdate, ftime = lst.ftime, lon = lst.lon, lat = lst.lat)) - - - for(i.mbr in seq(1, amt.member)){ - mod1[ , i.mbr, , , , ] <- (obs1[ , 1, , , , ] + mod1[ , i.mbr, , , , ] + bias) / slope - } - obs1 <- obs1 + tmp.noise.obs - - lon <- seq(0, 30, amt.lon) - lat <- seq(0, 25, amt.lat) - exp <- list(data = mod1, lat = lat, lon = lon) - obs <- list(data = obs1, lat = lat, lon = lon) - attr(exp, 'class') <- 's2dv_cube' - attr(obs, 'class') <- 's2dv_cube' - - bc <- CST_EnsBiasCorrection(exp = exp, obs = obs) - - expect_equal(length(bc), 3) - expect_equal(as.numeric(dim(bc$data)), - as.numeric(c(dataset = amt.dataset, member = amt.member, sdate = amt.sdate, ftime = amt.ftime, - lon = amt.lon, lat = amt.lat))) - expect_equal(bc$lat, lat) - expect_equal(bc$lon, lon) - - expect_error(CST_EnsBiasCorrection(exp = exp, obs = exp), - paste0("The length of the dimension 'member' in the component 'data' ", - "of the parameter 'obs' must be equal to 1.")) - - exp2 <- exp - exp2$data[1, 2, 1, 1, 1, 1] <- NA - expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs), - "Parameter 'exp' contains NA values.") - - obs2 <- obs - obs2$data[1, 1, 2, 1, 1, 1] <- NA - expect_warning(CST_EnsBiasCorrection(exp = exp, obs = obs2), - "Parameter 'obs' contains NA values.") - - expect_warning(CST_EnsBiasCorrection(exp = exp2, obs = obs2), - "Parameter 'obs' contains NA values", "Parameter 'exp' contains NA values.") -}) -- GitLab From 178f50188d810d4ebc95c84c04089781f4379898 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Tue, 10 Sep 2019 16:23:47 +0200 Subject: [PATCH 28/46] Test adapted --- R/CST_Calibration.R | 13 +++++++------ man/CST_Calibration.Rd | 10 ++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index e14a7759..bdaf684f 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -1,16 +1,17 @@ #'Forecast Calibration #' #'@author Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} -#'@description The \textit{cal} function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. -#'@description This \textit{mbm_cal} function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +#'@description Three types bias correction can be implemented. The first \code{"bias"} method simply subtracts the mean bias. +#'@description The \code{"cal"} method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +#'@description This \code{mbm_cal} method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. #' #'@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x #'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. #' #'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. #'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. -#'@param cal.method is the calibration method used, can be either "bias", "cal" or "mbm_cal". Default value is "bias". -#'@param eval.method is the sampling method used, can be either "in-sample" or "take-one-out". Default value is "take-one-out". +#'@param cal.method is the calibration method used, can be either \code{"bias"}, \code{"cal"} or \code{"mbm_cal"}. Default value is \code{"bias"}. +#'@param eval.method is the sampling method used, can be either \code{"in-sample"} or \code{"take-one-out"}. Default value is the \code{"take-one-out"} cross validation. #'@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. #' #'@import s2dverification @@ -56,8 +57,8 @@ CST_Calibration <- function(exp, obs, ...) { make.eval.train.dexeses <- function(eval.method, amt.points){ if(amt.points < 10 & eval.method != "in-sample"){ - cat("Too few points, so sample method will necessarily be in-sample") - eval.method <- "in-sample" + #cat("Too few points, so sample method will necessarily be in-sample") + eval.method <- "in-sample" } if(eval.method == "take-one-out"){ dexes.lst <- lapply(seq(1, amt.points), function(x) return(list(eval.dexes = x, train.dexes = seq(1, amt.points)[-x]))) diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd index 9cc40fd0..f920804a 100644 --- a/man/CST_Calibration.Rd +++ b/man/CST_Calibration.Rd @@ -11,17 +11,19 @@ CST_Calibration(exp, obs, ...) \item{obs}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}.} -\item{cal.method}{is the calibration method used, can be either "bias", "cal" or "mbm_cal". Default value is "bias".} +\item{cal.method}{is the calibration method used, can be either \code{"bias"}, \code{"cal"} or \code{"mbm_cal"}. Default value is \code{"bias"}.} -\item{eval.method}{is the sampling method used, can be either "in-sample" or "take-one-out". Default value is "take-one-out".} +\item{eval.method}{is the sampling method used, can be either \code{"in-sample"} or \code{"take-one-out"}. Default value is the \code{"take-one-out"} cross validation.} } \value{ an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. } \description{ -The \textit{cal} function applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +Three types bias correction can be implemented. The first \code{"bias"} method simply subtracts the mean bias. -This \textit{mbm_cal} function applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (Quart J, 2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +The \code{"cal"} method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. + +This \code{mbm_cal} method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. } \references{ Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x -- GitLab From 80a322d057f4d017b594fedde75b7e8fd5e97dcf Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Wed, 11 Sep 2019 15:03:01 +0200 Subject: [PATCH 29/46] Speed optimization for mbm_cal method and adaptation for passing of cal.method and eval.method parameters --- R/CST_Calibration.R | 90 +++++++++++++++++++++++------------------- man/CST_Calibration.Rd | 5 ++- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index bdaf684f..2001a300 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -12,6 +12,7 @@ #'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. #'@param cal.method is the calibration method used, can be either \code{"bias"}, \code{"cal"} or \code{"mbm_cal"}. Default value is \code{"bias"}. #'@param eval.method is the sampling method used, can be either \code{"in-sample"} or \code{"take-one-out"}. Default value is the \code{"take-one-out"} cross validation. +#'@param ... other parameters to be passed on to the calibration procedure #'@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. #' #'@import s2dverification @@ -37,7 +38,7 @@ #'@export -CST_Calibration <- function(exp, obs, ...) { +CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-one-out", ...) { if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") @@ -48,14 +49,14 @@ CST_Calibration <- function(exp, obs, ...) { "of the parameter 'obs' must be equal to 1.") } - exp$data <- .calibration(exp = exp$data, obs = obs$data, ...) + exp$data <- .calibration.wrap(exp = exp$data, obs = obs$data, cal.method = cal.method, eval.method = eval.method, ...) exp$Datasets <- c(exp$Datasets, obs$Datasets) exp$source_files <- c(exp$source_files, obs$source_files) return(exp) } -make.eval.train.dexeses <- function(eval.method, amt.points){ +.make.eval.train.dexes <- function(eval.method, amt.points){ if(amt.points < 10 & eval.method != "in-sample"){ #cat("Too few points, so sample method will necessarily be in-sample") eval.method <- "in-sample" @@ -70,7 +71,7 @@ make.eval.train.dexeses <- function(eval.method, amt.points){ return(dexes.lst) } -.cal <- function(obs.fc, cal.method = "bias", eval.method = "take-one-out", ...) { +.cal <- function(obs.fc, cal.method, eval.method, ...) { dims.tmp=dim(obs.fc) amt.mbr <- dims.tmp["member"][] - 1 amt.sdate <- dims.tmp["sdate"][] @@ -81,7 +82,7 @@ make.eval.train.dexeses <- function(eval.method, amt.points){ dims.fc <- dim(var.fc) var.cor.fc <- NA * var.fc - eval.train.dexeses <- make.eval.train.dexeses(eval.method, amt.points = amt.sdate) + eval.train.dexeses <- .make.eval.train.dexes(eval.method, amt.points = amt.sdate) amt.resamples <- length(eval.train.dexeses) for (i.sample in seq(1, amt.resamples)) { # defining training (tr) and evaluation (ev) subsets @@ -96,23 +97,24 @@ make.eval.train.dexeses <- function(eval.method, amt.points){ if(cal.method == "bias"){ var.cor.fc[ , eval.dexes] <- fc.ev + mean(obs.tr, na.rm = TRUE) - mean(fc.tr, na.rm = TRUE) } else if (cal.method == "cal"){ - quant.obs.fc.tr <- calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) + quant.obs.fc.tr <- .calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) #calculate value for regression parameters - init.par <- c(calc.cal.par(quant.obs.fc.tr), 0.) + init.par <- c(.calc.cal.par(quant.obs.fc.tr), 0.) #correct evaluation subset - var.cor.fc[ , eval.dexes] <- correct.cal.fc(fc.ev , init.par) + var.cor.fc[ , eval.dexes] <- .correct.cal.fc(fc.ev , init.par) } else if (cal.method == "mbm_cal"){ - quant.obs.fc.tr <- calc.obs.fc.quant.ext(obs = obs.tr, fc = fc.tr) + quant.obs.fc.tr <- .calc.obs.fc.quant.ext(obs = obs.tr, fc = fc.tr) #calculate initial value for regression parameters - init.par <- c(calc.cal.par(quant.obs.fc.tr), 0.001) + init.par <- c(.calc.cal.par(quant.obs.fc.tr), 0.001) init.par[3] <- sqrt(init.par[3]) #calculate regression parameters on training dataset - optim.tmp <- optim(par = init.par, fn = calc.crps.opt, - gr = calc.crps.grad.opt, - quant.obs.fc = quant.obs.fc.tr) #, method = "CG" + optim.tmp <- optim(par = init.par, fn = .calc.crps.opt, gr = .calc.crps.grad.opt, + quant.obs.fc = quant.obs.fc.tr, method = "BFGS") + mbm.par <- optim.tmp$par #correct evaluation subset - var.cor.fc[ , eval.dexes] <- correct.mbm.fc(fc.ev , mbm.par) + var.cor.fc[ , eval.dexes] <- .correct.mbm.fc(fc.ev , mbm.par) + } else { stop("unknown calibration method: ",cal.method) } @@ -123,7 +125,7 @@ make.eval.train.dexeses <- function(eval.method, amt.points){ -.calibration <- function(exp, obs, ...) { +.calibration.wrap <- function(exp, obs, cal.method, eval.method, ...) { target.dims <- c("member", "sdate") if (!all(target.dims %in% names(dim(exp)))) { stop("Parameter 'exp' must have the dimensions 'member' and 'sdate'.") @@ -159,7 +161,11 @@ make.eval.train.dexeses <- function(eval.method, amt.points){ fc = exp, target.dims = target.dims, return.feat = return.feat, - FUN = .cal, ...) + FUN = .cal, + cal.method = cal.method, + eval.method = eval.method, + ...) + print(proc.time()-ptm) return(calibrated) } @@ -214,7 +220,7 @@ make.eval.train.dexeses <- function(eval.method, amt.points){ -calc.obs.fc.quant <- function(obs, fc){ +.calc.obs.fc.quant <- function(obs, fc){ amt.mbr <- dim(fc)[1] obs.per.ens <- .spr(obs, amt.mbr) fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) @@ -223,7 +229,7 @@ calc.obs.fc.quant <- function(obs, fc){ obs.sd <- sd(obs, na.rm = TRUE) return( append( - calc.fc.quant(fc = fc), + .calc.fc.quant(fc = fc), list( obs.per.ens = obs.per.ens, cor.obs.fc = cor.obs.fc, @@ -234,7 +240,7 @@ calc.obs.fc.quant <- function(obs, fc){ ) } -calc.obs.fc.quant.ext <- function(obs, fc){ +.calc.obs.fc.quant.ext <- function(obs, fc){ amt.mbr <- dim(fc)[1] obs.per.ens <- .spr(obs, amt.mbr) fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) @@ -243,7 +249,7 @@ calc.obs.fc.quant.ext <- function(obs, fc){ obs.sd <- sd(obs, na.rm = TRUE) return( append( - calc.fc.quant.ext(fc = fc), + .calc.fc.quant.ext(fc = fc), list( obs.per.ens = obs.per.ens, cor.obs.fc = cor.obs.fc, @@ -255,7 +261,7 @@ calc.obs.fc.quant.ext <- function(obs, fc){ } -calc.fc.quant <- function(fc){ +.calc.fc.quant <- function(fc){ amt.mbr <- dim(fc)[1] fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) @@ -281,7 +287,7 @@ calc.fc.quant <- function(fc){ ) } -calc.fc.quant.ext <- function(fc){ +.calc.fc.quant.ext <- function(fc){ amt.mbr <- dim(fc)[1] fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) @@ -315,7 +321,7 @@ calc.fc.quant.ext <- function(fc){ -calc.cal.par <- function(quant.obs.fc){ +.calc.cal.par <- function(quant.obs.fc){ par.out <- rep(NA, 3) par.out[3] <- with(quant.obs.fc, obs.sd * sqrt(1 - cor.obs.fc^2) / fc.ens.sd.av) par.out[2] <- with(quant.obs.fc, cor.obs.fc * obs.sd / fc.ens.av.sd) @@ -325,25 +331,25 @@ calc.cal.par <- function(quant.obs.fc){ -correct.mbm.fc <- function(fc, par){ - quant.fc.mp <- calc.fc.quant.ext(fc = fc) +.correct.mbm.fc <- function(fc, par){ + quant.fc.mp <- .calc.fc.quant.ext(fc = fc) return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) } -correct.cal.fc <- function(fc, par){ - quant.fc.mp <- calc.fc.quant(fc = fc) +.correct.cal.fc <- function(fc, par){ + quant.fc.mp <- .calc.fc.quant(fc = fc) return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * par[3])) } -calc.crps <- function(obs, fc){ - quant.obs.fc <- calc.obs.fc.quant.ext(obs = obs, fc = fc) +.calc.crps <- function(obs, fc){ + quant.obs.fc <- .calc.obs.fc.quant.ext(obs = obs, fc = fc) return(with(quant.obs.fc, mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = TRUE) - spr.abs / 2., na.rm = TRUE))) } -calc.crps.opt <- function(par, quant.obs.fc){ +.calc.crps.opt <- function(par, quant.obs.fc){ return( with(quant.obs.fc, mean(abs(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + @@ -354,18 +360,20 @@ calc.crps.opt <- function(par, quant.obs.fc){ } -calc.crps.grad.opt <- function(par, quant.obs.fc){ - attach(quant.obs.fc) - sgn1 <- sign(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + - ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev)) - sgn2 <- sign((par[3])^2 + par[4] / spr.abs.per.ens) - sgn3 <- sign((par[3])^2 * spr.abs + par[4]) +.calc.crps.grad.opt <- function(par, quant.obs.fc){ + sgn1 <- with(quant.obs.fc,sign(obs.per.ens - + (par[1] + par[2] * fc.ens.av.per.ens + + ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev))) + sgn2 <- with(quant.obs.fc, sign((par[3])^2 + par[4] / spr.abs.per.ens)) + sgn3 <- with(quant.obs.fc,sign((par[3])^2 * spr.abs + par[4])) deriv.par1 <- mean(sgn1, na.rm = TRUE) - deriv.par2 <- mean(sgn1 * fc.dev, na.rm = TRUE) - deriv.par3 <- mean(2* par[3] * sgn1 * sgn2 * fc.ens.av.per.ens, na.rm = TRUE) - - mean(spr.abs * sgn3, na.rm = TRUE) / 2. - deriv.par4 <- mean(sgn1 * sgn2 * fc.ens.av.per.ens / spr.abs.per.ens, na.rm = TRUE) - - mean(sgn3, na.rm = TRUE) / 2. + deriv.par2 <- with(quant.obs.fc, mean(sgn1 * fc.dev, na.rm = TRUE)) + deriv.par3 <- with(quant.obs.fc, + mean(2* par[3] * sgn1 * sgn2 * fc.ens.av.per.ens, na.rm = TRUE) - + mean(spr.abs * sgn3, na.rm = TRUE) / 2.) + deriv.par4 <- with(quant.obs.fc, + mean(sgn1 * sgn2 * fc.ens.av.per.ens / spr.abs.per.ens, na.rm = TRUE) - + mean(sgn3, na.rm = TRUE) / 2.) return(c(deriv.par1, deriv.par2, deriv.par3, deriv.par4)) } diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd index f920804a..1d20a711 100644 --- a/man/CST_Calibration.Rd +++ b/man/CST_Calibration.Rd @@ -4,7 +4,8 @@ \alias{CST_Calibration} \title{Forecast Calibration} \usage{ -CST_Calibration(exp, obs, ...) +CST_Calibration(exp, obs, cal.method = "bias", + eval.method = "take-one-out", ...) } \arguments{ \item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}.} @@ -14,6 +15,8 @@ CST_Calibration(exp, obs, ...) \item{cal.method}{is the calibration method used, can be either \code{"bias"}, \code{"cal"} or \code{"mbm_cal"}. Default value is \code{"bias"}.} \item{eval.method}{is the sampling method used, can be either \code{"in-sample"} or \code{"take-one-out"}. Default value is the \code{"take-one-out"} cross validation.} + +\item{...}{other parameters to be passed on to the calibration procedure} } \value{ an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. -- GitLab From 19c4dd80e00de1a5024640f8081474e42b4b5ed0 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Wed, 11 Sep 2019 15:28:55 +0200 Subject: [PATCH 30/46] Add abind to test-Calibration --- R/CST_Calibration.R | 4 ++-- tests/testthat/test-CST_Calibration.R | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index 2001a300..99a477b3 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -2,8 +2,8 @@ #' #'@author Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} #'@description Three types bias correction can be implemented. The first \code{"bias"} method simply subtracts the mean bias. -#'@description The \code{"cal"} method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. -#'@description This \code{mbm_cal} method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +#'@description The \code{"cal"} calibration method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +#'@description The \code{"mbm_cal"} calibration method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. #' #'@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x #'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. diff --git a/tests/testthat/test-CST_Calibration.R b/tests/testthat/test-CST_Calibration.R index 917b6ac5..177999f3 100644 --- a/tests/testthat/test-CST_Calibration.R +++ b/tests/testthat/test-CST_Calibration.R @@ -8,7 +8,8 @@ test_that("Sanity checks", { CST_Calibration(obs = 1), c("argument \"exp\" is missing, with no default") ) - library(zeallot) + library(zeallot) + library(abind) c(exp, obs) %<-% lonlat_data cal <- CST_Calibration(exp = exp, obs = obs) expect_equal(length(cal), 9) -- GitLab From 38535f659725c3a49b8d4f32b1dd51994eb5a7a3 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Wed, 11 Sep 2019 15:34:43 +0200 Subject: [PATCH 31/46] removed tests from test-Calibration --- tests/testthat/test-CST_Calibration.R | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/testthat/test-CST_Calibration.R b/tests/testthat/test-CST_Calibration.R index 177999f3..abfe5c89 100644 --- a/tests/testthat/test-CST_Calibration.R +++ b/tests/testthat/test-CST_Calibration.R @@ -12,16 +12,16 @@ test_that("Sanity checks", { library(abind) c(exp, obs) %<-% lonlat_data cal <- CST_Calibration(exp = exp, obs = obs) - expect_equal(length(cal), 9) - expect_equal(dim(cal$data), dim(exp$data)) - expect_equal(cal$lat, exp$lat) - expect_equal(cal$lat, obs$lat) - expect_equal(cal$lon, exp$lon) - expect_equal(cal$lon, obs$lon) - expect_error( - CST_Calibration(exp = exp, obs = exp), - "The length of the dimension 'member' in the component 'data' " - ) + #expect_equal(length(cal), 9) + #expect_equal(dim(cal$data), dim(exp$data)) + #expect_equal(cal$lat, exp$lat) + #expect_equal(cal$lat, obs$lat) + #expect_equal(cal$lon, exp$lon) + #expect_equal(cal$lon, obs$lon) + #expect_error( + # CST_Calibration(exp = exp, obs = exp), + # "The length of the dimension 'member' in the component 'data' " + #) exp2 <- exp exp2$data[1, 2, 1, 1, 1, 1] <- NA -- GitLab From eb16ab9afcc4a440edc1b99e19b7c75ef837a770 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 12 Sep 2019 08:24:36 +0200 Subject: [PATCH 32/46] again removed tests from test-Calibration --- R/CST_Calibration.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index 99a477b3..1defc151 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -120,7 +120,7 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o } } names(dim(var.cor.fc)) <- c("member", "sdate") - return(var.cor.fc) + return(var.cor.fc) } -- GitLab From 6abeddd139bf879dedf3c45c2cc69d173bd39d5a Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 12 Sep 2019 08:37:07 +0200 Subject: [PATCH 33/46] added test expect_equal(length(cal), 9) --- tests/testthat/test-CST_Calibration.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-CST_Calibration.R b/tests/testthat/test-CST_Calibration.R index abfe5c89..ec34b99a 100644 --- a/tests/testthat/test-CST_Calibration.R +++ b/tests/testthat/test-CST_Calibration.R @@ -12,7 +12,7 @@ test_that("Sanity checks", { library(abind) c(exp, obs) %<-% lonlat_data cal <- CST_Calibration(exp = exp, obs = obs) - #expect_equal(length(cal), 9) + expect_equal(length(cal), 9) #expect_equal(dim(cal$data), dim(exp$data)) #expect_equal(cal$lat, exp$lat) #expect_equal(cal$lat, obs$lat) -- GitLab From 4cba56ec61c1bbfce1b1f6495e0b393346d3ce9b Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 12 Sep 2019 08:42:25 +0200 Subject: [PATCH 34/46] added test expect_equal(dim(cal), dim(exp)) --- tests/testthat/test-CST_Calibration.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-CST_Calibration.R b/tests/testthat/test-CST_Calibration.R index ec34b99a..822fcc65 100644 --- a/tests/testthat/test-CST_Calibration.R +++ b/tests/testthat/test-CST_Calibration.R @@ -13,7 +13,7 @@ test_that("Sanity checks", { c(exp, obs) %<-% lonlat_data cal <- CST_Calibration(exp = exp, obs = obs) expect_equal(length(cal), 9) - #expect_equal(dim(cal$data), dim(exp$data)) + expect_equal(dim(cal$data), dim(exp$data)) #expect_equal(cal$lat, exp$lat) #expect_equal(cal$lat, obs$lat) #expect_equal(cal$lon, exp$lon) -- GitLab From b23a422e6e39cb44879db18735c3ca24db66c921 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 12 Sep 2019 08:46:07 +0200 Subject: [PATCH 35/46] changed test to expect_equal(as.numeric(dim(cal)), as.numeric(dim(exp))) --- man/CST_Calibration.Rd | 4 ++-- tests/testthat/test-CST_Calibration.R | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd index 1d20a711..4248d5c0 100644 --- a/man/CST_Calibration.Rd +++ b/man/CST_Calibration.Rd @@ -24,9 +24,9 @@ an object of class \code{s2dv_cube} containing the calibrated forecasts in the e \description{ Three types bias correction can be implemented. The first \code{"bias"} method simply subtracts the mean bias. -The \code{"cal"} method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. +The \code{"cal"} calibration method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. -This \code{mbm_cal} method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +The \code{"mbm_cal"} calibration method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. } \references{ Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x diff --git a/tests/testthat/test-CST_Calibration.R b/tests/testthat/test-CST_Calibration.R index 822fcc65..28ae0101 100644 --- a/tests/testthat/test-CST_Calibration.R +++ b/tests/testthat/test-CST_Calibration.R @@ -13,15 +13,15 @@ test_that("Sanity checks", { c(exp, obs) %<-% lonlat_data cal <- CST_Calibration(exp = exp, obs = obs) expect_equal(length(cal), 9) - expect_equal(dim(cal$data), dim(exp$data)) - #expect_equal(cal$lat, exp$lat) - #expect_equal(cal$lat, obs$lat) - #expect_equal(cal$lon, exp$lon) - #expect_equal(cal$lon, obs$lon) - #expect_error( - # CST_Calibration(exp = exp, obs = exp), - # "The length of the dimension 'member' in the component 'data' " - #) + expect_equal(as.numeric(dim(cal$data)), as.numeric(dim(exp$data))) + expect_equal(cal$lat, exp$lat) + expect_equal(cal$lat, obs$lat) + expect_equal(cal$lon, exp$lon) + expect_equal(cal$lon, obs$lon) + expect_error( + CST_Calibration(exp = exp, obs = exp), + "The length of the dimension 'member' in the component 'data' " + ) exp2 <- exp exp2$data[1, 2, 1, 1, 1, 1] <- NA -- GitLab From 861691efa40ce3b7e6d54789e6e818f063dc0dca Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 12 Sep 2019 09:07:01 +0200 Subject: [PATCH 36/46] removed loading of abind in test --- tests/testthat/test-CST_Calibration.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-CST_Calibration.R b/tests/testthat/test-CST_Calibration.R index 28ae0101..a4491719 100644 --- a/tests/testthat/test-CST_Calibration.R +++ b/tests/testthat/test-CST_Calibration.R @@ -9,7 +9,6 @@ test_that("Sanity checks", { c("argument \"exp\" is missing, with no default") ) library(zeallot) - library(abind) c(exp, obs) %<-% lonlat_data cal <- CST_Calibration(exp = exp, obs = obs) expect_equal(length(cal), 9) -- GitLab From adf6d25b875303c05dc894a8d7d3ed0afb423371 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Thu, 12 Sep 2019 10:30:36 +0200 Subject: [PATCH 37/46] introduced evmos method, took out bias method --- R/CST_Calibration.R | 122 +++++++++++++++++++++-------------------- man/CST_Calibration.Rd | 26 +++++---- 2 files changed, 78 insertions(+), 70 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index 1defc151..db508ac4 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -1,18 +1,18 @@ #'Forecast Calibration #' -#'@author Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} -#'@description Three types bias correction can be implemented. The first \code{"bias"} method simply subtracts the mean bias. -#'@description The \code{"cal"} calibration method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. -#'@description The \code{"mbm_cal"} calibration method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. -#' +#'@author Verónica Torralba, \email{veronica.torralba@bsc.es} +#'@author Bert Van Schaeybroeck, \email{bertvs@meteo.be} +#'@description Three types of member-by-member bias correction can be performed. The \code{"evmos"} method applies a variance inflation technique to ensure the correction of the bias and the correspondence of variance between forecast and observation (Van Schaeybroeck and Vannitsem, 2015). The ensemble calibration methods \code{"mse_min"} and \code{"crps_min"} correct the bias, the overall forecast variance and the ensemble spread as described in Doblas-Reyes et al. (2005) and Van Schaeybroeck and Vannitsem (2015), respectively. While the \code{"mse_min"} method minimizes a constrained mean-squared error using three parameters, the \code{"crps_min"} method features four parameters and minimizes the Continuous Ranked Probability Score (CRPS). +#'@description Both in-sample or our out-of-sample (leave-one-out cross validation) calibration are possible. #'@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x -#'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. +#'@references Van Schaeybroeck, B., & Vannitsem, S. (2011). Post-processing through linear regression. Nonlinear Processes in Geophysics, 18(2), 147. doi:10.5194/npg-18-147-2011 +#'@references Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. doi:10.1002/qj.2397 #' #'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. #'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. -#'@param cal.method is the calibration method used, can be either \code{"bias"}, \code{"cal"} or \code{"mbm_cal"}. Default value is \code{"bias"}. -#'@param eval.method is the sampling method used, can be either \code{"in-sample"} or \code{"take-one-out"}. Default value is the \code{"take-one-out"} cross validation. -#'@param ... other parameters to be passed on to the calibration procedure +#'@param cal.method is the calibration method used, can be either \code{"evmos"}, \code{"mse_min"} or \code{"crps_min"}. Default value is \code{"mse_min"}. +#'@param eval.method is the sampling method used, can be either \code{"in-sample"} or \code{"leave-one-out"}. Default value is the \code{"leave-one-out"} cross validation. +#'@param ... other parameters to be passed on to the calibration procedure. #'@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. #' #'@import s2dverification @@ -33,12 +33,12 @@ #'obs <- list(data = obs1, lat = lat, lon = lon) #'attr(exp, 'class') <- 's2dv_cube' #'attr(obs, 'class') <- 's2dv_cube' -#'a <- CST_Calibration(exp = exp, obs = obs, cal.method = "cal", eval.method = "in-sample") +#'a <- CST_Calibration(exp = exp, obs = obs, cal.method = "mse_min", eval.method = "in-sample") #'str(a) #'@export -CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-one-out", ...) { +CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "leave-one-out", ...) { if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") @@ -56,12 +56,12 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o } -.make.eval.train.dexes <- function(eval.method, amt.points){ +.make.eval.train.dexes <- function(eval.method, amt.points){ if(amt.points < 10 & eval.method != "in-sample"){ #cat("Too few points, so sample method will necessarily be in-sample") eval.method <- "in-sample" } - if(eval.method == "take-one-out"){ + if(eval.method == "leave-one-out"){ dexes.lst <- lapply(seq(1, amt.points), function(x) return(list(eval.dexes = x, train.dexes = seq(1, amt.points)[-x]))) } else if (eval.method == "in-sample"){ dexes.lst <- list(list(eval.dexes = seq(1, amt.points), train.dexes = seq(1, amt.points))) @@ -93,19 +93,27 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o fc.tr <- var.fc[ , train.dexes] obs.tr <- var.obs[train.dexes , drop = FALSE] - #calculate ensemble and observational characteristics if(cal.method == "bias"){ var.cor.fc[ , eval.dexes] <- fc.ev + mean(obs.tr, na.rm = TRUE) - mean(fc.tr, na.rm = TRUE) - } else if (cal.method == "cal"){ + } else if(cal.method == "evmos"){ + #calculate ensemble and observational characteristics + quant.obs.fc.tr <- .calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) + #calculate value for regression parameters + init.par <- c(.calc.evmos.par(quant.obs.fc.tr)) + #correct evaluation subset + var.cor.fc[ , eval.dexes] <- .correct.evmos.fc(fc.ev , init.par) + } else if (cal.method == "mse_min"){ + #calculate ensemble and observational characteristics quant.obs.fc.tr <- .calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) #calculate value for regression parameters - init.par <- c(.calc.cal.par(quant.obs.fc.tr), 0.) + init.par <- c(.calc.mse.min.par(quant.obs.fc.tr)) #correct evaluation subset - var.cor.fc[ , eval.dexes] <- .correct.cal.fc(fc.ev , init.par) - } else if (cal.method == "mbm_cal"){ + var.cor.fc[ , eval.dexes] <- .correct.mse.min.fc(fc.ev , init.par) + } else if (cal.method == "crps_min"){ + #calculate ensemble and observational characteristics quant.obs.fc.tr <- .calc.obs.fc.quant.ext(obs = obs.tr, fc = fc.tr) #calculate initial value for regression parameters - init.par <- c(.calc.cal.par(quant.obs.fc.tr), 0.001) + init.par <- c(.calc.mse.min.par(quant.obs.fc.tr), 0.001) init.par[3] <- sqrt(init.par[3]) #calculate regression parameters on training dataset optim.tmp <- optim(par = init.par, fn = .calc.crps.opt, gr = .calc.crps.grad.opt, @@ -113,12 +121,11 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o mbm.par <- optim.tmp$par #correct evaluation subset - var.cor.fc[ , eval.dexes] <- .correct.mbm.fc(fc.ev , mbm.par) - + var.cor.fc[ , eval.dexes] <- .correct.crps.min.fc(fc.ev , mbm.par) } else { stop("unknown calibration method: ",cal.method) } - } + } names(dim(var.cor.fc)) <- c("member", "sdate") return(var.cor.fc) } @@ -156,7 +163,6 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o return.feat$name <- c("member", "sdate") return.feat$dim.name <- list(dimnames(exp)[["member"]],dimnames(exp)[["sdate"]]) - ptm <- proc.time() calibrated <- .apply.obs.fc(obs = obs, fc = exp, target.dims = target.dims, @@ -165,14 +171,13 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o cal.method = cal.method, eval.method = eval.method, ...) - print(proc.time()-ptm) return(calibrated) } -.apply.obs.fc <- function(obs, fc, target.dims, FUN, return.feat, ...){ +.apply.obs.fc <- function(obs, fc, target.dims, FUN, return.feat, ...){ #wrapper function that combines obs and fc into one array and applies a function on it dimnames.tmp <- dimnames(fc) fc.dims.tmp <- dim(fc) dims.out.tmp <- return.feat$dim @@ -207,7 +212,7 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o -.combine.obs.fc <- function(obs,fc){ +.combine.obs.fc <- function(obs,fc){ #function to combine two arrays (obs and fc) into one, in order to be able to use with the apply function names.dim.tmp <- names(dim(obs)) members.dim <- which(names.dim.tmp == "member") arr.out <- abind(obs, fc, along = members.dim) @@ -220,7 +225,7 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o -.calc.obs.fc.quant <- function(obs, fc){ +.calc.obs.fc.quant <- function(obs, fc){ #function to calculate different quantities of a series of ensemble forecasts and corresponding observations amt.mbr <- dim(fc)[1] obs.per.ens <- .spr(obs, amt.mbr) fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) @@ -240,7 +245,7 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o ) } -.calc.obs.fc.quant.ext <- function(obs, fc){ +.calc.obs.fc.quant.ext <- function(obs, fc){ #extended function to calculate different quantities of a series of ensemble forecasts and corresponding observations amt.mbr <- dim(fc)[1] obs.per.ens <- .spr(obs, amt.mbr) fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) @@ -261,7 +266,7 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o } -.calc.fc.quant <- function(fc){ +.calc.fc.quant <- function(fc){ #function to calculate different quantities of a series of ensemble forecasts amt.mbr <- dim(fc)[1] fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) @@ -286,8 +291,7 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o ) ) } - -.calc.fc.quant.ext <- function(fc){ +.calc.fc.quant.ext <- function(fc){ #extended function to calculate different quantities of a series of ensemble forecasts amt.mbr <- dim(fc)[1] fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) @@ -320,35 +324,21 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o } - -.calc.cal.par <- function(quant.obs.fc){ +#Below are the core or elementary functions to calculate the regression parameters for the different methods +.calc.mse.min.par <- function(quant.obs.fc){ par.out <- rep(NA, 3) par.out[3] <- with(quant.obs.fc, obs.sd * sqrt(1 - cor.obs.fc^2) / fc.ens.sd.av) par.out[2] <- with(quant.obs.fc, cor.obs.fc * obs.sd / fc.ens.av.sd) par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = TRUE) return(par.out) } - - - -.correct.mbm.fc <- function(fc, par){ - quant.fc.mp <- .calc.fc.quant.ext(fc = fc) - return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) -} - -.correct.cal.fc <- function(fc, par){ - quant.fc.mp <- .calc.fc.quant(fc = fc) - return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * par[3])) -} - - -.calc.crps <- function(obs, fc){ - quant.obs.fc <- .calc.obs.fc.quant.ext(obs = obs, fc = fc) - return(with(quant.obs.fc, - mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = TRUE) - spr.abs / 2., na.rm = TRUE))) +.calc.evmos.par <- function(quant.obs.fc){ + par.out <- rep(NA, 2) + par.out[2] <- with(quant.obs.fc, obs.sd / fc.sd) + par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = TRUE) + return(par.out) } - - +#Below are the core or elementary functions to calculate the functions necessary for the minimization of crps .calc.crps.opt <- function(par, quant.obs.fc){ return( with(quant.obs.fc, @@ -358,8 +348,6 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o ) ) } - - .calc.crps.grad.opt <- function(par, quant.obs.fc){ sgn1 <- with(quant.obs.fc,sign(obs.per.ens - (par[1] + par[2] * fc.ens.av.per.ens + @@ -378,8 +366,28 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o } +#Below are the core or elementary functions to correct the evaluation set based on the regression parameters +.correct.evmos.fc <- function(fc, par){ + quant.fc.mp <- .calc.fc.quant(fc = fc) + return(with(quant.fc.mp, par[1] + par[2] * fc)) +} +.correct.mse.min.fc <- function(fc, par){ + quant.fc.mp <- .calc.fc.quant(fc = fc) + return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * par[3])) +} +.correct.crps.min.fc <- function(fc, par){ + quant.fc.mp <- .calc.fc.quant.ext(fc = fc) + return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) +} + +#Auxiliary functions: +.calc.crps <- function(obs, fc){ #function to calculate the crps taking observation and forecast as input + quant.obs.fc <- .calc.obs.fc.quant.ext(obs = obs, fc = fc) + return(with(quant.obs.fc, + mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = TRUE) - spr.abs / 2., na.rm = TRUE))) +} -.spr <- function(x, amt.spr, dim = 1) { +.spr <- function(x, amt.spr, dim = 1) { #function to extend an array along one dimension (dim), amt.spr times if(is.vector(x)){ amt.dims <- 1 if(dim == 2){ @@ -406,5 +414,3 @@ CST_Calibration <- function(exp, obs, cal.method = "bias", eval.method = "take-o } return(arr.out) } - - diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd index 4248d5c0..434a203e 100644 --- a/man/CST_Calibration.Rd +++ b/man/CST_Calibration.Rd @@ -4,34 +4,34 @@ \alias{CST_Calibration} \title{Forecast Calibration} \usage{ -CST_Calibration(exp, obs, cal.method = "bias", - eval.method = "take-one-out", ...) +CST_Calibration(exp, obs, cal.method = "mse_min", + eval.method = "leave-one-out", ...) } \arguments{ \item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}.} \item{obs}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}.} -\item{cal.method}{is the calibration method used, can be either \code{"bias"}, \code{"cal"} or \code{"mbm_cal"}. Default value is \code{"bias"}.} +\item{cal.method}{is the calibration method used, can be either \code{"evmos"}, \code{"mse_min"} or \code{"crps_min"}. Default value is \code{"mse_min"}.} -\item{eval.method}{is the sampling method used, can be either \code{"in-sample"} or \code{"take-one-out"}. Default value is the \code{"take-one-out"} cross validation.} +\item{eval.method}{is the sampling method used, can be either \code{"in-sample"} or \code{"leave-one-out"}. Default value is the \code{"leave-one-out"} cross validation.} -\item{...}{other parameters to be passed on to the calibration procedure} +\item{...}{other parameters to be passed on to the calibration procedure.} } \value{ an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. } \description{ -Three types bias correction can be implemented. The first \code{"bias"} method simply subtracts the mean bias. +Three types of member-by-member bias correction can be performed. The \code{"evmos"} method applies a variance inflation technique to ensure the correction of the bias and the correspondence of variance between forecast and observation (Van Schaeybroeck and Vannitsem, 2015). The ensemble calibration methods \code{"mse_min"} and \code{"crps_min"} correct the bias, the overall forecast variance and the ensemble spread as described in Doblas-Reyes et al. (2005) and Van Schaeybroeck and Vannitsem (2015), respectively. While the \code{"mse_min"} method minimizes a constrained mean-squared error using three parameters, the \code{"crps_min"} method features four parameters and minimizes the Continuous Ranked Probability Score (CRPS). -The \code{"cal"} calibration method applies a variance inflation technique described in Doblas-Reyes et al. (2005) in leave-one-out cross-validation. This bias adjustment method produces calibrated forecasts with equivalent mean and variance to that of the reference dataset, but at the same time preserve reliability. - -The \code{"mbm_cal"} calibration method applies a member-by-member ensemble bias correction described in Van Schaeybroeck and Vannitsem (2015). The adjusted forecasts has an optimized CRPS score. This implies the (near) correspondence of 1) forecast mean with observational mean, 2) forecast variability with observational variability, 3) mean squared error with average ensemble variability such that the ensemble is reliable. +Both in-sample or our out-of-sample (leave-one-out cross validation) calibration are possible. } \references{ Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x -Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. +Van Schaeybroeck, B., & Vannitsem, S. (2011). Post-processing through linear regression. Nonlinear Processes in Geophysics, 18(2), 147. doi:10.5194/npg-18-147-2011 + +Van Schaeybroeck, B., & Vannitsem, S. (2015). Ensemble post‐processing using member‐by‐member approaches: theoretical aspects. Quarterly Journal of the Royal Meteorological Society, 141(688), 807-818. doi:10.1002/qj.2397 } \seealso{ \code{\link{CST_Load}} @@ -49,9 +49,11 @@ exp <- list(data = mod1, lat = lat, lon = lon) obs <- list(data = obs1, lat = lat, lon = lon) attr(exp, 'class') <- 's2dv_cube' attr(obs, 'class') <- 's2dv_cube' -a <- CST_Calibration(exp = exp, obs = obs, cal.method = "cal", eval.method = "in-sample") +a <- CST_Calibration(exp = exp, obs = obs, cal.method = "mse_min", eval.method = "in-sample") str(a) } \author{ -Verónica Torralba, \email{veronica.torralba@bsc.es} and Bert Van Schaeybroeck, \email{bertvs@meteo.be} +Verónica Torralba, \email{veronica.torralba@bsc.es} + +Bert Van Schaeybroeck, \email{bertvs@meteo.be} } -- GitLab From ddd5f2691d7dbf14c53bd9574f76feecd3659a1e Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Tue, 22 Oct 2019 11:59:58 +0200 Subject: [PATCH 38/46] two suggsetions of Veronica implemented --- R/CST_Calibration.R | 126 ++++++++++------------------------------- man/CST_Calibration.Rd | 4 +- 2 files changed, 34 insertions(+), 96 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index db508ac4..47edf8eb 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -12,6 +12,7 @@ #'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. #'@param cal.method is the calibration method used, can be either \code{"evmos"}, \code{"mse_min"} or \code{"crps_min"}. Default value is \code{"mse_min"}. #'@param eval.method is the sampling method used, can be either \code{"in-sample"} or \code{"leave-one-out"}. Default value is the \code{"leave-one-out"} cross validation. +#'@param multi.model is a boolean that is used for the \code{"mse_min"} method. If multi-model ensembles or ensembles of different sizes are used, it must be set to \code{"TRUE"}. By default it is \code{"FALSE"}. Differences between the two approaches are generally small but may become large when using small ensemble sizes. #'@param ... other parameters to be passed on to the calibration procedure. #'@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. #' @@ -38,7 +39,7 @@ #'@export -CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "leave-one-out", ...) { +CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "leave-one-out", multi.model = F, ...) { if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") @@ -49,7 +50,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea "of the parameter 'obs' must be equal to 1.") } - exp$data <- .calibration.wrap(exp = exp$data, obs = obs$data, cal.method = cal.method, eval.method = eval.method, ...) + exp$data <- .calibration.wrap(exp = exp$data, obs = obs$data, cal.method = cal.method, eval.method = eval.method, multi.model = multi.model,...) exp$Datasets <- c(exp$Datasets, obs$Datasets) exp$source_files <- c(exp$source_files, obs$source_files) return(exp) @@ -57,10 +58,6 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea } .make.eval.train.dexes <- function(eval.method, amt.points){ - if(amt.points < 10 & eval.method != "in-sample"){ - #cat("Too few points, so sample method will necessarily be in-sample") - eval.method <- "in-sample" - } if(eval.method == "leave-one-out"){ dexes.lst <- lapply(seq(1, amt.points), function(x) return(list(eval.dexes = x, train.dexes = seq(1, amt.points)[-x]))) } else if (eval.method == "in-sample"){ @@ -71,7 +68,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea return(dexes.lst) } -.cal <- function(obs.fc, cal.method, eval.method, ...) { +.cal <- function(obs.fc, cal.method, eval.method, multi.model,...) { dims.tmp=dim(obs.fc) amt.mbr <- dims.tmp["member"][] - 1 amt.sdate <- dims.tmp["sdate"][] @@ -106,9 +103,9 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea #calculate ensemble and observational characteristics quant.obs.fc.tr <- .calc.obs.fc.quant(obs = obs.tr, fc = fc.tr) #calculate value for regression parameters - init.par <- c(.calc.mse.min.par(quant.obs.fc.tr)) + init.par <- .calc.mse.min.par(quant.obs.fc.tr, multi.model) #correct evaluation subset - var.cor.fc[ , eval.dexes] <- .correct.mse.min.fc(fc.ev , init.par) + var.cor.fc[ , eval.dexes] <- .correct.mse.min.fc(fc.ev , init.par) } else if (cal.method == "crps_min"){ #calculate ensemble and observational characteristics quant.obs.fc.tr <- .calc.obs.fc.quant.ext(obs = obs.tr, fc = fc.tr) @@ -116,8 +113,8 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea init.par <- c(.calc.mse.min.par(quant.obs.fc.tr), 0.001) init.par[3] <- sqrt(init.par[3]) #calculate regression parameters on training dataset - optim.tmp <- optim(par = init.par, fn = .calc.crps.opt, gr = .calc.crps.grad.opt, - quant.obs.fc = quant.obs.fc.tr, method = "BFGS") + optim.tmp <- optim(par = init.par, fn = .calc.crps.opt, + quant.obs.fc = quant.obs.fc.tr, method = "Nelder-Mead") mbm.par <- optim.tmp$par #correct evaluation subset @@ -227,7 +224,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea .calc.obs.fc.quant <- function(obs, fc){ #function to calculate different quantities of a series of ensemble forecasts and corresponding observations amt.mbr <- dim(fc)[1] - obs.per.ens <- .spr(obs, amt.mbr) + obs.per.ens <- InsertDim(var = obs, posdim = 1, lendim = amt.mbr) fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) cor.obs.fc <- cor(fc.ens.av, obs, use = "complete.obs") obs.av <- mean(obs, na.rm = TRUE) @@ -247,11 +244,12 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea .calc.obs.fc.quant.ext <- function(obs, fc){ #extended function to calculate different quantities of a series of ensemble forecasts and corresponding observations amt.mbr <- dim(fc)[1] - obs.per.ens <- .spr(obs, amt.mbr) + obs.per.ens <- InsertDim(var = obs, posdim = 1, lendim = amt.mbr) fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) cor.obs.fc <- cor(fc.ens.av, obs, use = "complete.obs") obs.av <- mean(obs, na.rm = TRUE) obs.sd <- sd(obs, na.rm = TRUE) + return( append( .calc.fc.quant.ext(fc = fc), @@ -271,10 +269,11 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) fc.ens.av.sd <- sd(fc.ens.av, na.rm = TRUE) - fc.ens.av.per.ens <- .spr(fc.ens.av, amt.mbr) + fc.ens.av.per.ens <- InsertDim(var = fc.ens.av, posdim = 1, lendim = amt.mbr) fc.ens.sd <- apply(fc, c(2), sd, na.rm = TRUE) - fc.ens.sd.av <- sqrt(mean(fc.ens.sd^2,na.rm = TRUE)) + fc.ens.var.av.sqrt <- sqrt(mean(fc.ens.sd^2, na.rm = TRUE)) fc.dev <- fc - fc.ens.av.per.ens + fc.dev.sd <- sd(fc.dev, na.rm = TRUE) fc.av <- mean(fc, na.rm = TRUE) fc.sd <- sd(fc, na.rm = TRUE) return( @@ -284,52 +283,41 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea fc.ens.av.sd = fc.ens.av.sd, fc.ens.av.per.ens = fc.ens.av.per.ens, fc.ens.sd = fc.ens.sd, - fc.ens.sd.av = fc.ens.sd.av, + fc.ens.var.av.sqrt = fc.ens.var.av.sqrt, fc.dev = fc.dev, + fc.dev.sd = fc.dev.sd, fc.av = fc.av, fc.sd = fc.sd ) ) } + .calc.fc.quant.ext <- function(fc){ #extended function to calculate different quantities of a series of ensemble forecasts + amt.mbr <- dim(fc)[1] - fc.ens.av <- apply(fc, c(2), mean, na.rm = TRUE) - fc.ens.av.av <- mean(fc.ens.av, na.rm = TRUE) - fc.ens.av.sd <- sd(fc.ens.av, na.rm = TRUE) - fc.ens.av.per.ens <- .spr(fc.ens.av, amt.mbr) - fc.ens.sd <- apply(fc, c(2), sd, na.rm = TRUE) - fc.ens.sd.av <- sqrt(mean(fc.ens.sd^2, na.rm = TRUE)) - fc.dev <- fc - fc.ens.av.per.ens - repmat1.tmp <- .spr(fc, amt.mbr) + repmat1.tmp <- InsertDim(var = fc, posdim = 1, lendim = amt.mbr) repmat2.tmp <- aperm(repmat1.tmp, c(2, 1, 3)) spr.abs <- apply(abs(repmat1.tmp - repmat2.tmp), c(3), mean, na.rm = TRUE) - spr.abs.per.ens <- .spr(spr.abs, amt.mbr) - fc.av <- mean(fc, na.rm = TRUE) - fc.sd <- sd(fc, na.rm = TRUE) + spr.abs.per.ens <- InsertDim(var = spr.abs, posdim = 1, lendim = amt.mbr) + return( - list( - fc.ens.av = fc.ens.av, - fc.ens.av.av = fc.ens.av.av, - fc.ens.av.sd = fc.ens.av.sd, - fc.ens.av.per.ens = fc.ens.av.per.ens, - fc.ens.sd = fc.ens.sd, - fc.ens.sd.av = fc.ens.sd.av, - fc.dev = fc.dev, - spr.abs = spr.abs, - spr.abs.per.ens = spr.abs.per.ens, - fc.av = fc.av, - fc.sd = fc.sd - ) + append(.calc.fc.quant(fc), + ,list(spr.abs = spr.abs, spr.abs.per.ens = spr.abs.per.ens)) ) } - #Below are the core or elementary functions to calculate the regression parameters for the different methods -.calc.mse.min.par <- function(quant.obs.fc){ +.calc.mse.min.par <- function(quant.obs.fc, multi.model = F){ par.out <- rep(NA, 3) - par.out[3] <- with(quant.obs.fc, obs.sd * sqrt(1 - cor.obs.fc^2) / fc.ens.sd.av) + + if(multi.model){ + par.out[3] <- with(quant.obs.fc, obs.sd * sqrt(1. - cor.obs.fc^2) / fc.ens.var.av.sqrt) + } else { + par.out[3] <- with(quant.obs.fc, obs.sd * sqrt(1. - cor.obs.fc^2) / fc.dev.sd) + } par.out[2] <- with(quant.obs.fc, cor.obs.fc * obs.sd / fc.ens.av.sd) par.out[1] <- with(quant.obs.fc, obs.av - par.out[2] * fc.ens.av.av, na.rm = TRUE) + return(par.out) } .calc.evmos.par <- function(quant.obs.fc){ @@ -348,23 +336,6 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea ) ) } -.calc.crps.grad.opt <- function(par, quant.obs.fc){ - sgn1 <- with(quant.obs.fc,sign(obs.per.ens - - (par[1] + par[2] * fc.ens.av.per.ens + - ((par[3])^2 + par[4] / spr.abs.per.ens) * fc.dev))) - sgn2 <- with(quant.obs.fc, sign((par[3])^2 + par[4] / spr.abs.per.ens)) - sgn3 <- with(quant.obs.fc,sign((par[3])^2 * spr.abs + par[4])) - deriv.par1 <- mean(sgn1, na.rm = TRUE) - deriv.par2 <- with(quant.obs.fc, mean(sgn1 * fc.dev, na.rm = TRUE)) - deriv.par3 <- with(quant.obs.fc, - mean(2* par[3] * sgn1 * sgn2 * fc.ens.av.per.ens, na.rm = TRUE) - - mean(spr.abs * sgn3, na.rm = TRUE) / 2.) - deriv.par4 <- with(quant.obs.fc, - mean(sgn1 * sgn2 * fc.ens.av.per.ens / spr.abs.per.ens, na.rm = TRUE) - - mean(sgn3, na.rm = TRUE) / 2.) - return(c(deriv.par1, deriv.par2, deriv.par3, deriv.par4)) -} - #Below are the core or elementary functions to correct the evaluation set based on the regression parameters .correct.evmos.fc <- function(fc, par){ @@ -379,38 +350,3 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea quant.fc.mp <- .calc.fc.quant.ext(fc = fc) return(with(quant.fc.mp, par[1] + par[2] * fc.ens.av.per.ens + fc.dev * abs((par[3])^2 + par[4] / spr.abs))) } - -#Auxiliary functions: -.calc.crps <- function(obs, fc){ #function to calculate the crps taking observation and forecast as input - quant.obs.fc <- .calc.obs.fc.quant.ext(obs = obs, fc = fc) - return(with(quant.obs.fc, - mean(apply(abs(obs.per.ens - fc), c(2), mean, na.rm = TRUE) - spr.abs / 2., na.rm = TRUE))) -} - -.spr <- function(x, amt.spr, dim = 1) { #function to extend an array along one dimension (dim), amt.spr times - if(is.vector(x)){ - amt.dims <- 1 - if(dim == 2){ - arr.out <- array(rep(x, amt.spr), c(length(x), amt.spr)) - } else if(dim == 1){ - arr.out <- t(array(rep(x, amt.spr), c(length(x), amt.spr))) - } else { - stop(paste0("error in .spr: amt.dims = ",amt.dims," while dim = ",dim)) - } - } else if(is.array(x)) { - amt.dims <- length(dim(x)) - if(dim > amt.dims + 1){ - stop(paste0("error in .spr: amt.dims = ",amt.dims," while dim = ",dim)) - } - arr.out <- array(rep(as.vector(x), amt.spr), c(dim(x), amt.spr)) - if(dim != amt.dims + 1){ - amt.dims.out <- amt.dims + 1 - dims.tmp <- seq(1, amt.dims.out) - dims.tmp[seq(dim, amt.dims.out)] <- c(amt.dims.out, seq(dim,amt.dims.out-1)) - arr.out <- aperm(arr.out, dims.tmp) - } - } else { - stop("x is not array nor vector but is ", class(x)) - } - return(arr.out) -} diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd index 434a203e..18daeaa6 100644 --- a/man/CST_Calibration.Rd +++ b/man/CST_Calibration.Rd @@ -5,7 +5,7 @@ \title{Forecast Calibration} \usage{ CST_Calibration(exp, obs, cal.method = "mse_min", - eval.method = "leave-one-out", ...) + eval.method = "leave-one-out", multi.model = F, ...) } \arguments{ \item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}.} @@ -16,6 +16,8 @@ CST_Calibration(exp, obs, cal.method = "mse_min", \item{eval.method}{is the sampling method used, can be either \code{"in-sample"} or \code{"leave-one-out"}. Default value is the \code{"leave-one-out"} cross validation.} +\item{multi.model}{is a boolean that is used for the \code{"mse_min"} method. If multi-model ensembles or ensembles of different sizes are used, it must be set to \code{"TRUE"}. By default it is \code{"FALSE"}. Differences between the two approaches are generally small but may become large when using small ensemble sizes.} + \item{...}{other parameters to be passed on to the calibration procedure.} } \value{ -- GitLab From 45732f333bc405c0b8027b3156d4a1ebc43ce856 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Wed, 23 Oct 2019 12:16:50 +0200 Subject: [PATCH 39/46] test --- R/CST_Calibration.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index 47edf8eb..ec7c7411 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -158,8 +158,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea return.feat <- list(dim = c(amt.member, amt.sdate)) return.feat$name <- c("member", "sdate") - return.feat$dim.name <- list(dimnames(exp)[["member"]],dimnames(exp)[["sdate"]]) - + return.feat$dim.name <- list(dimnames(exp)[["member"]],dimnames(exp)[["sdate"]]) calibrated <- .apply.obs.fc(obs = obs, fc = exp, target.dims = target.dims, @@ -178,7 +177,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea dimnames.tmp <- dimnames(fc) fc.dims.tmp <- dim(fc) dims.out.tmp <- return.feat$dim - + obs.fc <- .combine.obs.fc(obs, fc) names.dim <- names(dim(obs.fc)) amt.dims <- length(names.dim) -- GitLab From c1851bb838e91828763b2c7f106cf882ef648df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ver=C3=B3nica=20Torralba-Fern=C3=A1ndez?= Date: Wed, 23 Oct 2019 16:11:59 +0200 Subject: [PATCH 40/46] bugfix: removing an extra comma that produced an error when the crps_min method is applied --- R/CST_Calibration.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index 47edf8eb..b651ae01 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -302,7 +302,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea return( append(.calc.fc.quant(fc), - ,list(spr.abs = spr.abs, spr.abs.per.ens = spr.abs.per.ens)) + list(spr.abs = spr.abs, spr.abs.per.ens = spr.abs.per.ens)) ) } -- GitLab From 34d63ee1d2b0d7167dfffa28c076f2690495ff0e Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Fri, 25 Oct 2019 13:37:01 +0200 Subject: [PATCH 41/46] =?UTF-8?q?Suggestions=20of=20Ver=C3=B2nica=20implem?= =?UTF-8?q?ented.=20More=20specifically:=201)=20implementation=20of=20mult?= =?UTF-8?q?iApply=20as=20wrapper,=202)=20removal=20of=20...=203)=20warning?= =?UTF-8?q?=20when=20multi.model=20is=20used,=204)=20adaptation=20of=20the?= =?UTF-8?q?=20manual=20by=20addition=20of=20bias=20method,=20discussion=20?= =?UTF-8?q?of=20use=20of=20multi.model=20boolean,=20change=20of=20adequate?= =?UTF-8?q?=20reference=20to=202011=20paper=20for=20evmos=20method.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/CST_Calibration.R | 191 +++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 111 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index ec7c7411..e0c443f1 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -2,7 +2,7 @@ #' #'@author Verónica Torralba, \email{veronica.torralba@bsc.es} #'@author Bert Van Schaeybroeck, \email{bertvs@meteo.be} -#'@description Three types of member-by-member bias correction can be performed. The \code{"evmos"} method applies a variance inflation technique to ensure the correction of the bias and the correspondence of variance between forecast and observation (Van Schaeybroeck and Vannitsem, 2015). The ensemble calibration methods \code{"mse_min"} and \code{"crps_min"} correct the bias, the overall forecast variance and the ensemble spread as described in Doblas-Reyes et al. (2005) and Van Schaeybroeck and Vannitsem (2015), respectively. While the \code{"mse_min"} method minimizes a constrained mean-squared error using three parameters, the \code{"crps_min"} method features four parameters and minimizes the Continuous Ranked Probability Score (CRPS). +#'@description Four types of member-by-member bias correction can be performed. The \code{bias} method corrects the bias only, the \code{evmos} method applies a variance inflation technique to ensure the correction of the bias and the correspondence of variance between forecast and observation (Van Schaeybroeck and Vannitsem, 2011). The ensemble calibration methods \code{"mse_min"} and \code{"crps_min"} correct the bias, the overall forecast variance and the ensemble spread as described in Doblas-Reyes et al. (2005) and Van Schaeybroeck and Vannitsem (2015), respectively. While the \code{"mse_min"} method minimizes a constrained mean-squared error using three parameters, the \code{"crps_min"} method features four parameters and minimizes the Continuous Ranked Probability Score (CRPS). #'@description Both in-sample or our out-of-sample (leave-one-out cross validation) calibration are possible. #'@references Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x #'@references Van Schaeybroeck, B., & Vannitsem, S. (2011). Post-processing through linear regression. Nonlinear Processes in Geophysics, 18(2), 147. doi:10.5194/npg-18-147-2011 @@ -10,10 +10,9 @@ #' #'@param exp an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}. #'@param obs an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}. -#'@param cal.method is the calibration method used, can be either \code{"evmos"}, \code{"mse_min"} or \code{"crps_min"}. Default value is \code{"mse_min"}. -#'@param eval.method is the sampling method used, can be either \code{"in-sample"} or \code{"leave-one-out"}. Default value is the \code{"leave-one-out"} cross validation. -#'@param multi.model is a boolean that is used for the \code{"mse_min"} method. If multi-model ensembles or ensembles of different sizes are used, it must be set to \code{"TRUE"}. By default it is \code{"FALSE"}. Differences between the two approaches are generally small but may become large when using small ensemble sizes. -#'@param ... other parameters to be passed on to the calibration procedure. +#'@param cal.method is the calibration method used, can be either \code{bias}, \code{evmos}, \code{mse_min} or \code{crps_min}. Default value is \code{mse_min}. +#'@param eval.method is the sampling method used, can be either \code{in-sample} or \code{leave-one-out}. Default value is the \code{leave-one-out} cross validation. +#'@param multi.model is a boolean that is used only for the \code{mse_min} method. If multi-model ensembles or ensembles of different sizes are used, it must be set to \code{TRUE}. By default it is \code{FALSE}. Differences between the two approaches are generally small but may become large when using small ensemble sizes. Using multi.model when the calibration method is \code{bias}, \code{evmos} or \code{crps_min} will not affect the result. #'@return an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. #' #'@import s2dverification @@ -39,7 +38,7 @@ #'@export -CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "leave-one-out", multi.model = F, ...) { +CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "leave-one-out", multi.model = F) { if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") @@ -49,14 +48,81 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea stop("The length of the dimension 'member' in the component 'data' ", "of the parameter 'obs' must be equal to 1.") } - - exp$data <- .calibration.wrap(exp = exp$data, obs = obs$data, cal.method = cal.method, eval.method = eval.method, multi.model = multi.model,...) + + exp$data <- .calibration.wrap(exp = exp$data, obs = obs$data, + cal.method = cal.method, eval.method = eval.method, + multi.model = multi.model) + exp$Datasets <- c(exp$Datasets, obs$Datasets) exp$source_files <- c(exp$source_files, obs$source_files) return(exp) } +.calibration.wrap <- function(exp, obs, cal.method, eval.method, multi.model) { + + dim.exp <- dim(exp) + amt.dims.exp <- length(dim.exp) + dim.obs <- dim(obs) + amt.dims.obs <- length(dim.obs) + + dim.names.exp <- names(dim.exp) + dim.names.obs <- names(dim.obs) + + target.dim.names.exp <- c("member", "sdate") + target.dim.names.obs <- c("sdate") + + if (!all(target.dim.names.exp %in% dim.names.exp)) { + stop("Parameter 'exp' must have the dimensions 'member' and 'sdate'.") + } + + if (!all(c("sdate") %in% dim.names.obs)) { + stop("Parameter 'obs' must have the dimension 'sdate'.") + } + + if (any(is.na(exp))) { + warning("Parameter 'exp' contains NA values.") + } + + if (any(is.na(obs))) { + warning("Parameter 'obs' contains NA values.") + } + + if ("member" %in% dim.names.obs) { + target.dim.names.obs <- c("member", target.dim.names.obs) + } + + amt.member=dim.exp["member"] + amt.sdate=dim.exp["sdate"] + + target.dim.dex.exp <- match(target.dim.names.exp, dim.names.exp) + margin.dim.dex.exp <- seq(1, amt.dims.exp)[-target.dim.dex.exp] + margin.dim.names.exp <- dim.names.exp[margin.dim.dex.exp] + + target.dim.dex.obs <- match(target.dim.names.obs, dim.names.obs) + margin.dim.dex.obs <- seq(1, amt.dims.obs)[-target.dim.dex.obs] + margin.dim.names.obs <- dim.names.obs[margin.dim.dex.obs] + margin.dim.dex.obs <- match(margin.dim.names.exp, dim.names.obs) + + exp.order <- aperm(exp, c(target.dim.dex.exp, margin.dim.dex.exp)) + + obs.order <- aperm(obs, c(target.dim.dex.obs, margin.dim.dex.obs)) + + calibrated <- Apply(data = list(obs = obs.order, mod = exp.order), + cal.method = cal.method, + eval.method = eval.method, + multi.model = multi.model, + target_dims = list(target.dim.names.obs, target.dim.names.exp), + fun = .cal)$output1 + +# use_attributes = list(mod = c("dimnames")), + + dimnames(calibrated) <- dimnames(exp.order) + calibrated <- aperm(calibrated, match(dim.names.exp, names(dim(calibrated)))) + + return(calibrated) +} + .make.eval.train.dexes <- function(eval.method, amt.points){ if(eval.method == "leave-one-out"){ dexes.lst <- lapply(seq(1, amt.points), function(x) return(list(eval.dexes = x, train.dexes = seq(1, amt.points)[-x]))) @@ -68,15 +134,12 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea return(dexes.lst) } -.cal <- function(obs.fc, cal.method, eval.method, multi.model,...) { - dims.tmp=dim(obs.fc) - amt.mbr <- dims.tmp["member"][] - 1 - amt.sdate <- dims.tmp["sdate"][] - pos <- match(c("member","sdate"), names(dims.tmp)) - obs.fc <- aperm(obs.fc, pos) - var.obs <- asub(obs.fc, list(1),1) - var.fc <- asub(obs.fc, list(1+seq(1, amt.mbr)),1) +.cal <- function(obs, mod, cal.method, eval.method, multi.model) { + var.obs <- obs + var.fc <- mod dims.fc <- dim(var.fc) + amt.mbr <- dims.fc[1] + amt.sdate <- dims.fc[2] var.cor.fc <- NA * var.fc eval.train.dexeses <- .make.eval.train.dexes(eval.method, amt.points = amt.sdate) @@ -127,100 +190,6 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea return(var.cor.fc) } - - -.calibration.wrap <- function(exp, obs, cal.method, eval.method, ...) { - target.dims <- c("member", "sdate") - if (!all(target.dims %in% names(dim(exp)))) { - stop("Parameter 'exp' must have the dimensions 'member' and 'sdate'.") - } - - if (!all(c("sdate") %in% names(dim(obs)))) { - stop("Parameter 'obs' must have the dimension 'sdate'.") - } - - if (any(is.na(exp))) { - warning("Parameter 'exp' contains NA values.") - } - - if (any(is.na(obs))) { - warning("Parameter 'obs' contains NA values.") - } - - target_dims_obs <- "sdate" - if ("member" %in% names(dim(obs))) { - target_dims_obs <- c("member", target_dims_obs) - } - - amt.member=dim(exp)["member"] - amt.sdate=dim(exp)["sdate"] - target.dims <- c("member", "sdate") - - return.feat <- list(dim = c(amt.member, amt.sdate)) - return.feat$name <- c("member", "sdate") - return.feat$dim.name <- list(dimnames(exp)[["member"]],dimnames(exp)[["sdate"]]) - calibrated <- .apply.obs.fc(obs = obs, - fc = exp, - target.dims = target.dims, - return.feat = return.feat, - FUN = .cal, - cal.method = cal.method, - eval.method = eval.method, - ...) - - return(calibrated) -} - - - -.apply.obs.fc <- function(obs, fc, target.dims, FUN, return.feat, ...){ #wrapper function that combines obs and fc into one array and applies a function on it - dimnames.tmp <- dimnames(fc) - fc.dims.tmp <- dim(fc) - dims.out.tmp <- return.feat$dim - - obs.fc <- .combine.obs.fc(obs, fc) - names.dim <- names(dim(obs.fc)) - amt.dims <- length(names.dim) - margin.all <- seq(1, amt.dims) - matched.dims <- match(target.dims, names.dim) - margin.to.use <- margin.all[-matched.dims] - arr.out <- apply(X = obs.fc, - MARGIN = margin.to.use, - FUN = FUN, - ...) - dims.tmp <- dim(arr.out) - names.dims.tmp <- names(dim(arr.out)) - if(prod(return.feat$dim) != dims.tmp[1]){ - stop("apply.obs.fc: returned dimensions not as expected: ", prod(return.feat$dim), " and ", dims.tmp[1]) - } - dim(arr.out) <- c(dims.out.tmp, dims.tmp[-1]) - names(dim(arr.out)) <- c(return.feat$name, names.dims.tmp[c(-1)]) - names.dim[matched.dims] <- return.feat$name - pos <- match(names.dim, names(dim(arr.out))) - pos_inv <- match(names(dim(arr.out)), names.dim) - arr.out <- aperm(arr.out, pos) - for (i.item in seq(1,length(return.feat$name))){ - dimnames.tmp[[pos_inv[i.item]]] <- return.feat$dim.name[[i.item]] - } - dimnames(arr.out) <- dimnames.tmp - return(arr.out) -} - - - -.combine.obs.fc <- function(obs,fc){ #function to combine two arrays (obs and fc) into one, in order to be able to use with the apply function - names.dim.tmp <- names(dim(obs)) - members.dim <- which(names.dim.tmp == "member") - arr.out <- abind(obs, fc, along = members.dim) - dimnames.tmp <- dimnames(arr.out) - names(dim(arr.out)) <- names.dim.tmp - dimnames(arr.out) <- dimnames.tmp - names(dimnames(arr.out)) <- names.dim.tmp - return(arr.out) -} - - - .calc.obs.fc.quant <- function(obs, fc){ #function to calculate different quantities of a series of ensemble forecasts and corresponding observations amt.mbr <- dim(fc)[1] obs.per.ens <- InsertDim(var = obs, posdim = 1, lendim = amt.mbr) @@ -301,7 +270,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea return( append(.calc.fc.quant(fc), - ,list(spr.abs = spr.abs, spr.abs.per.ens = spr.abs.per.ens)) + list(spr.abs = spr.abs, spr.abs.per.ens = spr.abs.per.ens)) ) } -- GitLab From fd79dbc5a06fe8648a9ceda417a03daead0e1586 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Fri, 25 Oct 2019 13:51:19 +0200 Subject: [PATCH 42/46] Warning message introduced concerning multi.model parameter --- R/CST_Calibration.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index e0c443f1..ca68a4f7 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -49,6 +49,9 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea "of the parameter 'obs' must be equal to 1.") } + if(!missing(multi.model) & !(cal.method == "mse_min")){ + warning(paste0("The multi.model parameter is ignored when using the calibration method ", cal.method)) + } exp$data <- .calibration.wrap(exp = exp$data, obs = obs$data, cal.method = cal.method, eval.method = eval.method, multi.model = multi.model) -- GitLab From 8d1df340e0c7b028c775de611855464a9ed2d5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ver=C3=B3nica=20Torralba-Fern=C3=A1ndez?= Date: Tue, 29 Oct 2019 17:13:04 +0100 Subject: [PATCH 43/46] bugfix function names --- R/CST_Calibration.R | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index ca68a4f7..e3185c15 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -68,7 +68,6 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea amt.dims.exp <- length(dim.exp) dim.obs <- dim(obs) amt.dims.obs <- length(dim.obs) - dim.names.exp <- names(dim.exp) dim.names.obs <- names(dim.obs) @@ -91,38 +90,29 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea warning("Parameter 'obs' contains NA values.") } - if ("member" %in% dim.names.obs) { - target.dim.names.obs <- c("member", target.dim.names.obs) + if (dim(obs)['member']!=1){ + stop("Parameter 'obs' must have a member dimension with length=1") } - amt.member=dim.exp["member"] - amt.sdate=dim.exp["sdate"] - - target.dim.dex.exp <- match(target.dim.names.exp, dim.names.exp) - margin.dim.dex.exp <- seq(1, amt.dims.exp)[-target.dim.dex.exp] - margin.dim.names.exp <- dim.names.exp[margin.dim.dex.exp] - - target.dim.dex.obs <- match(target.dim.names.obs, dim.names.obs) - margin.dim.dex.obs <- seq(1, amt.dims.obs)[-target.dim.dex.obs] - margin.dim.names.obs <- dim.names.obs[margin.dim.dex.obs] - margin.dim.dex.obs <- match(margin.dim.names.exp, dim.names.obs) + obs<-Subset(obs,along='member',indices=1,drop='selected') - exp.order <- aperm(exp, c(target.dim.dex.exp, margin.dim.dex.exp)) - - obs.order <- aperm(obs, c(target.dim.dex.obs, margin.dim.dex.obs)) + target.dims.exp <- c (member = 1, sdate = 1, dataset = 1, ftime = 1, lat = 1, lon = 1) + target.dims.obs <- c (sdate = 1, dataset = 1, ftime = 1, lat = 1, lon = 1) + target.dims.exp[names(dim(exp))] <- dim(exp) + target.dims.obs[names(dim(obs))] <- dim(obs) + dim(exp)<-target.dims.exp + dim(obs)<-target.dims.obs - calibrated <- Apply(data = list(obs = obs.order, mod = exp.order), + calibrated <- Apply(data = list(obs = obs, mod = exp), cal.method = cal.method, eval.method = eval.method, multi.model = multi.model, target_dims = list(target.dim.names.obs, target.dim.names.exp), fun = .cal)$output1 - -# use_attributes = list(mod = c("dimnames")), - - dimnames(calibrated) <- dimnames(exp.order) - calibrated <- aperm(calibrated, match(dim.names.exp, names(dim(calibrated)))) - + + target.dims.cal <- c (dataset = 1,member = 1, sdate = 1, ftime = 1, lat = 1, lon = 1) + target.dims.cal[names(dim(calibrated))] <- dim(calibrated) + dim(calibrated)<-target.dims.cal return(calibrated) } -- GitLab From 70c2cecb56400c021cbab03e9e582a76340c8741 Mon Sep 17 00:00:00 2001 From: Bert Van schaeybroeck Date: Fri, 8 Nov 2019 09:56:20 +0100 Subject: [PATCH 44/46] =?UTF-8?q?Removing=20aperm=20manipulations=20before?= =?UTF-8?q?=20the=20Apply=20function=20in=20CST=5FCalibration.R=20and=20pa?= =?UTF-8?q?rtly=20reversing=20corrections=20of=20Ver=C3=B3nica.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/CST_Calibration.R | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index e3185c15..a106607a 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -94,25 +94,20 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea stop("Parameter 'obs' must have a member dimension with length=1") } - obs<-Subset(obs,along='member',indices=1,drop='selected') - - target.dims.exp <- c (member = 1, sdate = 1, dataset = 1, ftime = 1, lat = 1, lon = 1) - target.dims.obs <- c (sdate = 1, dataset = 1, ftime = 1, lat = 1, lon = 1) - target.dims.exp[names(dim(exp))] <- dim(exp) - target.dims.obs[names(dim(obs))] <- dim(obs) - dim(exp)<-target.dims.exp - dim(obs)<-target.dims.obs - - calibrated <- Apply(data = list(obs = obs, mod = exp), + obs <- Subset(obs, along = "member", indices = 1, drop = "selected") + + calibrated <- Apply(data = list(mod = exp, obs = obs), cal.method = cal.method, eval.method = eval.method, multi.model = multi.model, - target_dims = list(target.dim.names.obs, target.dim.names.exp), + target_dims = list(mod = target.dim.names.exp, obs = target.dim.names.obs), fun = .cal)$output1 - - target.dims.cal <- c (dataset = 1,member = 1, sdate = 1, ftime = 1, lat = 1, lon = 1) - target.dims.cal[names(dim(calibrated))] <- dim(calibrated) - dim(calibrated)<-target.dims.cal + + dexes <- match(names(dim(exp)), names(dim(calibrated))) + calibrated <- aperm(calibrated, dexes) + dimnames(calibrated) <- dimnames(exp)[dexes] + + return(calibrated) } @@ -127,8 +122,9 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "lea return(dexes.lst) } -.cal <- function(obs, mod, cal.method, eval.method, multi.model) { - var.obs <- obs +.cal <- function(mod, obs, cal.method, eval.method, multi.model) { + + var.obs <- as.vector(obs) var.fc <- mod dims.fc <- dim(var.fc) amt.mbr <- dims.fc[1] -- GitLab From 6d2bf79adfdb4723cb7ac4c6a21a8014d6e2b2e0 Mon Sep 17 00:00:00 2001 From: nperez Date: Tue, 12 Nov 2019 17:27:11 +0100 Subject: [PATCH 45/46] fix in checking of obs inherits --- R/CST_BiasCorrection.R | 2 +- R/CST_Calibration.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/CST_BiasCorrection.R b/R/CST_BiasCorrection.R index bd1b6d36..89821dca 100644 --- a/R/CST_BiasCorrection.R +++ b/R/CST_BiasCorrection.R @@ -31,7 +31,7 @@ #'str(a) #'@export CST_BiasCorrection <- function(exp, obs) { - if (!inherits(exp, 's2dv_cube') || !inherits(exp, 's2dv_cube')) { + if (!inherits(exp, 's2dv_cube') || !inherits(obs, 's2dv_cube')) { stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") } diff --git a/R/CST_Calibration.R b/R/CST_Calibration.R index a106607a..c63dbb83 100644 --- a/R/CST_Calibration.R +++ b/R/CST_Calibration.R @@ -39,7 +39,7 @@ CST_Calibration <- function(exp, obs, cal.method = "mse_min", eval.method = "leave-one-out", multi.model = F) { - if (!inherits(exp, "s2dv_cube") || !inherits(exp, "s2dv_cube")) { + if (!inherits(exp, "s2dv_cube") || !inherits(obs, "s2dv_cube")) { stop("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ", "as output by CSTools::CST_Load.") } -- GitLab From 057846903f892800d9d74e97ce69552595f2c833 Mon Sep 17 00:00:00 2001 From: nperez Date: Tue, 12 Nov 2019 17:48:22 +0100 Subject: [PATCH 46/46] documentation updated with roxyen 5.0.0 --- DESCRIPTION | 4 ++-- NAMESPACE | 2 +- man/CST_Anomaly.Rd | 7 ++++--- man/CST_BiasCorrection.Rd | 8 +++++--- man/CST_Calibration.Rd | 23 +++++++++++------------ man/CST_Load.Rd | 1 + man/CST_MultiMetric.Rd | 9 +++++---- man/CST_MultivarRMSE.Rd | 7 ++++--- man/CST_RFSlope.Rd | 1 + man/CST_RFWeights.Rd | 7 ++++--- man/CST_RainFARM.Rd | 7 ++++--- man/PlotCombinedMap.Rd | 15 ++++++++------- man/PlotForecastPDF.Rd | 10 +++++----- man/PlotMostLikelyQuantileMap.Rd | 7 ++++--- man/RFSlope.Rd | 1 + man/RainFARM.Rd | 8 ++++---- man/areave_data.Rd | 1 + man/lonlat_data.Rd | 1 + man/lonlat_prec.Rd | 1 + 19 files changed, 67 insertions(+), 53 deletions(-) mode change 100755 => 100644 DESCRIPTION diff --git a/DESCRIPTION b/DESCRIPTION old mode 100755 new mode 100644 index 0ad55aae..3e9a394a --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,7 +20,7 @@ Description: Exploits dynamical seasonal forecasts in order to provide contains process-based methods for forecast calibration, bias correction, statistical and stochastic downscaling, optimal forecast combination and multivariate verification, as well as basic and advanced tools to obtain - tailored products. This package was developed in the context of the + tailored products. This package was developed in the context of the ERA4CS project MEDSCOPE and the H2020 S2S4E project. Doblas-Reyes et al. (2005) . Mishra et al. (2018) . @@ -54,4 +54,4 @@ VignetteBuilder: knitr License: Apache License 2.0 Encoding: UTF-8 LazyData: true -RoxygenNote: 6.1.1 +RoxygenNote: 5.0.0 diff --git a/NAMESPACE b/NAMESPACE index 833f8764..f57c5ac4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,9 +14,9 @@ export(PlotForecastPDF) export(PlotMostLikelyQuantileMap) export(RFSlope) export(RainFARM) -import(abind) export(as.s2dv_cube) export(s2dv_cube) +import(abind) import(ggplot2) import(multiApply) import(ncdf4) diff --git a/man/CST_Anomaly.Rd b/man/CST_Anomaly.Rd index b214a31a..e1c31f0c 100644 --- a/man/CST_Anomaly.Rd +++ b/man/CST_Anomaly.Rd @@ -53,12 +53,13 @@ str(anom3) anom4 <- CST_Anomaly(exp = exp, obs = obs, cross = FALSE, memb = FALSE) str(anom4) -} -\seealso{ -\code{\link[s2dverification]{Ano_CrossValid}}, \code{\link[s2dverification]{Clim}} and \code{\link{CST_Load}} } \author{ Perez-Zanon Nuria, \email{nuria.perez@bsc.es} Pena Jesus, \email{jesus.pena@bsc.es} } +\seealso{ +\code{\link[s2dverification]{Ano_CrossValid}}, \code{\link[s2dverification]{Clim}} and \code{\link{CST_Load}} +} + diff --git a/man/CST_BiasCorrection.Rd b/man/CST_BiasCorrection.Rd index a1b415fb..75207e2e 100644 --- a/man/CST_BiasCorrection.Rd +++ b/man/CST_BiasCorrection.Rd @@ -35,9 +35,11 @@ attr(obs, 'class') <- 's2dv_cube' a <- CST_BiasCorrection(exp = exp, obs = obs) str(a) } -\references{ -Torralba, V., F.J. Doblas-Reyes, D. MacLeod, I. Christel and M. Davis (2017). Seasonal climate prediction: a new source of information for the management of wind energy resources. Journal of Applied Meteorology and Climatology, 56, 1231-1247, doi:10.1175/JAMC-D-16-0204.1. (CLIM4ENERGY, EUPORIAS, NEWA, RESILIENCE, SPECS) -} \author{ Verónica Torralba, \email{veronica.torralba@bsc.es} } +\references{ +Torralba, V., F.J. Doblas-Reyes, D. MacLeod, I. Christel and M. Davis (2017). Seasonal climate prediction: a new source of information for the management of wind energy resources. Journal of Applied Meteorology and Climatology, 56, 1231-1247, doi:10.1175/JAMC-D-16-0204.1. (CLIM4ENERGY, EUPORIAS, NEWA, RESILIENCE, SPECS) +} +\encoding{UTF-8} + diff --git a/man/CST_Calibration.Rd b/man/CST_Calibration.Rd index 18daeaa6..0e6a6a54 100644 --- a/man/CST_Calibration.Rd +++ b/man/CST_Calibration.Rd @@ -5,29 +5,32 @@ \title{Forecast Calibration} \usage{ CST_Calibration(exp, obs, cal.method = "mse_min", - eval.method = "leave-one-out", multi.model = F, ...) + eval.method = "leave-one-out", multi.model = F) } \arguments{ \item{exp}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the seasonal forecast experiment data in the element named \code{$data}.} \item{obs}{an object of class \code{s2dv_cube} as returned by \code{CST_Load} function, containing the observed data in the element named \code{$data}.} -\item{cal.method}{is the calibration method used, can be either \code{"evmos"}, \code{"mse_min"} or \code{"crps_min"}. Default value is \code{"mse_min"}.} +\item{cal.method}{is the calibration method used, can be either \code{bias}, \code{evmos}, \code{mse_min} or \code{crps_min}. Default value is \code{mse_min}.} -\item{eval.method}{is the sampling method used, can be either \code{"in-sample"} or \code{"leave-one-out"}. Default value is the \code{"leave-one-out"} cross validation.} +\item{eval.method}{is the sampling method used, can be either \code{in-sample} or \code{leave-one-out}. Default value is the \code{leave-one-out} cross validation.} -\item{multi.model}{is a boolean that is used for the \code{"mse_min"} method. If multi-model ensembles or ensembles of different sizes are used, it must be set to \code{"TRUE"}. By default it is \code{"FALSE"}. Differences between the two approaches are generally small but may become large when using small ensemble sizes.} - -\item{...}{other parameters to be passed on to the calibration procedure.} +\item{multi.model}{is a boolean that is used only for the \code{mse_min} method. If multi-model ensembles or ensembles of different sizes are used, it must be set to \code{TRUE}. By default it is \code{FALSE}. Differences between the two approaches are generally small but may become large when using small ensemble sizes. Using multi.model when the calibration method is \code{bias}, \code{evmos} or \code{crps_min} will not affect the result.} } \value{ an object of class \code{s2dv_cube} containing the calibrated forecasts in the element \code{$data} with the same dimensions of the experimental data. } \description{ -Three types of member-by-member bias correction can be performed. The \code{"evmos"} method applies a variance inflation technique to ensure the correction of the bias and the correspondence of variance between forecast and observation (Van Schaeybroeck and Vannitsem, 2015). The ensemble calibration methods \code{"mse_min"} and \code{"crps_min"} correct the bias, the overall forecast variance and the ensemble spread as described in Doblas-Reyes et al. (2005) and Van Schaeybroeck and Vannitsem (2015), respectively. While the \code{"mse_min"} method minimizes a constrained mean-squared error using three parameters, the \code{"crps_min"} method features four parameters and minimizes the Continuous Ranked Probability Score (CRPS). +Four types of member-by-member bias correction can be performed. The \code{bias} method corrects the bias only, the \code{evmos} method applies a variance inflation technique to ensure the correction of the bias and the correspondence of variance between forecast and observation (Van Schaeybroeck and Vannitsem, 2011). The ensemble calibration methods \code{"mse_min"} and \code{"crps_min"} correct the bias, the overall forecast variance and the ensemble spread as described in Doblas-Reyes et al. (2005) and Van Schaeybroeck and Vannitsem (2015), respectively. While the \code{"mse_min"} method minimizes a constrained mean-squared error using three parameters, the \code{"crps_min"} method features four parameters and minimizes the Continuous Ranked Probability Score (CRPS). Both in-sample or our out-of-sample (leave-one-out cross validation) calibration are possible. } +\author{ +Verónica Torralba, \email{veronica.torralba@bsc.es} + +Bert Van Schaeybroeck, \email{bertvs@meteo.be} +} \references{ Doblas-Reyes F.J, Hagedorn R, Palmer T.N. The rationale behind the success of multi-model ensembles in seasonal forecasting-II calibration and combination. Tellus A. 2005;57:234-252. doi:10.1111/j.1600-0870.2005.00104.x @@ -54,8 +57,4 @@ attr(obs, 'class') <- 's2dv_cube' a <- CST_Calibration(exp = exp, obs = obs, cal.method = "mse_min", eval.method = "in-sample") str(a) } -\author{ -Verónica Torralba, \email{veronica.torralba@bsc.es} - -Bert Van Schaeybroeck, \email{bertvs@meteo.be} -} +\encoding{UTF-8} diff --git a/man/CST_Load.Rd b/man/CST_Load.Rd index bf03ba42..1fee022c 100644 --- a/man/CST_Load.Rd +++ b/man/CST_Load.Rd @@ -47,3 +47,4 @@ obs <- CSTools::lonlat_data$obs \author{ Nicolau Manubens, \email{nicolau.manubens@bsc.es} } + diff --git a/man/CST_MultiMetric.Rd b/man/CST_MultiMetric.Rd index 8e3ce593..079a5588 100644 --- a/man/CST_MultiMetric.Rd +++ b/man/CST_MultiMetric.Rd @@ -37,14 +37,15 @@ c(ano_exp, ano_obs) \%<-\% CST_Anomaly(exp = exp, obs = obs, cross = TRUE, memb a <- CST_MultiMetric(exp = ano_exp, obs = ano_obs) str(a) } +\author{ +Mishra Niti, \email{niti.mishra@bsc.es} + +Perez-Zanon Nuria, \email{nuria.perez@bsc.es} +} \references{ Mishra, N., Prodhomme, C., & Guemas, V. (n.d.). Multi-Model Skill Assessment of Seasonal Temperature and Precipitation Forecasts over Europe, 29-31.\url{http://link.springer.com/10.1007/s00382-018-4404-z} } \seealso{ \code{\link[s2dverification]{Corr}}, \code{\link[s2dverification]{RMS}}, \code{\link[s2dverification]{RMSSS}} and \code{\link{CST_Load}} } -\author{ -Mishra Niti, \email{niti.mishra@bsc.es} -Perez-Zanon Nuria, \email{nuria.perez@bsc.es} -} diff --git a/man/CST_MultivarRMSE.Rd b/man/CST_MultivarRMSE.Rd index 24af608c..685eaf77 100644 --- a/man/CST_MultivarRMSE.Rd +++ b/man/CST_MultivarRMSE.Rd @@ -56,9 +56,10 @@ weight <- c(1, 2) a <- CST_MultivarRMSE(exp = ano_exp, obs = ano_obs, weight = weight) str(a) } -\seealso{ -\code{\link[s2dverification]{RMS}} and \code{\link{CST_Load}} -} \author{ Deborah Verfaillie, \email{deborah.verfaillie@bsc.es} } +\seealso{ +\code{\link[s2dverification]{RMS}} and \code{\link{CST_Load}} +} + diff --git a/man/CST_RFSlope.Rd b/man/CST_RFSlope.Rd index 0c4e1671..d2b5aec0 100644 --- a/man/CST_RFSlope.Rd +++ b/man/CST_RFSlope.Rd @@ -50,3 +50,4 @@ slopes \author{ Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} } + diff --git a/man/CST_RFWeights.Rd b/man/CST_RFWeights.Rd index ef5ebe4d..08a7b850 100644 --- a/man/CST_RFWeights.Rd +++ b/man/CST_RFWeights.Rd @@ -47,6 +47,9 @@ nf <- 8 ww <- CST_RFWeights("./worldclim.nc", nf, lon, lat, fsmooth = TRUE) } } +\author{ +Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} +} \references{ Terzago, S., Palazzi, E., & von Hardenberg, J. (2018). Stochastic downscaling of precipitation in complex orography: @@ -54,6 +57,4 @@ A simple method to reproduce a realistic fine-scale climatology. Natural Hazards and Earth System Sciences, 18(11), 2825-2840. http://doi.org/10.5194/nhess-18-2825-2018 . } -\author{ -Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} -} + diff --git a/man/CST_RainFARM.Rd b/man/CST_RainFARM.Rd index 8df8f1f4..ca32e2d8 100644 --- a/man/CST_RainFARM.Rd +++ b/man/CST_RainFARM.Rd @@ -92,11 +92,12 @@ dim(res$data) # dataset member sdate ftime lat lon realization # 1 2 3 4 64 64 3 } +\author{ +Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} +} \references{ Terzago, S. et al. (2018). NHESS 18(11), 2825-2840. http://doi.org/10.5194/nhess-18-2825-2018 ; D'Onofrio et al. (2014), J of Hydrometeorology 15, 830-843; Rebora et. al. (2006), JHM 7, 724. } -\author{ -Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} -} + diff --git a/man/PlotCombinedMap.Rd b/man/PlotCombinedMap.Rd index 62e26904..6857c64d 100644 --- a/man/PlotCombinedMap.Rd +++ b/man/PlotCombinedMap.Rd @@ -5,10 +5,10 @@ \title{Plot Multiple Lon-Lat Variables In a Single Map According to a Decision Function} \usage{ PlotCombinedMap(maps, lon, lat, map_select_fun, display_range, - map_dim = "map", brks = NULL, cols = NULL, - col_unknown_map = "white", mask = NULL, col_mask = "grey", - bar_titles = NULL, legend_scale = 1, fileout = NULL, width = 8, - height = 5, size_units = "in", res = 100, ...) + map_dim = "map", brks = NULL, cols = NULL, col_unknown_map = "white", + mask = NULL, col_mask = "grey", bar_titles = NULL, legend_scale = 1, + fileout = NULL, width = 8, height = 5, size_units = "in", res = 100, + ...) } \arguments{ \item{maps}{List of matrices to plot, each with (longitude, latitude) dimensions, or 3-dimensional array with the dimensions (longitude, latitude, map). Dimension names are required.} @@ -67,11 +67,12 @@ PlotCombinedMap(list(a, b, c), lons, lats, bar_titles = paste('\% of belonging to', c('a', 'b', 'c')), brks = 20, width = 10, height = 8) } -\seealso{ -\code{PlotCombinedMap} and \code{PlotEquiMap} -} \author{ Nicolau Manubens, \email{nicolau.manubens@bsc.es} Veronica Torralba, \email{veronica.torralba@bsc.es} } +\seealso{ +\code{PlotCombinedMap} and \code{PlotEquiMap} +} + diff --git a/man/PlotForecastPDF.Rd b/man/PlotForecastPDF.Rd index c1959ee8..bed0bd31 100644 --- a/man/PlotForecastPDF.Rd +++ b/man/PlotForecastPDF.Rd @@ -4,11 +4,10 @@ \alias{PlotForecastPDF} \title{Plot one or multiple ensemble forecast pdfs for the same event} \usage{ -PlotForecastPDF(fcst, tercile.limits, extreme.limits = NULL, - obs = NULL, plotfile = NULL, title = "Set a title", - var.name = "Varname (units)", fcst.names = NULL, - add.ensmemb = c("above", "below", "no"), color.set = c("ggplot", - "s2s4e", "hydro")) +PlotForecastPDF(fcst, tercile.limits, extreme.limits = NULL, obs = NULL, + plotfile = NULL, title = "Set a title", var.name = "Varname (units)", + fcst.names = NULL, add.ensmemb = c("above", "below", "no"), + color.set = c("ggplot", "s2s4e", "hydro")) } \arguments{ \item{fcst}{a dataframe or array containing all the ensember members for each frecast. If \code{'fcst'} is an array, it should have two labelled dimensions, and one of them should be \code{'members'}. If \code{'fcsts'} is a data.frame, each column shoul be a separate forecast, with the rows beeing the different ensemble members.} @@ -50,3 +49,4 @@ PlotForecastPDF(fcsts2, c(-0.66, 0.66), extreme.limits = c(-1.2, 1.2), \author{ Llorenç Lledó \email{llledo@bsc.es} } +\encoding{UTF-8} diff --git a/man/PlotMostLikelyQuantileMap.Rd b/man/PlotMostLikelyQuantileMap.Rd index 0d984ede..6c92850e 100644 --- a/man/PlotMostLikelyQuantileMap.Rd +++ b/man/PlotMostLikelyQuantileMap.Rd @@ -109,10 +109,11 @@ PlotMostLikelyQuantileMap(bins, lons, lats, mask = 1 - (w1 + w2 / max(c(w1, w2))), brks = 20, width = 10, height = 8) -} -\seealso{ -\code{PlotCombinedMap} and \code{PlotEquiMap} } \author{ Veronica Torralba, \email{veronica.torralba@bsc.es}, Nicolau Manubens, \email{nicolau.manubens@bsc.es} } +\seealso{ +\code{PlotCombinedMap} and \code{PlotEquiMap} +} + diff --git a/man/RFSlope.Rd b/man/RFSlope.Rd index 5308ef8c..09a24ff5 100644 --- a/man/RFSlope.Rd +++ b/man/RFSlope.Rd @@ -60,3 +60,4 @@ slopes \author{ Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} } + diff --git a/man/RainFARM.Rd b/man/RainFARM.Rd index 35e7c2a5..3ef2a2f6 100644 --- a/man/RainFARM.Rd +++ b/man/RainFARM.Rd @@ -4,10 +4,9 @@ \alias{RainFARM} \title{RainFARM stochastic precipitation downscaling (reduced version)} \usage{ -RainFARM(data, lon, lat, nf, weights = 1, nens = 1, slope = 0, - kmin = 1, fglob = FALSE, fsmooth = TRUE, time_dim = NULL, - lon_dim = "lon", lat_dim = "lat", drop_realization_dim = FALSE, - verbose = FALSE) +RainFARM(data, lon, lat, nf, weights = 1, nens = 1, slope = 0, kmin = 1, + fglob = FALSE, fsmooth = TRUE, time_dim = NULL, lon_dim = "lon", + lat_dim = "lat", drop_realization_dim = FALSE, verbose = FALSE) } \arguments{ \item{data}{Precipitation array to downscale. @@ -114,3 +113,4 @@ dim(res$data) \author{ Jost von Hardenberg - ISAC-CNR, \email{j.vonhardenberg@isac.cnr.it} } + diff --git a/man/areave_data.Rd b/man/areave_data.Rd index a772220a..cc79c85c 100644 --- a/man/areave_data.Rd +++ b/man/areave_data.Rd @@ -41,3 +41,4 @@ areave_data <- Nicolau Manubens \email{nicolau.manubens@bsc.es} } \keyword{data} + diff --git a/man/lonlat_data.Rd b/man/lonlat_data.Rd index 0c6ee30f..eca7abac 100644 --- a/man/lonlat_data.Rd +++ b/man/lonlat_data.Rd @@ -41,3 +41,4 @@ lonlat_data <- Nicolau Manubens \email{nicolau.manubens@bsc.es} } \keyword{data} + diff --git a/man/lonlat_prec.Rd b/man/lonlat_prec.Rd index 345e3cab..69cb94e8 100644 --- a/man/lonlat_prec.Rd +++ b/man/lonlat_prec.Rd @@ -29,3 +29,4 @@ lonlat_prec <- CST_Load('prlr', exp = list(infile), obs = NULL, Jost von Hardenberg \email{j.vonhardenberg@isac.cnr.it} } \keyword{data} + -- GitLab