##################################################
#' At-site frequency analysis using annual maxima
#'
#' Return a fitting of a distribution, normally representing annual maximums.
#' Both maximum likelihood and L-moments method are available.
#' If the maximum likelihood method fails, the L-moments is returned and a warning
#' message is issued.
#' The function \code{FitAmax.auto} select automatically the best distribution
#' according to the AIC criteria and return the fitted object.
#'
#' @param x Data.
#'
#' @param distr Distribution to fit. For \code{FitAmax.auto} it is a list of
#' candidate distributions.
#' See \code{\link{vec2par}} for the list of available distribution.
#'
#' @param method Estimation method.
#' Either maximum likelihood ('mle') or L-moments ('lmom').
#'
#' @param varcov Should the variance-covariance matrix of the parameters
#' be computed. For \code{mle} the covariance matrix is derived from the
#' hessian matrix. For L-moments, non-parametric bootstrap is used.
#'
#' @param lmm L-moments of the data.
#' Can be use to speed up multiple calls of FitAmax
#' See \code{\link[lmomco]{lmoms}}.
#'
#' @param nsim Number of simulations used to evaluate the covariance matrix
#' when using L-moment based estimator.
#'
#' @return
#'
#' \item{data}{Data Values.}
#' \item{lmom}{L-moments.}
#' \item{para}{Parameter estimates.}
#' \item{varcov}{Covariance matrix of the parameter}
#' \item{llik}{Value of the log-likelihood}
#'
#'
#' @section References:
#'
#' Coles, S. (2001). An introduction to statistical
#' modeling of extreme values. Springer Verlag.
#'
#' Hosking, J. R. M., & Wallis, J. R. (1997). Regional frequency analysis:
#' an approach based on L-moments. Cambridge Univ Pr.
#'
#' @seealso \code{\link{predict.amax}}, \code{\link{gofTest}},
#' \code{\link{plot.amax}} .
#'
#' @export
#'
#' @examples
#'
#' ## Extract a time series of annual maxima
#' x <- ExtractAmax(flow~date, flowStJohn)$flow
#'
#' ## Fitting of GEV distribution using L-moments
#'
#' fit <- FitAmax(x,'gev')
#' print(fit)
#' coef(fit)
#' AIC(fit)
#' fit$lmom
#'
#' ## The evaluation of the variance-covariance matrix can be turn down
#' fit <- FitAmax(x,'gev', varcov = FALSE)
#'
#' ## Using Maximum likelihood
#' fit <- FitAmax(x,'gev', method ='mle')
#' print(fit)
#' vcov(fit)
#'
#' ## Standard deviation of the parameter
#' sqrt(diag(vcov(fit)))
#'
#' ## Chose the best distribution according to AIC
#' FitAmax.auto(x, distr = c('gev','glo','gno','pe3'), method = 'mle')
#'
#'
FitAmax <-
function(x, distr = 'gev', method = 'lmom',
varcov = TRUE, lmm = NULL, nsim = 1000){
#################################################
## Verify that all values are finite values
if(!all(is.finite(x)))
stop('There is one (or more) non finite values')
if(varcov & method == 'lmom' & nsim < 2)
stop('There is not enough simulations (nsim) to perform boostrap')
## Keep only the firs distribution passed
distr <- distr[1]
#################################################
## Compute the L-moments and associated parameters
if(is.null(lmm))
lmm <- lmomco::lmoms(x)
f <- lmomco::lmom2par(lmm,distr)
if(method == 'mle'){
## Fit the distribution by maximum likelihood
suppressWarnings(
para <- try(lmomco::mle2par(x, distr, para.init = f, silent = TRUE,
hessian = varcov)))
## If mle return an error, use the L-moment estimate
if(class(para) == 'try-error'){
para <- f
varcov <- NA
method = 'lmom'
## compute the log-likelihood
llik <- sum(log(lmomco::dlmomco(x,para)))
warning('Maximum likelihood has fail')
} else {
## Verify and warn the user if optim does not converge
if(para$optim$convergence != 0)
warning('Maximum likelihood may have not coverge')
## (If necessary)
## Compute the covariance matrix by inversing the hessian matrix
if(varcov)
varcov <- chol2inv(chol(para$optim$hessian))
else
varcov <- NA
## compute the log-likelihood value
llik <- -para$optim$value
## Remove the optim element from the list for a cleaner output
para <- para[1:3]
}
} else if(method == 'lmom'){
para <- f
## Compute the covariance matrix by boostrap if required
if(varcov){
xboot <- replicate(nsim, lmomco::rlmomco(length(x), para))
pboot <- apply(xboot, 2, lmomco::lmoms)
pboot <- lapply(pboot,lmomco::lmom2par, distr)
pboot <- sapply(pboot, getElement, 'para')
varcov <- as.matrix(Matrix::nearPD(cov(t(pboot)))$mat)
} else
varcov <- NA
## compute the log-likelihood
llik <- sum(log(lmomco::dlmomco(x,para)))
}
## Return an amax object
ans <- list(lmom = lmm$lambdas,
method = method,
para = para$para,
distr = distr,
varcov = varcov,
llik = llik,
data = x)
class(ans) <- 'amax'
ans
}
#' @export
print.amax <- function(obj){
cat('\nAt-site frequency analysis\n')
cat('\nDistribution:', obj$distr,
'\nAIC:', format(AIC(obj), digits = 4),
'\nMethod:', obj$method)
cat('\nEstimate:\n')
print(obj$para, digit = 4)
if(all(!is.na(obj$varcov))){
se <- sqrt(diag(vcov(obj)))
names(se) <- names(obj$para)
cat('\nStd.err:\n')
print(se, digits = 4)
}
cat('\nLmoments:\n')
print(data.frame(l1 = obj$lmom[1],
lcv = obj$lmom[2]/obj$lmom[1],
lsk = obj$lmom[3]/obj$lmom[2],
lkt = obj$lmom[4]/obj$lmom[2]), digits = 4)
}
#' @export
coef.amax <- function(obj) obj$para
#' @export
AIC.amax <- function(obj, k = 2)
as.numeric(k*length(obj$para) - 2*obj$llik)
#' @export
as.list.amax <- function(obj){
class(obj) <- 'list'
obj
}
#' @export
vcov.amax <- function(obj){
## if exist return the covariance matrix
if(!is.null(obj$varcov))
ans <- obj$varcov
else
ans <- NA
## return
ans
}
#' @export
#' @rdname FitAmax
FitAmax.auto <-
function(x, distr = c('gev','gno', 'pe3', 'glo'), ...,
tol.gev = 0){
## compute lmoments once
lmm <- lmomco::lmoms(x)
## Fit data and compute the AIC
fits <- lapply(distr, function(z) try(FitAmax(x, distr = z, lmm = lmm, ...)))
crit <- lapply(fits, function(z) try(AIC(z)))
## If the fitting fails put AIC to Infinity
crit.err <- sapply(crit, function(z) class(z) != 'numeric')
if(any(crit.err))
crit[[crit.err]] <- Inf
crit <- unlist(crit)
## It is assumed for small difference in AIC, the fitting is similar
## Therefore GEV could selected as default due to its theoritical
## justification. The tolerate difference is tol.gev
gid <- which(distr == 'gev')
crit[gid] <- crit[gid] - tol.gev
## return
fits[[which.min(crit)]]
}
######################################################################
#' Predict return levels
#'
#' Return the flood quantile of annual maximum distribution and
#' Confident intervals are provided by bootstrap.
#'
#' @param obj Output from \code{\link{FitAmax}}
#'
#' @param q Probabilities associated to the return level. For example,
#' a 100 years return period is equivalent to \code{q = 0.99}.
#'
#' @param se Return the standard deviation of the return level using
#' the delta method. The fitted model must
#'
#' @param ci Method to compute the confident interval. One of \code{'delta'}
#' for the delta method, \code{'boot'} for parametric boostrap and \code{'norm'}
#' for Monte-Carlo approximation assuming normality of the parameters.
#'
#' @param alpha Probability outside the confident interval.
#'
#' @param nsim Number of simulation use for resampling.
#'
#' @param out.matrix Logical. Should the resampling be returned. If true,
#' a list is returned containing the prediction table (\code{pred}),
#' the parameters (\code{para}) and the return levels (\code{qua}).
#'
#' @export
#'
#' @examples
#'
#' #' ## Extract an time series of annual maxima
#' x <- ExtractAmax(flow~date, flowStJohn)$flow
#'
#' ## Fitting of GEV distribution using L-moments
#' fit <- FitAmax(x,'gev', method = 'mle')
#'
#' ## Get the estimated quantile of 10 and 100 years return period
#' rp <- 1-1/c(10,100)
#' predict(fit, rp)
#' predict(fit, se = TRUE, ci = 'delta')
#'
#' ## The bootstrap sample used for CI are returned
#' fit <- FitAmax(x,'gev', varcov = FALSE)
#' boot <- predict(fit, rp, se = FALSE, ci = 'boot',
#' nsim = 500, out.matrix = TRUE)
#'
predict.amax <- function(obj, q = c(.5, .8, .9, .95, .98, .99),
se = FALSE, ci = 'none',
alpha = .05, nsim = 1000,
out.matrix = FALSE){
n <- length(obj$data)
## Function that compute the return level for a given vector of parameter
FunQ <- function(z, q0){
lmomco::qlmomco(q0, lmomco::vec2par(z, obj$distr))
}
## Compute Return level
ans <- FunQ(obj$para, q)
## Compute confident intervals by resampling techniques
if(ci == 'boot'){
## using Parametric bootstrap
bootp <- matrix(NA,nsim,length(obj$para))
p0 <- lmomco::vec2par(obj$para, obj$distr)
for(ii in 1:nsim){
repeat{
## simulate
b <- lmomco::rlmomco(n,p0)
## estimate
if(obj$method == 'gml'){
suppressWarnings(p <- try(
FitGev(b, varcov = FALSE, mu = obj$prior[1], sig2 = obj$prior[2]),
silent = TRUE))
} else {
suppressWarnings(p <- try(
FitAmax(b, distr = obj$distr, method = obj$method, varcov = FALSE),
silent = TRUE))
}
## Sample only feasible set
if(any(class(p) != 'try-error'))
if(p$method == obj$method)
break
}# end repeat
bootp[ii,] <- p$para
}# end for
} else if(ci == 'norm'){
## Verify requirements for using the delta method
if(any(is.na(obj$varcov)))
stop('Covariance matrix was not estimated')
bootp <- mnormt::rmnorm(nsim, obj$para, obj$varcov)
} else
bootp <- NA
if(!any(is.na(bootp))){
## Compute the return level
bootq <- t(apply(bootp,1, FunQ, q))
## if only one return period was passed in argument
## pivot the matrix
if(all(dim(bootq) == c(1,nsim)))
bootq <- t(bootq)
## Compute the confident interval
bnd <- t(apply(bootq, 2, quantile, c(alpha/2,1 - alpha/2)))
colnames(bnd) <- c('lower','upper')
}
## Compute the standard deviation via the delta method
if(se | ci == 'delta'){
## Verify requirements for using the delta method
if(any(is.na(obj$varcov)))
stop('Covariance matrix was not estimated')
## could eventually be replaced by analogic formulas
g <- sapply(q, function(z) numDeriv::grad(FunQ, obj$para, q0 = z))
sig <- sqrt(diag(crossprod(g, obj$varcov %*% g)))
if(ci == 'delta'){
nq <- abs(qnorm(alpha/2))
bnd <- cbind(lower = ans - sig * nq, upper = ans + sig * nq)
}
}
## Built the final output
ans <- data.frame(pred = ans)
if(se)
ans <- cbind(ans, se = sig)
if(ci %in% c('boot','delta','norm'))
ans <- cbind(ans, bnd)
if(ncol(ans) == 1)
ans <- ans[,1]
if(out.matrix)
ans <- list(pred = ans, para = bootp, qua = bootq)
return(ans)
}
#############################################
#' Return level plot
#'
#'
#' @param obj Output from \code{\link{FitAmax}}.
#'
#' @param ci Logical. Does confident interval should be display. if so the
#' the Monte-Carlo approximation method is used.
#' See \code{\link{predict.amax}}.
#'
#' @param col.ci,lty.ci,lwd.ci Graphical parameters definining the display
#' of the confident interval.
#'
#' @param ... Other graphical parameters. See \code{\link{par}}.
#'
#' @export
#'
#' @examples
#'
#' x <- ExtractAmax(flow~date, flowStJohn)$flow
#'
#' fit <- FitAmax(x, distr = 'gev', method = 'mle')
#'
#' plot(fit, ci = TRUE)
plot.amax <- function(obj, main = 'Return level plot',
xlab = 'Return period',
ylab = 'Return levels',
ci = FALSE, col.ci = 'red', lty.ci = 2,
lwd.ci = 1, ...){
n <- length(obj$data)
x <- sort(obj$data)
p <- obj$para
nrt <- 200
## Plotting axis
xat <- c(1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 5000, 10000)
prt <- (seq(n) - .5) / n
ip <- seq(min(prt), max(prt), l = nrt)
llip <- -log(-log(ip))
pat <- 1-1/xat
irt <- 1-1/ip
## Plot empirical points
plot(-log(-log(prt)), x, axes = FALSE, main = main,
ylab = ylab, xlab = xlab, ...)
axis(2)
axis(1, at = -log(-log(pat)), lab = xat)
## if Confident interval are to be plotted
if(ci){
bnd <- predict(obj, q = ip, se = FALSE, ci = 'delta')
lines(llip, bnd[,1], col = col.ci, lwd = lwd.ci)
lines(llip,smooth.spline(irt,bnd[,2])$y,
lty = lty.ci, col = col.ci, lwd = lwd.ci)
lines(llip,smooth.spline(irt,bnd[,3])$y,
lty = lty.ci, col = col.ci, lwd = lwd.ci)
} else{
bnd <- predict(obj, q = ip, se = FALSE, ci = 'none')
lines(llip, bnd, col = col.ci, lwd = lwd.ci)
}
}
#############################
#' L-moment ratio diagram
#'
#' Plot the L-skew and L-kurtosis on the l-moment ratio diagram.
#'
#' @param x Matrix (n x 2) containing the L-skew and L-kurtosis
#' respectively.
#'
#' @param pos Position of the legend. See \link{legend}.
#' @param ... Other ploting parameters. See \link{par}.
#'
#' @export
#'
#' @examples
#' library(lmomco)
#'
#' ## Replicate 20 times the simulation of a GEV distribution
#' Fsim <- function() rAmax(100,c(100,20,-.1),'gev')
#' x <- replicate(20,Fsim())
#'
#' ## Compute the L-ratios and display the diagram
#' FLcoef <- function(z) lmoms(z)$ratio[3:4]
#' ll <- apply(x,2, FLcoef )
#'
#' Ldiag(t(ll), pch = 16, cex = .7)
#' points(t(rowMeans(ll)), col = 2, pch = 17, cex = 2)
#'
Ldiag <- function(x, pos = 'bottomright',
xlim = NULL, ylim = NULL, ...){
if(is.null(xlim)) xlim <- c(.9 * min(x[,1]), 1.1 * max(x[,1]))
if(is.null(ylim)) ylim <- c(.9 * min(x[,2]), 1.1 * max(x[,2]))
lmomco::plotlmrdia(lmomco::lmrdia(),
nopoints = TRUE, nolimits = TRUE,noaep = TRUE,
nogpa = TRUE, nogov = TRUE, xlim = xlim,
ylim = ylim)
points(x, ...)
if(!is.null(pos)) legend(pos, col = c(2,3,4,6), lty = c(2,1,2,1),
legend = c('GEV','GLO','LN3','PE3'))
}
######################################################################
#' Fit Generalized extreme value (GEV) distribution
#'
#' Fit a GEV distribution on annual maxima using the generalized
#' maximum likelihood method with Beta prior.
#' The output is of the class \code{amax}. See \link{FitAmax}.
#' Asymptotic result are computed like the maximum likelihood approach.
#'
#' @param x Data.
#' @param varcov Logical. Should the covariance matrix be returned.
#' @param mu,sig2 Mean and variance of the Beta prior.
#' @param method.optim Optimisation method used by \code{\link{optim}}
#' @param ... Other parameter pass to \code{\link{optim}}
#'
#' @section References:
#'
#' Martins, E.S., Stedinger, J.R., 2000. Generalized maximum-likelihood
#' generalized extreme-value quantile estimators for hydrologic data.
#' Water Resour. Res. 36, 737–744. https://doi.org/10.1029/1999WR900330
#'
#' @export
#'
#' @examples
#'
#' x <- ExtractAmax(flow~date, flowStJohn)$flow
#'
#' ## Using the default physiographic prior.
#' fit <- FitGev(x)
#' print(fit)
#' coef(fit)
#' vcov(fit)
#' predict(fit, ci = 'delta')
#'
#' ## A uniform prior on interlval -0.5 to 0.5 can be used to approximate
#' ## the maximum likelihood estimate.
#'
#' AIC(FitAmax(x, distr = 'gev', method ='mle'))
#' AIC(FitGev(x, mu = 0, sig2 = 1/12))
#'
#' ## A regional study can be performed by using an empirical prior
#' ## Here 20 sites with the same GEV distribution are simulated
#' ## without priors.
#' ## Then a regional estimate is obtained using an empirical prior
#'
#' xmat <- replicate(20, rAmax(20, c(100,3, -.1), 'gev') )
#' flist <- apply(xmat,2, FitAmax, distr = 'gev', varcov = FALSE)
#' pmat <- sapply(flist, getElement,'para')
#'
#' kap0 <- pmin(.5,pmax(-.5,pmat[3,]))
#'
#' FitGev(xmat[,1], mu = mean(kap0), sig2 = var(kap0))
#' FitAmax(xmat[,1], distr = 'gev', method = 'mle')
#'
FitGev <- function(x, varcov = TRUE, mu = -.1, sig2 = 0.015,
method.optim = 'BFGS',...){
## Verify that all values are finite values
if(!all(is.finite(x)))
stop('There is one (or more) non finite values')
## Compute the L-moments and associated parameters as starting value
lmm <- lmomco::lmoms(x)
startp <- lmomco::lmom2par(lmm,'gev')$para
## apply transformation to parameter to ensure positivness of scale parameter
## and modify the sign of the shape parameter to agree with the parametrization
startp[2] <- log(startp[2])
## Find the parameter of the prior Beta distribution based on
## mean and variance passed in arguments
mu <- mu + .5
a <- mu * ( (mu * (1-mu) / sig2) - 1)
b <- a * (1 - mu) / mu
if(a <= 0 | b <=0)
stop('The parameters of the Beta distribution are not correct')
## Function returning Negative log-likelihood of the model
Nllik <- function(p, w = 1){
-sum(dgev(x,p[1],exp(p[2]), p[3] ,log=TRUE)) -
w * dbeta(p[3]+.5, a, b, log = TRUE)
}
## optimize the Nllik to obtain the estimate
out <- optim(startp, Nllik, hessian = varcov, method = method.optim, ...)
## Compute the covariance matrix if necessary by iversion of the hessian
## matrix
if(varcov)
varcov <- chol2inv(chol(out$hessian))
else
varcov <- NA
## Create the output object
pout <- out$par
pout[2] <- exp(pout[2])
names(pout) <- c('xi','alpha','kappa')
ans <- list(para = pout,
distr = 'gev',
llik = -Nllik(out$par, w = 0),
varcov = varcov,
method = 'gml',
lmom = lmm$lambdas,
data = x,
prior = c(mu - 0.5, sig2))
class(ans) <- 'amax'
return(ans)
}
## Copy from R-package evd with modification of the parametrization
dgev <- function (x, xi = 0, alpha = 1, kappa = 0, log = FALSE){
## Verify if the parameter are valid
if (min(alpha) <= 0)
stop("invalid alpha")
if (length(kappa) != 1)
stop("invalid -kappa")
## center and alpha
x <- (x - xi)/alpha
if (kappa == 0){
## Case of Gumbel distribution
d <- log(1/alpha) - x - exp(-x)
} else {
nn <- length(x)
xx <- 1 - kappa * x
xxpos <- xx[xx > 0 | is.na(xx)]
alpha <- rep(alpha, length.out = nn)[xx > 0 | is.na(xx)]
d <- numeric(nn)
d[xx > 0 | is.na(xx)] <- log(1/alpha) - xxpos^(1/kappa) -
(1-1/kappa) * log(xxpos)
d[xx <= 0 & !is.na(xx)] <- -Inf
}
if (!log)
d <- exp(d)
return(d)
}
############################################################################
#' Basic probability functions for annual maxima
#'
#' Density, distribution function, quantile function and random generation
#' for various distribution used for modeling annual maxima.
#'
#' @name Amax
#' @param x,q Vector of quantiles.
#' @param p Vector of probabilities.
#' @param n Number of observations.
#' @param para Vector of parameters for the distribution.
#' @param distr Characters indicating the distribution family.
#' @param log Logical. If TRUE, probabilities \code{p} are given as
#' \code{log(p)}.
#'
#' @export
#'
#' @examples
#'
#' u <- runif(5)
#' u
#' x <- qAmax(u, c(100,3,.001), 'gno')
#' pAmax(x, c(100,3,.001), 'gno')
#'
#' x <- rAmax(100, c(100,30,0), 'gev')
#' sum(dAmax(x, c(100,30, 0), 'gev', log = TRUE))
#'
#'
#' @export
#' @rdname dAmax
dAmax <- function(x, para, distr, log = FALSE){
ans <- lmomco::dlmomco(x, lmomco::vec2par(para,distr))
if(log)
ans <- log(ans)
return(ans)
}
#' @export
#' @rdname dAmax
pAmax <- function(q, para, distr)
lmomco::plmomco(q, lmomco::vec2par(para,distr))
#' @export
#' @rdname dAmax
qAmax <- function(p, para, distr)
lmomco::qlmomco(p, lmomco::vec2par(para,distr))
#' @export
#' @rdname dAmax
rAmax <- function(n, para, distr)
lmomco::rlmomco(n, lmomco::vec2par(para,distr))
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.