Nothing
#' Detect trends and quantify their effect on the probability of SPI values occurring
#'
#' @param rain.at.TS
#' A 4-column matrix generated with `TSaggreg()`. No other objects are accepted.
#' * 1st column is years (YYYY),
#' * 2nd is the months (1 to 12),
#' * 3rd is the quasiWeeks (1 to 4),
#' * and 4th is the rainfall totals accumulated at a time scale.
#' @param only.linear
#' A character string value (`Yes` or `No`) defining if the function must
#' consider only linear models (`Yes`) or linear and non-linear models (`No`).
#' Default is `Yes`.
#' @returns
#' A `list` object with:
#' \describe{
#' \item{data.week}{The Rainfall amounts, SPI, cumulative probability of the SPI values under the stationary
#' approach, cumulative probability of the SPI values under the non-stationary approach,
#' and the changes in the frequency of below zero SPI values caused by the changes in rainfall patterns.}
#' \item{model.selection}{The generalized additive model that best fits the rainfall series}
#' \item{Changes.Freq.Drought}{changes in the frequency of zero precipitation, moderate to extreme,
#' severe to extreme and extreme drought events,as categorized by the SPI classification system,
#' caused by the changes in rainfall patterns.
#' Changes in the precipitation amounts associated describing normal conditions is also shown.}
#' \item{Statistics}{Year to year changes in the expected frequency of moderate to extreme, severe to extreme
#' and extreme drought events.}
#' \item{data.week}{The Rainfall amounts, \acronym{SPI}, cumulative
#' probability of the \acronym{SPI} values under the stationary approach,
#' cumulative probability of the \acronym{SPI} values under the non-stationary
#' approach, and the changes in the frequency of below zero \acronym{SPI}
#' values caused by the changes in rainfall patterns.}
#' \item{model.selection}{The generalized additive model that best fits the
#' rainfall series}
#' \item{Changes.Freq.Drought}{changes in the frequency of zero precipitation,
#' moderate, severe and extreme drought events, as defined by the
#' \acronym{SPI} classification system, caused by the changes in rainfall
#' patterns. Changes in the precipitation amounts associated describing normal
#' conditions is also shown.}
#' \item{Statistics}{Year to year changes in the expected frequency of
#' moderate, severe and extreme drought events.}
#' }
#' @examples
#'
#' rainTS4 <- rainTS4
#' Changes_SPI <- SPIChanges(rain.at.TS=rainTS4, only.linear = "yes")
#' @importFrom gamlss gamlss GAIC
#' @importFrom gamlss.dist GA pGA qGA BI
#' @importFrom stats qnorm fitted glm predict binomial
#' @importFrom spsUtil quiet
#' @importFrom MuMIn AICc
#' @importFrom rlang arg_match
#' @importFrom brglm2 brglmFit
#' @autoglobal
#' @export
SPIChanges <- function(rain.at.TS, only.linear = "Yes"){
if (!inherits(rain.at.TS, c("TSaggreg", "matrix", "array"))) {
stop("`rain.at.TS` must be a matrix that is generated by using `TSaggreg()`.")
}
if (!is.numeric(rain.at.TS) || anyNA(rain.at.TS) ||
length(rain.at.TS[rain.at.TS < 0]) != 0 || ncol(rain.at.TS) != 4 ) {
stop("Physically impossible or missing values in rain.at.TS.")}
n <- length(rain.at.TS[, 1])
if (n < 480) {
stop("Less than 10 years of rainfall records. We cannot proceed.")
}
if (n < 1440) {
warning("Less than 30 years of rainfall records. Longer periods are highly recommended.")
}
if (length(rain.at.TS[rain.at.TS[, 2] < 1]) != 0 ||
length(rain.at.TS[rain.at.TS[, 2] > 12]) != 0 ||
length(rain.at.TS[rain.at.TS[, 2] == 1]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 2]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 3]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 4]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 5]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 6]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 7]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 8]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 9]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 10]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 11]) < 32 ||
length(rain.at.TS[rain.at.TS[, 2] == 12]) < 32) {
stop("Column Month in rain.at.TS is probably malformed.")
}
if (length(rain.at.TS[rain.at.TS[, 3] < 1]) != 0 ||
length(rain.at.TS[rain.at.TS[, 3] > 4]) != 0 ||
length(rain.at.TS[rain.at.TS[, 3] == 1]) < 96 ||
length(rain.at.TS[rain.at.TS[, 3] == 2]) < 96 ||
length(rain.at.TS[rain.at.TS[, 3] == 3]) < 96 ||
length(rain.at.TS[rain.at.TS[, 3] == 4]) < 96) {
stop("Column quasiWeek in rain.at.TS is probably malformed.")
}
only.linear <- tolower(arg_match(only.linear,
c("yes", "no", "Yes", "No", "YES", "NO", "yEs", "nO", "yeS")))
years <- rain.at.TS[,1]
months <- rain.at.TS[,2]
data.week <- data.frame(matrix(NA, length(rain.at.TS[, 1]), 8))
data.week[,1:4] <- rain.at.TS
data.week <- data.week[order(data.week[,2],data.week[,3]),]
month <- 1
week <- 1
model.selection <- matrix(NA,48,1)
Changes.Freq.Drought <- matrix(NA,48,7)
for (a in 1:48) {
rain.week <- (data.week[which(data.week[, 2] == month &
data.week[, 3] == week), 4])
n.week <- length(rain.week)
rain.week.nozeros <- (rain.week[rain.week > 0])
if (a > 1) {
initial.row <- last.row + 1
last.row <- initial.row + n.week - 1
} else{
initial.row <- 1
last.row <- n.week
}
nz <- n.week - length(rain.week.nozeros)
time <- as.matrix(seq(1:n.week))
probzero.st <- calc.probzero.st(rain.week)
if (probzero.st[1] >= 0.50) {
warning(
"rainfall series Month ",
month,
" Week ",
week,
"has more than 50% of zeros. In this situation ",
"the SPI cannot assume values lower than 0"
)} else if (probzero.st[1] > 0.159) {
warning(
"rainfall series Month ",
month,
" Week ",
week,
" has more than 15.9% of zeros. In this situation ",
"the SPI cannot assume values lower than -1"
)
} else if (probzero.st[1] > 0.067) {
warning(
"rainfall series Month ",
month,
" Week ",
week,
" has more than 6.7% of zeros. In this situation ",
"the SPI cannot assume values lower than -1.5"
)
}
id <- which(rain.week>0)
time.nonzero <- as.vector(time[id])
n.time.nonzero <- length(time.nonzero)
Model.Drought.week <- data.frame(matrix(NA,n.time.nonzero,5))
t.gam <- quiet(gamlss(
rain.week.nozeros ~ 1,
family = GA,
mu.link = "log",
sigma.link = "log"
))
if (only.linear == "yes") {
models <- Fit.linears(rain.week.nozeros, time.nonzero)
model.selection[a, 1] <- models$best
selected.model <- models$selected.model
} else {
models <- Fit.Nonlinears(rain.week.nozeros, time.nonzero)
model.selection[a, 1] <- models$best
selected.model <- models$selected.model
}
quasiprob <- (
probzero.st[1] + (1 - probzero.st[1]) * pGA(
rain.week,
mu = t.gam$mu.fv[1],
sigma = t.gam$sigma.fv[1],
lower.tail = TRUE,
log.p = FALSE
)
)
quasiprob[quasiprob < 0.001351] <- 0.001351
quasiprob[quasiprob > 0.998649] <- 0.998649
data.week[initial.row:last.row, 6] <- quasiprob
normal.param.prob <- ifelse(probzero.st[1] < 0.5, 0.5 - probzero.st[1], 0)
mod.param.prob <- ifelse(probzero.st[1] < 0.159, 0.159 - probzero.st[1], 0)
sev.param.prob <- ifelse(probzero.st[1] < 0.067, 0.067 - probzero.st[1], 0)
ext.param.prob <- ifelse(probzero.st[1] < 0.023, 0.023 - probzero.st[1], 0)
calc.stat.rain <- function(param.prob, t.gam) {
return(qGA(param.prob, mu = t.gam$mu.fv[1], sigma = t.gam$sigma.fv[1]))
}
list2env(lapply(
list(
"stat.rain.normal" = normal.param.prob,
"stat.rain.drought.mod" = mod.param.prob,
"stat.rain.drought.sev" = sev.param.prob,
"stat.rain.drought.extr" = ext.param.prob
),
calc.stat.rain,
t.gam
), envir = environment())
if (model.selection[a,1]==1){
quasiprob.ns <- as.matrix(data.week[initial.row:last.row, 6])
Changes.Freq.Drought[a, 1] <- round((probzero.st[1]), 3)
Changes.Freq.Drought[a, 2] <- round((probzero.st[n.week]), 3)
Changes.Freq.Drought[a, 3] <- round(stat.rain.normal, 2)
Changes.Freq.Drought[a, 4] <- Changes.Freq.Drought [a, 3]
Changes.Freq.Drought[a, 5:7] <- 0
Model.Drought.week[, 3] <- probzero.st[1]
Model.Drought.week[, 4] <- t.gam$mu.fv
Model.Drought.week[, 5] <- t.gam$sigma.fv
} else {
probzero <- calc.probzero(rain.week, time)
probzero[probzero < 0.0001] <- 0
quasiprob.ns <- as.matrix(probzero+(1-probzero)*pGA(rain.week,mu = selected.model$mu.fv, sigma = selected.model$sigma.fv))
Changes.Freq.Drought [a,1] <- round((probzero.st[1]),3)
Changes.Freq.Drought [a,2] <- round((probzero[n.week]),3)
Changes.Freq.Drought [a,3] <- round(stat.rain.normal,2)
nonstat.normal.param.prob <- ifelse(probzero[n.week] < 0.5, 0.5 - probzero[n.week], 0)
Changes.Freq.Drought [a,4] <- round(qGA(nonstat.normal.param.prob , mu = selected.model$mu.fv[(n.week-nz)],
sigma = selected.model$sigma.fv[(n.week-nz)]),2)
Changes.Freq.Drought [a,5] <- round(ifelse(probzero[n.week] >= 0.159, 100 * (probzero[n.week]-probzero[1]),
100 * ((probzero[n.week] + (1 - probzero[n.week]) *
pGA(stat.rain.drought.mod,
mu = selected.model$mu.fv[(n.week-nz)],
sigma = selected.model$sigma.fv[(n.week-nz)]))-0.159)),2)
Changes.Freq.Drought [a,6] <- round(ifelse(probzero[n.week] >= 0.067, 100*(probzero[n.week]-probzero[1]),
100*((probzero[n.week]+(1-probzero[n.week])*
pGA(stat.rain.drought.sev,
mu = selected.model$mu.fv[(n.week-nz)],
sigma = selected.model$sigma.fv[(n.week-nz)]))-0.067)),2)
Changes.Freq.Drought [a,7] <- round(ifelse(probzero[n.week] >= 0.023, 100*(probzero[n.week]-probzero[1]),
100*((probzero[n.week]+(1-probzero[n.week])*
pGA(stat.rain.drought.extr,
mu = selected.model$mu.fv[(n.week-nz)],
sigma = selected.model$sigma.fv[(n.week-nz)]))-0.023)),2)
Model.Drought.week[,3] <- probzero[time.nonzero]
Model.Drought.week[,4] <- selected.model$mu.fv
Model.Drought.week[,5] <- selected.model$sigma.fv
}
Model.Drought.week[,1] <- rep(month,n.time.nonzero)
Model.Drought.week[,2] <- rep(week,n.time.nonzero)
quasiprob.ns[quasiprob.ns < 0.001351] <- 0.001351
quasiprob.ns[quasiprob.ns > 0.998649] <- 0.998649
data.week[initial.row:last.row,7] <- quasiprob.ns
ifelse(a == 1,
Statistics <- as.data.frame(Model.Drought.week),
Statistics <- quiet(rbind(Statistics, Model.Drought.week)))
week <- week + 1
if (week == 5)
{
month <- month + 1
week <- 1}
}
Statistics[,3:5] <- Statistics[,3:5]
data.week[,5] <- c(qnorm(data.week[,6], mean = 0, sd = 1))
dry.values <- which(data.week[,5] <= 0)
wet.values <- which(data.week[,5] > 0)
data.week[dry.values,8] <- 100*round(data.week[dry.values,7]-data.week[dry.values,6],3)
data.week[wet.values,8] <- "NoDrought"
data.week <- data.week[order(data.week[,1]),]
colnames(data.week) <- c(
"Year",
"Month",
"quasiWeek",
"rain.at.TS",
"SPI",
"Exp.Acum.Prob",
"Actual.Acum.Prob",
"ChangeDryFreq"
)
data.week[,5:7] <- round(data.week[,5:7],3)
Statistics <- round(Statistics,3)
months <- sort(rep(seq(1:12),4))
quasiweeks <- rep(seq(1:4),12)
Changes.Freq.Drought <- cbind(months, quasiweeks, Changes.Freq.Drought)
colnames(Changes.Freq.Drought) <- c(
"Month",
"quasiWeek",
"StatProbZero",
"NonStatProbZero",
"StatNormalRain",
"NonStatNormalRain",
"ChangeMod",
"ChangeSev",
"ChangeExt"
)
model.selection <- cbind(months, quasiweeks, model.selection)
colnames(model.selection) <- c("Month", "quasiWeek", "model")
colnames(Statistics) <- c("Month", "quasiWeek", "ProbZero", "mu", "sigma")
Drought_Changes <- list(
data.week = data.week,
model.selection = model.selection,
Changes.Freq.Drought = Changes.Freq.Drought,
Statistics = Statistics
)
return(Drought_Changes)
}
#' Calculate probzero, the Probability of Zero Rain under non-stationary assumption
#'
#' @param rain.week data vector
#' @param time data vector
#' @author Adam H. Sparks \email{adamhsparks@@gmail.com}
#' @author Gabriel C. Blain \email{gabrielblain@@gmail.com}
#' @noRd
#' @keywords Internal
calc.probzero <- function(rain.week,time) {
zero_rain <- as.integer(rain.week == 0)
modelo <- quiet(glm(zero_rain ~ time, family = binomial(link = "logit"), method = brglm2::brglmFit))
prob_zero_rain <- fitted(modelo, "mu")
return(prob_zero_rain)
}
#' Calculate probzero the Probability of Zero Rain under stationary assumption
#'
#' @param rain.week data vector
#' @author Adam H. Sparks \email{adamhsparks@@gmail.com}
#' @author Gabriel C. Blain \email{gabrielblain@@gmail.com}
#' @noRd
#' @keywords Internal
calc.probzero.st <- function(rain.week) {
zero_rain <- as.integer(rain.week == 0)
modelo <- quiet(gamlss(zero_rain~1, family = BI))
prob_zero_rain <- fitted(modelo, "mu")
return(prob_zero_rain)
}
#' Fit the linear models
#'
#' @param rain.week.nozeros vector of positive rain
#' @param time.nonzero time vector
#' @note This was adapted from \CRANpkg{gamlss}.
#' @noRd
#' @keywords Internal
Fit.linears <- function(rain.week.nozeros, time.nonzero) {
t.gams <- list(
t.gam <- quiet(
gamlss(
rain.week.nozeros ~ 1,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns10 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns01 <- quiet(
gamlss(
rain.week.nozeros ~ 1,
sigma.formula = ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns11 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero,
sigma.formula = ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
)
)
best <- which.min(lapply(t.gams, AICc, k = 4.4))
selected.model <- switch(best, t.gam, t.gam.ns10, t.gam.ns01, t.gam.ns11)
return(list(selected.model = selected.model, best = best))
}
#' Fit the Nonlinear models
#'
#' @param rain.week.nozeros vector of positive rain
#' @param time.nonzero time vector
#' @note This was adapted from \CRANpkg{gamlss}.
#' @noRd
#' @keywords Internal
Fit.Nonlinears <- function(rain.week.nozeros, time.nonzero) {
t.gams <- list(
t.gam <- quiet(
gamlss(
rain.week.nozeros ~ 1,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns10 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns01 <- quiet(
gamlss(
rain.week.nozeros ~ 1,
sigma.formula = ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns11 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero,
sigma.formula = ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns20 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero + I(time.nonzero ^ 2),
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns02 <- quiet(
gamlss(
rain.week.nozeros ~ 1,
family = GA,
mu.link = "log",
sigma.formula = ~ time.nonzero +
I(time.nonzero ^ 2),
sigma.link = "log"
)
),
t.gam.ns21 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero + I(time.nonzero ^ 2),
sigma.formula = ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns12 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero,
sigma.formula = ~ time.nonzero + I(time.nonzero ^ 2),
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns22 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero + I(time.nonzero ^ 2),
sigma.formula = ~ time.nonzero + I(time.nonzero ^ 2),
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns30 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns03 <- quiet(
gamlss(
rain.week.nozeros ~ 1,
family = GA,
mu.link = "log",
sigma.formula = ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
sigma.link = "log"
)
),
t.gam.ns31 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
sigma.formula = ~ time.nonzero,
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns13 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero,
sigma.formula = ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns32 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
sigma.formula = ~ time.nonzero +
I(time.nonzero ^ 2),
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns23 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero +
I(time.nonzero ^ 2),
sigma.formula = ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
family = GA,
mu.link = "log",
sigma.link = "log"
)
),
t.gam.ns33 <- quiet(
gamlss(
rain.week.nozeros ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
sigma.formula = ~ time.nonzero +
I(time.nonzero ^ 2) +
I(time.nonzero ^ 3),
family = GA,
mu.link = "log",
sigma.link = "log"
)
)
)
best <- which.min(lapply(t.gams, AICc, k = 4.4))
selected.model <- switch(
best,
t.gam,
t.gam.ns10,
t.gam.ns01,
t.gam.ns11,
t.gam.ns20,
t.gam.ns02,
t.gam.ns21,
t.gam.ns12,
t.gam.ns22,
t.gam.ns30,
t.gam.ns03,
t.gam.ns31,
t.gam.ns13,
t.gam.ns32,
t.gam.ns23,
t.gam.ns33
)
return(list(selected.model = selected.model, best = best))
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.