# glimpr.R GLM application for downscaling
#
# Copyright (C) 2017 Santander Meteorology Group (http://www.meteo.unican.es)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#' @title GLM downscaling
#' @description Implementation of GLM's to downscale precipitation data
#' @param y A grid or station data containing the observed climate data for the training period
#' @param modelPars Output object from function ppModelSetup containing the predictors and test data
#' @param simulate Character. Options are "no", "yes" or "occurrence". The last option simulates the occurrence but not the amount.
#' @param wet.threshold Value below which precipitation amount is considered zero
#' @param n.eofs Integer indicating the number of EOFs to be used as predictors
#' @family downscaling
#' @author J Bedia, M Iturbide
#' @importFrom abind abind
#' @importFrom stats glm binomial predict runif Gamma rgamma na.exclude
#' @importFrom transformeR array3Dto2Dmat getCoordinates mat2Dto3Darray
#' @keywords internal
glimpr <- function(y = y, modelPars = modelPars, simulate = c("none", "loocv", "kfold"), return.models = FALSE, wet.threshold = wet.threshold, n.pcs = n.pcs) {
if (is.null(n.pcs)) {
n.pcs <- dim(modelPars$pred.mat)[2]
}
# Prepare predictand matrix
ymat <- if (isTRUE(modelPars$stations)) {
y$Data
} else {
if (is.null(dim(y$Data))) {
as.matrix(y$Data)
} else {
array3Dto2Dmat(y$Data)
}
}
# binarize
ymat.bin <- ymat
ymat.bin[which(ymat < wet.threshold)] <- 0
ymat.bin[which(ymat >= wet.threshold)] <- 1L
#obtaining climatological frequencies of occurrences for probability threshold calibration
wet.prob <- apply(ymat.bin, 2, function(x) {sum(x, na.rm = TRUE) / length(na.exclude(x))})
# Filter empty gridcell series
rm.ind <- which(is.na(wet.prob))
# Valid columns or gridcells (cases)
cases <- if (length(rm.ind) > 0) {
(1:ncol(ymat))[-rm.ind]
} else {
1:ncol(ymat)
}
# Models
message("[", Sys.time(), "] Fitting models ...")
err <- numeric()
pred.list <- list()
mod.bin <- list()
mod.gamma <- list()
suppressWarnings(
for (x in 1:length(cases)) {
mod.bin.x <- glm(ymat.bin[ ,cases[x]] ~ ., data = as.data.frame(modelPars$pred.mat)[,1:n.pcs], family = binomial(link = "logit"))
sims.bin <- sapply(1:length(modelPars$sim.mat), function(i) {
pred <- predict(mod.bin.x, newdata = as.data.frame(modelPars$sim.mat[[i]])[,1:n.pcs], type = "response")
if (simulate != "no") {
rnd <- runif(length(pred), min = 0, max = 1)
ind01 <- which(pred > rnd)
pred[ind01] <- 1
pred[-ind01] <- 0
} else {
pred[which(pred >= quantile(mod.bin.x$fitted.values, 1 - wet.prob[cases[x]], na.rm = TRUE))] <- 1L
#pred[which(pred >= 0.5)] <- 1L
pred <- as.integer(pred)
}
return(pred)
})
if (return.models == FALSE) mod.bin <- NULL
wet <- which(ymat[ ,cases[x]] >= wet.threshold)
# TODO: handle exception when model is over-specified (https://stat.ethz.ch/pipermail/r-help/2000-January/009833.html)
Npcs <- n.pcs + 1
mod.gamma.x <- NULL
###########
while (is.null(mod.gamma.x) & Npcs > 1) {
Npcs <- Npcs - 1
mod.gamma.x <- tryCatch(expr = {
glm(ymat[ ,cases[x]] ~., data = as.data.frame(modelPars$pred.mat)[ ,1:Npcs],
family = Gamma(link = "log"),
subset = wet)
},
error = function(err) {
mod.gamma.x <- NULL
})
}
if (length(mod.gamma.x$model) - 1 < n.pcs) {
err[x] <- 1
} else {
err[x] <- 0
}
###########
if (is.null(mod.gamma.x)) {
sims <- sapply(1:length(modelPars$sim.mat), function(i) {
matrix(NA, ncol = 1, nrow = nrow(modelPars$sim.mat[[i]]))
})
} else {
sims <- sapply(1:length(modelPars$sim.mat), function(i) {
if (simulate == "yes") {
predg <- unname(predict(mod.gamma.x, newdata = as.data.frame(modelPars$sim.mat[[i]])[,1:n.pcs], type = "response"))
# predg[which(predg<=wet.threshold)] <- 0
rgamma(n = length(predg), shape = 1/summary(mod.gamma.x)$dispersion, scale = summary(mod.gamma.x)$dispersion * predg) * sims.bin[,i]
} else {
unname(predict(mod.gamma.x, newdata = as.data.frame(modelPars$sim.mat[[i]])[,1:n.pcs], type = "response") ) * sims.bin[,i]
}
})
}
if (return.models == FALSE) mod.gamma.x <- NULL
pred.list[[x]] <- unname(sims)
sims <- NULL
mod.bin[[x]] <- mod.bin.x
mod.gamma[[x]] <- mod.gamma.x
})
mod <- list("binary" = mod.bin, "gamma" = mod.gamma)
n.err <- length(which(err == 1))
if (n.err > 0) {
n <- round(n.err/length(err)*100, digits = 2)
warning("In ", n,"% of the locations: glm.fit convergence reached after variable number reduction.")
}
# err <- lapply(1:length(pred.list), function(u){
# is.logical(pred.list[[u]])
# })
#
# n.err <- length(which(do.call("abind", err)==TRUE))
#
# if(n.err > 0){
# warning("glm.fit did not converge ", n.err, " times, NA returned.")
# }
# Refill with NaN series
#######
if (length(rm.ind) > 0) {
aux.list <- rep(list(matrix(nrow = nrow(modelPars$sim.mat[[1]]), ncol = length(modelPars$sim.mat))), ncol(ymat))
aux.list[cases] <- pred.list
pred.list <- aux.list
aux.list <- NULL
}
x.obs <- getCoordinates(y)$x
y.obs <- getCoordinates(y)$y
aux.list <- lapply(1:length(modelPars$sim.mat), function(i) {
aux <- lapply(1:length(pred.list), function(j) {
pred.list[[j]][ ,i]
})
aux.mat <- do.call("cbind", aux)
out <- if (!modelPars$stations) {
mat2Dto3Darray(aux.mat, x.obs, y.obs)
} else {
aux.mat
}
aux.mat <- NULL
return(out)
})
# Data array - rename dims
# obs$Data <-
o <- drop(unname(do.call("abind", c(aux.list, along = -1))))
aux.list <- NULL
# Date replacement
# obs$Dates <- modelPars$sim.dates
message("[", Sys.time(), "] Done.")
# return(obs)
if (isTRUE(return.models)) o <- list(o, "models" = mod)
return(o)
# return(sims.bin)
}
# End
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.