#' metroponcfs: A package for implementing the Metropolis algorithm
#'
#' The metroponcfs provides many interesting functions for
#' implementing the Metropolis algorithm, but the two main help pages
#' of interest are \code{GeneralSingleMetropolis}, which implements
#' and demonstrate how to use the Metropolis algorithm, and
#' \code{c.GSMetrop}, which demonstrate how to combine different
#' chains in an object.
#'
#'
#' @docType package
#' @name metroponcfs
NULL
#' @title Common Teal captured and Ringed in Abberton Reservoir, UK,
#' and Camargue, France.
#'
#' @details
#' Dataset describing the recapture locations of common teals
#' initially captured and ringed in two Western European places: (i)
#' in Abberton Reservoir, UK, and (ii) in Camargue, sourthern
#' France. See Guillemain et al. for a complete description of this
#' dataset. To circumvent copyright issues, we provide here an
#' altered version of the dataset used by these authors: we selected a
#' random sample of 75\% of the recaptures of the original data,
#' keeping only the location information (i.e. only the x and y
#' coordinates of the recaptures), and we added a random noise to
#' these locations (we moved every bird recapture location randomly by
#' a distance comprised between 0 and 100 km).
#'
#'
#' @format A list with the following elements:
#'
#' \describe{
#'
#' \item{recaptures}{this element is a data.frame containing the
#' following information for each recapture of common teal: (i) a
#' variable named \code{date}, storing the number of years elapsed
#' between the initial capture and recapture, (ii) a variable named
#' \code{Abberton}, indicating whether the recaptured animal was
#' initially captured at Abberton Reservoir, UK (=1) or in Camargue,
#' southern France (=0), and (iii) two variables named \code{x} and
#' \code{y} containing the coordinates of the recapture (coordinate
#' system: Lambert azimuthal equal-area).}
#'
#' \item{rotationMatrix}{this 2x2 matrix M contains the two vectors
#' \code{cbind(m1, m2)} used by Guillemain et al. to rotate the
#' geographical coordinates in a new coordinate system (see Guillemain
#' et al.). Thus, if \code{x} is a vector of length two containing the
#' x and y coordinates of a location in the Lambert azimuthal
#' equal-area system, \code{z <- x\%*\%M} contains the coordinates of
#' this point in this new system.}
#'
#' \item{inverseRotationMatrix}{this 2x2 matrix \code{R} allows to
#' transform the coordinates of a point from the new coordinate system
#' to the old one. Thus, if \code{z} is a vector of length two
#' containing the coordinates of a point in the new coordinate system,
#' \code{x <- z\%*\%R} contains the coordinates of this point in the
#' original Lambert azimuthal equal-area system.}
#'
#' \item{knots}{this vector contains the coordinates of
#' the 26 knots in the new coordinate system used to define the
#' B-spline basis in the paper of Guillemain et al.}
#'
#' \item{lipum}{a list containing the parameters of the updating
#' mechanisms used in the Metropolis algorithm.}
#' }
#'
#' @references Guillemain M., Calenge C., Champagnon J. and Hearn
#' R. in prep. Determining the boundaries and plasticity of migratory
#' bird flyways: a Bayesian model for Common Teal Anas crecca
#' in Western Europe.
#'
"recteal"
#' @title Bayesian Model Fitted to Estimate the Boundaries of Common
#' Teal Flyways
#'
#' @details This model is fully described in the vignette "flyways".
#' Please type \code{vignette("flyways")} for more details.
#'
#' @format An object of class \code{CMM} (see
#' \code{help("c.GSMetrop")} for additional details on objects of
#' class \code{CMM}.
#'
#' @references Guillemain M., Calenge C., Champagnon J. and Hearn
#' R. in prep. Determining the boundaries and plasticity of migratory
#' bird flyways: a Bayesian model for Common Teal Anas crecca
#' in Western Europe.
"rectealmodel"
#' @title Bayesian Model Fitted to Estimate the Boundaries of Common
#' Teal Flyways (time effect)
#'
#' @details This model is fully described in the vignette "flyways".
#' Please type \code{vignette("flyways")} for more details.
#'
#' @format An object of class \code{CMM} (see
#' \code{help("c.GSMetrop")} for additional details on objects of
#' class \code{CMM}.
#'
#' @references Guillemain M., Calenge C., Champagnon J. and Hearn
#' R. in prep. Determining the boundaries and plasticity of migratory
#' bird flyways: a Bayesian model for Common Teal Anas crecca
#' in Western Europe.
"rectealmodeltime"
#' @title Results of the Sensitivity Analysis for the Rotation Angles
#'
#' @details This model is fully described in the vignette "flyways".
#' Please type \code{vignette("flyways")} for more details.
#'
#' @format A list with three elements named \code{fit} (containing the
#' model fits), \code{proportion} and \code{differenceWithRef}.
#'
#' @references Guillemain M., Calenge C., Champagnon J. and Hearn
#' R. in prep. Determining the boundaries and plasticity of migratory
#' bird flyways: a Bayesian model for Common Teal Anas crecca
#' in Western Europe.
"sensitivityAngles"
#' @title Results of the Sensitivity Analysis for the Number of B-spline Functions
#'
#' @details This model is fully described in the vignette "flyways".
#' Please type \code{vignette("flyways")} for more details.
#'
#' @format A list with three elements named \code{fit} (containing the
#' model fits), \code{proportion} and \code{differenceWithRef}.
#'
#' @references Guillemain M., Calenge C., Champagnon J. and Hearn
#' R. in prep. Determining the boundaries and plasticity of migratory
#' bird flyways: a Bayesian model for Common Teal Anas crecca
#' in Western Europe.
"sensitivityBsplines"
#' @title Find Starting Values for the Metropolis Algorithm
#' @export
#'
#' @details \code{findStartingValues} allows to find a list of starting
#' values for the Metropolis algorithm for the MCMC fitting of Bayesian models.
#'
#' @param support named list with one element per parameter (the names
#' of the list correspond to the name of the parameters). Each
#' element should be a vector of length 2 indicating the limits of the
#' support of the parameters (i.e. the min and max values in which the
#' starting values will be searched).
#' @param logposterior a function for the calculation of the log
#' posterior for the current model. This function must rely only on
#' the data provided in \code{lidat}. This function must have three
#' parameters: (i) \code{par} is the list of parameters, (ii)
#' \code{lidat} is the list containing the data, and (iii) \code{ctrl}
#' is a named list (see the help page of
#' \code{GeneralSingleMetropolis}). Note that the argument \code{ctrl}
#' is not used by the function here.
#' @param lidat a named list containing the data (the names of the
#' list correspond to the name of the variables used in logposterior,
#' combined with the parameters), required for the calculation of the
#' posterior
#' @param multiple a named list describing which parameters are
#' vectors of parameters. For example, if a model relies on a single
#' parameter \code{"a"} and two vectors of parameters \code{"theta1"}
#' and \code{"theta2"} of length 5 and 6 respectively, the argument
#' \code{multiple} should take the value: \code{list(theta1=5,
#' theta2=6)}. Note that single parameters are not indicated in this
#' list.
#' @param nrand the number of starting values to generate before a
#' result is returned (see details).
#' @param method the method to be used by the function (see details).
#' @param ndispersed integer. When \code{method="dispersed"},
#' indicates how many dispersed starting values should be
#' returned. Should be greater than \code{nrand}.
#' @param info whether information on the generation process should be
#' displayed to the user.
#'
#' @details Three methods are proposed to generate one or several
#' vectors of starting values. All of them rely on the generation of
#' \code{nrand} plausible starting values (i.e. leading to finite
#' log-posterior) randomly sampled in the support of the
#' parameters. Then: (i) the (default) method \code{"onebest"} chooses
#' the starting value leading to the highest log-posterior, (ii) the
#' method \code{"dispersed"} chooses \code{ndispersed} values the most
#' different among each other (to start several chains), (iii) the
#' method \code{"all"} return all sampled values. The user can play
#' with the "support" of the parameters to find more or less dispersed
#' values.
#'
#' @return If \code{method=="onebest"}, a named list of starting
#' values for the parameters, that can be used directly as an argument
#' to \code{GeneralSingleMetropolis}. For other methods, a list of
#' lists of starting values.
#' @seealso \code{\link{GeneralSingleMetropolis}}
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
#' @import graphics
#' @import stats
#' @examples
#'
#' ## We generate data
#' x1 <- rnorm(100)
#' x2 <- rnorm(100)
#' x3 <- rnorm(100)
#' y <- 1+x1+0.5*x2+1.5*x3+rnorm(100, sd=0.5)
#'
#' ## Our data
#' lid <- list(x=cbind(x1,x2,x3), y=y)
#'
#' ## 5 parameters: intercept + 3 slopes + residual variance
#' ## For the sake of demonstration, we put the three slopes in
#' ## a vector theta for the estimation.
#' ## The list of starting values should therefore contain: (i) theta,
#' ## (ii) intercept, (iii) residprec
#' ##
#' ## The function for the posterior is:
#' lpost <- function(par, lidat, ctrl)
#' {
#' ## prior:
#' parb <- par
#' parb$residprec <- NULL
#' lprior <- sum(dnorm(unlist(parb), mean=0, sd=100)) ## vague prior
#' ## (uniform prior for the residual precision, we do not add this constant)
#'
#' mu <- lidat$x%*%par$theta+par$intercept
#' ## log-posterior
#' return(sum(lprior + dnorm(lidat$y, mean=mu, sd = 1/sqrt(par$residprec), log=TRUE)))
#' }
#'
#' ## We define large support for all parameters
#' support <- list(theta=c(-10,10), intercept=c(-10,10), residprec=c(0.0001, 10))
#' ## only theta is multiple (3 elements), so:
#' multiple <- list(theta=3)
#'
#' ## Default method: finds the best starting value among 1000 sampled values
#' ## The plot shows the distribution of the sampled posterior (up to a constant)
#' ## the best value is returned in sv
#' (sv <- findStartingValues(support, lpost, lid, multiple, nrand=1000))
#'
#' ## Same, but keeps 4 most dispersed starting values among 100 generated values:
#' (sv2 <- findStartingValues(support, lpost, lid, multiple, nrand=100,
#' method="dispersed", ndispersed=4))
#'
findStartingValues <- function(support, logposterior, lidat,
multiple=list(), nrand=10,
method=c("onebest","dispersed", "all"),
ndispersed=3, info=TRUE)
{
if (length(names(support))<1)
stop("support should be a named list, with one element per parameter")
if (!all(sapply(support, length)==2))
stop("Each element of support should be a vector of length 2\ncontaining the limits (min, max) of the support for the corresponding parameter")
if (!all(names(multiple)%in%names(support)))
stop("all the names of the parameter multiple should correspond to names in support")
method <- match.arg(method)
if (method=="dispersed") {
if (ndispersed>nrand)
stop("ndispersed should be <= nrand")
}
lpar <- lapply(1:nrand, function(r) {
if (info)
cat("Iteration", r, "out of", nrand,"\r")
ok <- FALSE
while (!ok) {
parinit <- lapply(1:length(support), function(i) {
n <- ifelse(names(support)[i]%in%names(multiple),
multiple[[names(support)[i]]],1)
return(runif(n, support[[i]][1], support[[i]][2]))
})
names(parinit) <- names(support)
post <- logposterior(parinit, lidat, list(namePar="No221cc9328Parameter23",iter=1))
if (!is.infinite(post)) {
ok <- TRUE
}
}
attr(parinit,"lposterior") <- logposterior(parinit,lidat, list(namePar="No221cc9328Parameter23",iter=1))
return(parinit)
})
sp <- sapply(lpar, function(x) attr(x, "lposterior"))
if (info)
plot(sort(sp, decreasing=TRUE), ylab = "log-posterior", xlab = "Trials")
if (method=="all")
return(lpar)
lp <- lpar[[which.max(sp)]]
if (method=="onebest")
return(lp)
if (method=="dispersed") {
if (info)
cat("Identifying dispersed values...\r")
la <- do.call(rbind,lapply(lpar, function(x) unlist(x)))
la2 <- scale(la)
## Solution to find proposed here:
## https://stackoverflow.com/questions/22152482/choose-n-most-distant-points-in-r
di <- as.matrix(dist(la2))
pa <- 1:nrow(la2)
while (length(pa) > ndispersed) {
cdists = rowSums(di)
closest <- which(cdists == min(cdists))[1]
pa <- pa[-closest]
di <- di[-closest,-closest]
}
return(lpar[pa])
}
}
.iterMetrop <- function(par, lidat, logposterior, lipum, NamesP, UpdatingMechanism,
liopt, composition, debug, tempf2, optionsDebug)
{
## Posterior courante (inutilisée si optimisation de la mise à jour d'un paramètre)
postcur <- logposterior(par, lidat, list(namePar="No221cc9328Parameter23",iter=1))
opti <- FALSE
if (debug) {
cat("##\n##\n## LLLInit: Log-Posterior:", postcur, "\n",file=tempf2, append=TRUE)
if (optionsDebug$showPar)
saveprm("## PPPInit: Value of the parameters:", "par", par, tempf2)
if (optionsDebug$showSeed) {
presentseed <- .save_rng()
saveprm("## PPPInit: restore the seed", "presentseed", presentseed, tempf2)
cat("metroponcfs:::.restore_rng(presentseed)\n", file=tempf2, append=TRUE)
}
}
## "working parameter"
parw <- par
## liste d'acceptation
accept <- list()
## Ordre des paramètres
nesp <- 1:length(parw)
if (composition=="random")
nesp <- sample(1:length(NamesP))
if (debug) {
if (composition=="random")
saveprm("## NNNInit: order of sampling (random composition):", "nesp", nesp, tempf2)
cat("\n\n## ** Starting per-parameters update \n", file=tempf2, append=TRUE)
}
## Mise à jour de a:
for (j in 1:length(parw)) {
## Les paramètres nécessaires
nam <- NamesP[nesp[j]]
fun <- UpdatingMechanism[nesp[j]]
if (debug) {
cat("\n## ********* Updating Parameter", nam, "\n", file=tempf2, append=TRUE)
}
## Si optimisation pour le paramètre précédent et pas pour le paramètre courant,
## recalculer la posterior courante
if (opti&(!(nam%in%liopt))) {
postcur <- logposterior(par, lidat, list(namePar="NoBPo221ccParameter23",iter=1))
}
## Mise à jour de opti pour le paramètre courant
opti <- nam%in%liopt
## Mise en œuvre de la fonction
liparam <- list(par=parw, lidat=lidat, nam=nam, pum=lipum[[nam]],
ctrl=list(namePar=nam,iter=1),
logposterior=logposterior,
postcur=postcur, optimiser=opti, debug=debug,
dumpfile=tempf2, composition)
resm <- do.call(fun, liparam)
## Résultats
parw[[nam]] <- resm$parm
accept[[nam]] <- resm$accept
postcur <- resm$postcur
}
parw <- lapply(1:length(NamesP), function(i) parw[[NamesP[i]]])
accept <- lapply(1:length(NamesP), function(i) accept[[NamesP[i]]])
names(parw) <- NamesP
names(accept) <- NamesP
if (debug)
cat("\n\n\n\n\n", file=tempf2, append=TRUE)
return(list(par=parw, accept=accept, postcur=postcur))
}
## Solution inspired by Ben Bolker to save state of RNG
## here:
## https://stackoverflow.com/questions/13997444/print-the-current-random-seed-so-that-i-can-enter-it-with-set-seed-later
.save_rng <- function() {
if (!exists(".Random.seed")) {
runif(1)
}
seed <- get(".Random.seed", .GlobalEnv)
RNGkind <- RNGkind()
sr <- list(seed=seed, RNGkind=RNGkind)
return(sr)
}
.restore_rng <- function(sr) {
do.call("RNGkind",as.list(sr$RNGkind))
assign(".Random.seed", sr$seed, .GlobalEnv)
}
## Debug:
saveprm <- function(title, name, value, file)
{
cat(title, "\n", file=file, append=TRUE)
cat(paste0(name, " <- ", paste(deparse(value), collapse="\n")),
file=file, append=TRUE)
cat("\n", file=file, append=TRUE)
}
#' @title Updating mechanisms used in the metropolis algorithm
#' @export
#' @details \code{singleStep} implements an updating mechanism for a
#' unique parameter based on a normal distribution.
#' \code{MultipleIndependentSteps} implements the same updating
#' mechanism for a vector of parameters. \code{MultiNormalStep}
#' implements an updating mechanism relying on the sampling of a
#' multinormal distribution. These functions are used by the package,
#' but are not to be used directly by the user.
#'
#' @param par a named list containing the parameters of the model (the
#' names of the list correspond to the names of the parameters)
#' @param lidat a named list containing the variables and constants
#' required by the model (especially the function \code{logposterior}
#' @param nam character string. The name of the parameter to be
#' updated.
#' @param pum for \code{singleStep}, the standard deviation of the
#' Gaussian distribution used to generate the proposal. For
#' \code{MultipleIndependentSteps}, a vector containing the standard
#' deviations for the Gaussian distribution used to generate the
#' proposal of every element of the vector. For \code{MultiNormalStep},
#' the covariance matrix of the multinormal distribution.
#' @param ctrl a named list with two parameters: (i) \code{namePar} is
#' the name of the parameter in \code{par} that is updated, (ii) if
#' this parameter is a vector where each component is updated in turn
#' (e.g. for \code{MultipleIndependentSteps}), \code{iter} indicates
#' the element of this vector that is updated (this element is ignored
#' in other cases).
#' @param logposterior a function used to calculate the log-posterior
#' probability of \code{par} (up to a constant). This function must
#' have two arguments: \code{par} is the list of parameters and
#' \code{lidat} is the list containing the data used in the model.
#' @param postcur the current value of the posterior, before updating.
#' @param optimiser logical. If \code{TRUE}, the log-posterior
#' calculation is optimized for the current parameter (i.e. the
#' posterior is proportional to the log-posterior but the
#' proportionality constant is not necessarily the same for other
#' optimized parameters, so that the current posterior should be
#' calculated before every step). If \code{FALSE} the function
#' \code{logposterior} returns the exact log-posterior. In other
#' words, if \code{optimiser} is TRUE, the function \code{singleStep},
#' etc. calculates the current posterior (prior to updating) and does
#' not take into account the value of \code{postcur} passed to the
#' function. If FALSE, the function relies on the value of
#' \code{postcur} passed as argument for the updating.
#' @param debug logical. If \code{TRUE} the updating should be run in
#' debugging mode.
#' @param dumpfile character. The name of the debugging file.
#' @param composition character. How composition of parameters are
#' managed (see the help page of
#' \code{GeneralSingleMetropolis}). Currently, only used by
#' \code{MultipleIndependentSteps}.
#'
#' @return A list with three elements: (i) parm is the updated vector
#' of parameters, (ii) postcur is the value of the (optionally
#' optimized) posterior after updating, and (iii) accept is a vector
#' containing TRUE if an element proposed has been accepted and FALSE
#' otherwise
#'
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
#' @seealso \code{\link{GeneralSingleMetropolis}}
#' @importFrom MASS mvrnorm
#' @import graphics
#' @import stats
singleStep <- function(par, lidat, nam, pum, ctrl, logposterior,
postcur, optimiser, debug, dumpfile, composition)
{
parw <- par
prev <- parw[[nam]]
if (debug)
cat("## >> singleStep updating\n", file=dumpfile, append=TRUE)
## si optimisation, on doit calculer la posterior à priori
## sinon, on prend l'argument
if (optimiser) {
postcur <- logposterior(parw, lidat, ctrl)
}
nex <- prev+rnorm(1, mean=0, sd=pum)
parw[[nam]] <- nex
postnext <- logposterior(parw, lidat, ctrl)
alpha <- min(c(1,exp(postnext-postcur)))
un <- runif(1)<alpha
accept <- as.numeric(un)
if (debug) {
saveprm(paste0("## <1> ", "Old value of ", nam, " (log-posterior=", postcur,"):"),
"prev", prev, dumpfile)
saveprm(paste0("## <2> ", "Proposal value of ", nam, " (log-posterior=", postnext,"):"),
"nex", nex, dumpfile)
cat(paste0("## <3> Metropolis ratio: min(1,",
exp(postnext-postcur)),")\n", file=dumpfile, append=TRUE)
saveprm(c("## <4> We accept", "## <4> We reject")[c(un,!un)],
paste0("par[[\"", nam, "\"]] "), ifelse(un, nex, prev), dumpfile)
}
parm <- ifelse(un, nex, prev)
pc <- ifelse(un, postnext, postcur)
return(list(parm=parm, postcur=postcur, accept=accept))
}
#' @rdname singleStep
#' @export
MultipleIndependentSteps <- function(par, lidat, nam, pum, ctrl, logposterior,
postcur, optimiser, debug, dumpfile, composition)
{
parw <- par
parm <- parw[[nam]]
if (debug)
cat("## >> MultipleIndependentSteps updating\n", file=dumpfile, append=TRUE)
accept <- rep(0,length(parm))
nesp <- 1:length(parm)
if (composition=="random")
nesp <- sample(1:length(parm))
if (debug) {
if (composition=="random")
saveprm("## Composition: order of sampling for this parameter:", "nespb",
nesp, dumpfile)
cat("## \n\n## ** Starting per-parameters update \n\n", file=dumpfile, append=TRUE)
}
for (i in 1:length(parm)) {
ctrl$iter <- nesp[i]
if (optimiser) {
postcur <- logposterior(parw, lidat, ctrl)
}
prev <- parm[nesp[i]]
nex <- prev+rnorm(1, mean=0, sd=pum[nesp[i]])
parm[nesp[i]] <- nex
parw[[nam]] <- parm
postnext <- logposterior(parw, lidat, ctrl)
alpha <- min(c(1,exp(postnext-postcur)))
un <- runif(1)<alpha
accept[nesp[i]] <- un
if (debug) {
cat(paste0("## EEEEi Element ", nesp[i], " of the parameter ", nam, "\n"),
file=dumpfile, append=TRUE)
saveprm(paste0("## <1> ", "Old value of ", nam, "[", nesp[i],
"] (log-posterior=", postcur,"):"),
"prev", prev, dumpfile)
saveprm(paste0("## <2> ", "Proposal value of ", nam, "[", nesp[i],
"] (log-posterior=", postnext,"):"),
"nex", nex, dumpfile)
cat(paste0("## <3> Metropolis ratio: min(1,",
exp(postnext-postcur)),")\n", file=dumpfile, append=TRUE)
saveprm(c("## <4> We accept", "## <4> We reject")[c(un,!un)],
paste0("par[[\"", nam, "\"]][",nesp[i],"] "), ifelse(un, nex, prev), dumpfile)
}
parm[nesp[i]] <- ifelse(un, nex, prev)
postcur <- ifelse(un, postnext, postcur)
}
return(list(parm=parm, postcur=postcur, accept=accept))
}
#' @rdname singleStep
#' @export
MultiNormalStep <- function(par, lidat, nam, pum, ctrl, logposterior,
postcur, optimiser, debug, dumpfile, composition)
{
parw <- par
prev <- parw[[nam]]
if (debug)
cat("## >> MultiNormalStep updating\n", file=dumpfile, append=TRUE)
if (optimiser) {
postcur <- logposterior(parw, lidat, ctrl)
}
nex <- prev+MASS::mvrnorm(1, mu=rep(0,ncol(pum)), Sigma=pum)
parw[[nam]] <- nex
postnext <- logposterior(parw, lidat, ctrl)
alpha <- min(c(1,exp(postnext-postcur)))
un <- runif(1)<alpha
accept <- as.numeric(un)
if (debug) {
saveprm(paste0("## <1> ", "Old value of ", nam, " (log-posterior=", postcur,"):"),
"prev", prev, dumpfile)
saveprm(paste0("## <2> ", "Proposal value of ", nam, " (log-posterior=", postnext,"):"),
"nex", nex, dumpfile)
cat(paste0("## <3> Metropolis ratio: min(1,",
exp(postnext-postcur)),")\n", file=dumpfile, append=TRUE)
rrr <- prev
if (un)
rrr <- nex
saveprm(c("## <4> We accept", "## <4> We reject")[c(un,!un)],
paste0("par[[\"", nam, "\"]] "), rrr, dumpfile)
}
if (un) {
parm <- nex
postcur <- postnext
} else {
parm <- prev
}
return(list(parm=parm, postcur=postcur, accept=accept))
}
#' @title Implements one Markov chain with the Metropolis algorithm
#' @export
#'
#' @details \code{GeneralSingleMetropolis} implements the Metropolis
#' algorithm for the MCMC fitting of Bayesian models.
#' \code{restartGSM} can be used to restart a model for \code{another}
#' iterations. \code{reloadGSM} can be used to return the GSMetro
#' object saved in a file by the backup mechanism of
#' \code{GeneralSingleMetropolis}. \code{defaultListUGSM} can be used
#' to generate default values for \code{listUpdating} (defaulting to
#' \code{"sis"} for parameters of length 1 and to \code{"mis"} for
#' vectors of parameters). This list can then be manually altered
#' (e.g. changing the updating mechanism to "mns" for some
#' parameters).
#'
#' @param parInit named list with one element per parameter (the
#' names of the list correspond to the name of the parameters),
#' containing the initial values for the parameters
#' @param lidat a named list containing the data (the names of the
#' list correspond to the name of the variables used in logposterior,
#' combined with the parameters), required for the calculation of the
#' posterior.
#' @param logposterior a function for the calculation of the log
#' posterior for the current model. This function must rely only on
#' the data provided in \code{lidat}. This function must have three
#' parameters: (i) \code{par} is the list of parameters, (ii)
#' \code{lidat} is the list containing the data, and (iii)
#' \code{ctrl} is a named list with two elements: (i) an element
#' \code{namePar} is a character string containing the name of the
#' parameter in \code{par} that is updated, (ii) if this parameter is
#' a vector where each component is updated in turn (e.g. when
#' \code{MultipleIndependentSteps} is the updating mechanism),
#' \code{iter} indicates the element of this vector that is updated
#' (this element is ignored in other cases). It is possible to define
#' a log-posterior that does not make use of \code{ctrl} (i.e., which
#' returns the same value whatever the parameter that is updated),
#' but even in this case, the function should have an argument named
#' \code{ctrl}. Note that the argument \code{ctrl} is essentially
#' useful when the model contains many parameters
#' (e.g. overdispersion residuals).
#' @param lipum a named list containing the parameters of the
#' updating mechanisms (see the help page of the updating mechanisms
#' for a description of the required parameters). The names of the
#' list correspond to the name of the parameters of the model
#' (i.e. \code{names(parInit)}).
#' @param listUpdating named list with one element parameter (the
#' names of the list correspond to the name of the parameters),
#' each one containing one of the following character strings:
#' (i) "mis", which implements the function \code{MultipleIndependentSteps}
#' as updating function for the corresponding parameter, (ii) "sis",
#' which implements the function \code{singleStep} as updating function
#' for the corresponding parameter, (iii) "mns", which implements
#' the function \code{MultiNormalStep}. If \code{NULL}, the function
#' \code{defaultListUGSM} is used internally to generate a default
#' value ("mis" or "sis").
#' @param liopt optionally, a vector containing the name of the
#' parameters for which the log-posterior is optimized (i.e. the
#' function logposterior returns a value that is proportional to the
#' log-posterior, and not the log-poserior itself, see the help page
#' of \code{singleStep}).
#' @param nrepet an integer giving the number of iterations required
#' for the MCMC algorithm.
#' @param nburnin an integer giving the number of burnin iterations
#' required
#' @param thinPar an integer giving the number of steps separating
#' the storage of two parameter vectors generated by the algorithm
#' @param thinAcc an integer giving the number of steps separating
#' the storage of the vector describing whether the proposal has been
#' accepted or not
#' @param composition character. Determines in which order the
#' parameters are updated. Either \code{"fixed"} (the default), in
#' which case the parameters are updated in the order provided in
#' \code{parInit}, or \code{"random"}, in which case the parameters
#' are updated in a random order at each step.
#' @param verbose logical. Whether information should be displayed to
#' the user.
#' @param saveResults logical. Whether the results should be saved in a file.
#' @param saveEvery integer. When should the program save the list of
#' generated values (for backup)?
#' @param fileSave character string. The name of the Rdata file used to
#' save the results.
#' @param debug logical. Whether the chain should be debugged (in this
#' case, the whole state of the chain is stored in a dump file,
#' storing a list with one element per iteration, storing the whole
#' process). Debug mode is not possible for the function \code{restartGSM}.
#' @param optionsDebug list with two elements named \code{showPar} and
#' \code{showSeed} indicating what to show in the dump file at each
#' iteration (at each iteration, the seed of the random number
#' generator can be saved, as well as the value of the list of
#' parameters, so that the user can focus on a particular step without
#' bothering of the other steps).
#' @param x an object of class GSMetrop.
#' @param resuParms an object of class GSMetrop.
#' @param another an integer giving the number of iterations required
#' for the MCMC algorithm.
#' @param \dots additional arguments to be passed to or from other
#' functions
#'
#'
#' @details This functions backs up regularly the state of the chain
#' (every \code{saveEvery} iterations) in a list of class
#' \code{GSMetrop} named \code{libackup}, saved in a file named
#' \code{fileSave}. This object can be retrieved with \code{reloadGSM}.
#'
#' @note The debug mode produces a temporary file with a name starting
#' by \code{dumpmetrop***.R}, which contains the R code corresponding
#' to the state of the chain at each step (old value of the parameter,
#' proposed value, etc.). This file contains all the elements required
#' to understand the state of the chain. At the top of the file, the
#' function includes R code that allows to explore the chain in
#' another session (i.e. the code in this file does not rely on the
#' global environment). The arguments of the functions are restored in
#' the environment at the beginning of the file.
#'
#' @return A list of class "GSMetrop", where each element corresponds
#' to one parameter, and where each element is a matrix containing the
#' generated values as rows.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
#' @examples
#' ## Generates a dataset:
#' x <- rnorm(100)
#' y <- 1+ 2*x + rnorm(100, sd = 0.1)
#'
#' ## Linear regression: estimate a, b, sigma in
#' ## y = a + b*x + epsilon
#' ## epsilon ~ dnorm(0, sigma)
#' ## We will model lsigma=log(sigma) to ensure it is >0
#'
#' ## Log-posterior
#' logp <- function(par, lidat, ctrl)
#' {
#' ## Current values of the parameters
#' a <- par$a
#' b <- par$b
#' sigma <- exp(par$lsigma) ## sigma=exp(log(sigma))
#'
#' ## log-Prior:
#' lprior <- dnorm(a, mean=0, sd=100, log=TRUE)
#' lprior <- lprior + dnorm(b, mean=0, sd=100, log=TRUE)
#' ## uniform prior on log-sigma
#'
#' ## log-likelihood
#' llike <- sum(dnorm(lidat$y, mean=lidat$x*b+a, sd=sigma, log=TRUE))
#'
#' ## log-posterior
#' return(llike+lprior)
#' }
#'
#' ## Elements required for the fit
#' parinit <- list(a=0, b=1, lsigma=0) ## initial values
#'
#' lipum <- list(a=0.1, b=0.1, lsigma=1) ## test standard deviation of the proposals
#'
#' lidat <- list(x=x, y=y) ## the data
#'
#' \dontrun{
#' gsm <- GeneralSingleMetropolis(parinit, lidat, logp, lipum,
#' nrepet=10000, saveResults = FALSE)
#' gsm
#'
#' ## Plot the chain
#' plot(gsm)
#'
#' ## We increase the burnin
#' gsm2 <- increaseBurnin(gsm, newBurnin=200)
#'
#' ## Ok
#' plot(gsm2)
#'
#' ## show a summary of the results
#' summary(gsm2, parameters=TRUE, accept=TRUE)
#'
#' ## Try to use a multinormal updating for a and b
#' ## we change the logposterior, and define theta
#' ## as c(a,b)
#' logp2 <- function(par, lidat, ctrl)
#' {
#' ## Current values of the parameters
#' theta <- par$theta
#' a <- theta[1]
#' b <- theta[2]
#' sigma <- exp(par$lsigma)
#'
#' ## log-Prior:
#' lprior <- dnorm(a, mean=0, sd=100, log=TRUE)
#' lprior <- lprior + dnorm(b, mean=0, sd=100, log=TRUE)
#' ## uniform prior on sigma
#'
#' ## log-likelihood
#' llike <- sum(dnorm(lidat$y, mean=lidat$x*b+a, sd=sigma, log=TRUE))
#'
#' ## log-posterior
#' return(llike+lprior)
#' }
#'
#' ## We get the last value of the parameters generated in gsm2
#' pr <- getParameterVector(gsm2, nrow(gsm2[[1]]))
#'
#' ## We change the variables
#' parinit2 <- list(theta=c(pr$a, pr$b), lsigma=pr$lsigma) ## initial values
#' listUpdating2 <- list(theta="mns", lsigma="sis") ## now, theta is updated with mns
#' lipum2 <- list(theta=cov(cbind(gsm2$a, gsm2$b)), lsigma=1)
#'
#' ## And we start again, saving the results in ficxample231.Rdata
#' gsm3 <- GeneralSingleMetropolis(parinit2, lidat, logp2, lipum2, listUpdating2,
#' nrepet=10000, fileSave="ficxample231.Rdata")
#'
#' gsm3
#'
#' plot(gsm3)
#'
#' summary(gsm3, parameters=TRUE, accept=TRUE)
#'
#' ## Demonstrate how to continue the iterations:
#' gsm4 <- restartGSM(gsm3, another=10000)
#' gsm4
#'
#' ## demonstrates how to reload a file:
#' gsm5 <- reloadGSM("ficxample231.Rdata")
#'
#' ## housekeeping
#' file.remove("ficxample231.Rdata")
#' }
GeneralSingleMetropolis <- function(parInit, lidat, logposterior, lipum, listUpdating=NULL,
liopt = NULL, nrepet=999, nburnin=10, thinPar = 1,
thinAcc = thinPar,
composition=c("fixed","random"),
verbose=TRUE, saveResults=TRUE, saveEvery=1000,
fileSave="saveMetrop.Rdata", debug=FALSE,
optionsDebug=list(showSeed=FALSE, showPar=TRUE))
{
## Starts duration
startTime <- proc.time()
## Generates listUpdating
if (is.null(listUpdating))
listUpdating <- defaultListUGSM(parInit)
## Checks length
if (length(parInit)!=length(listUpdating))
stop("listUpdating does not have the same length as parInit")
## Checks names
if (!all(names(parInit)==names(listUpdating)))
stop("listUpdating has names different from the parInit")
## checks updating mechanisms
if (!all(unlist(listUpdating%in%c("mis", "mns", "sis"))))
stop("Not allowed updating mechanisms")
## checks the composition
composition <- match.arg(composition)
## checks arguments of logposterior
ar <- names(formals(logposterior))
if (!all((ar==c("par", "lidat", "ctrl"))))
stop("The log-posterior must have three arguments named par, lidat and ctrl")
## Checks that the logposterior works for all parameters (at least for the
## first element)
restmp <- lapply(1:length(parInit), function(i) {
ii <- try(logposterior(parInit, lidat,
list(namePar=names(parInit)[i], iter=1)))
if (inherits(ii, "try-error"))
stop(paste("The evaluation of the log-posterior fails for parameter",
names(parInit)[i]))
})
lp <- logposterior(parInit, lidat,
list(namePar=names(parInit)[1], iter=1))
if (is.infinite(lp))
stop("bad starting values for the parameters (infinite log-posterior)")
## The seed
seedinit <- .save_rng()
## If debugging
if (debug) {
## Check the debugging options
na <- c("showPar", "showSeed")
def <- list(showSeed=FALSE, showPar=TRUE)
if (!all(na%in%names(optionsDebug))) {
warning("Some elements are missing in optionsDebug.\nDefault values are used for these elements")
optionsDebug <- lapply(na, function(i) {
if (!any(names(optionsDebug)==i))
return(def[[i]])
return(optionsDebug[[i]])
})
names(optionsDebug) <- na
}
tempf2 <- tempfile("dumpmetrop", tmpdir=".", fileext = ".R")
tempf <- tempfile("dumpargs", tmpdir=".", fileext = ".Rdata")
liargs <- list(parInit=parInit, lidat=lidat, logposterior=logposterior,
lipum=lipum, listUpdating=listUpdating,
liopt = liopt, nrepet=nrepet, nburnin=nburnin, thinPar = thinPar,
thinAcc = thinAcc, composition=composition,
verbose=verbose, saveResults=saveResults, saveEvery=saveEvery,
fileSave=fileSave, debug=TRUE, seedInit=seedinit)
save(liargs, file=tempf)
cat("Debugging mode: the file", tempf2,"describes the MCMC process\n\n")
cat("## *****************************************************\n",
"## * *\n",
"## * Debug output for GeneralSingleMetropolis *\n",
"## * *\n",
"## *****************************************************\n\n\n",
"## The arguments of the functions are stored in the file ", tempf,
"\n",
"## Restores the environment\n",
"load(\"",tempf,"\")\n\n",
"## Assigns the value of the arguments\n",
"for (i in names(liargs)) {\nassign(i, liargs[[i]], envir=.GlobalEnv)\n}\n\n",
"## Restore the seed\n",
"metroponcfs:::.restore_rng(seedInit)\n\n",
file=tempf2, sep="")
saveprm("## Starting value of the parameters", "par", parInit, tempf2)
cat("\n\n\n\n\n\n\n\n\n", append=TRUE, file=tempf2)
} else {
tempf2 <- NA
}
## We pass the list to par
par <- parInit
## The names of the parameters
NamesP <- names(par)
## we transform the code for updating parameters into function names
upd <- as.numeric(factor(sapply(names(par), function(x) listUpdating[[x]]),
levels=c("mis","mns","sis")))
UpdatingMechanism <- c("MultipleIndependentSteps","MultiNormalStep","singleStep")[upd]
## List of generated parameters
liparms <- list()
## List of accepted mechanisms
liacc <- list()
## function used for saving
saveMetrop <- function(r)
{
resuParms <- lapply(names(par), function(na) {
do.call(rbind,lapply(liparms, function(x) {
x[[na]]
}))
})
resuAcc <- lapply(names(par), function(na) {
do.call(rbind,lapply(liacc, function(x) {
x[[na]]
}))
})
seedend <- .save_rng()
names(resuParms) <- names(par)
names(resuAcc) <- names(par)
attr(resuParms, "AcceptationRate") <- resuAcc
attr(resuParms, "thinPar") <- thinPar
attr(resuParms, "thinAcc") <- thinAcc
attr(resuParms, "composition") <- composition
attr(resuParms, "nburnin") <- nburnin
prestime <- proc.time()
attr(resuParms, "duration") <- (prestime-startTime)
attr(resuParms, "listUpdating") <- listUpdating
attr(resuParms, "lidat") <- lidat
attr(resuParms, "lipum") <- lipum
attr(resuParms, "logposterior") <- logposterior
attr(resuParms, "verbose") <- verbose
attr(resuParms, "saveResults") <- saveResults
attr(resuParms, "saveEvery") <- saveEvery
attr(resuParms, "fileSave") <- fileSave
attr(resuParms, "seedInit") <- seedinit
attr(resuParms, "seedEnd") <- seedend
if (!is.null(liopt))
attr(resuParms,"liopt") <- liopt
attr(resuParms, "fileSave") <- fileSave
class(resuParms) <- "GSMetrop"
libackup <- resuParms
if (saveResults)
save(libackup, file=fileSave)
return(resuParms)
}
## Main loop
for (r in 1:nrepet) {
if (verbose)
cat("Iteration", r, "\r")
if (debug)
cat("## ******************** MCMC Iteration", r, " #######################\n",
file=tempf2, append=TRUE)
im <- .iterMetrop(par, lidat, logposterior, lipum, NamesP, UpdatingMechanism,
liopt, composition, debug, tempf2, optionsDebug)
par <- im$par
accept <- im$accept
## Storage
if ((r%%thinPar==0)&(r>nburnin)) {
liparms[[length(liparms)+1]] <- par
}
## Acceptation
if (r%%thinAcc==0&(r>nburnin)) {
liacc[[length(liacc)+1]] <- accept
}
## save?
if ((r%%saveEvery==0)&(r>nburnin)&(!is.na(fileSave))) {
resuParms <- saveMetrop(r)
}
}
## Results
if (!is.na(fileSave)) {
resuParms <- saveMetrop(r)
}
class(resuParms) <- "GSMetrop"
return(resuParms)
}
#' @rdname GeneralSingleMetropolis
#' @export
restartGSM <- function(resuParms, another=1000)
{
## Checks
if (!inherits(resuParms, "GSMetrop"))
stop("incorrect class")
## No need to check the other arguments, the class
## "backupMetropolis" says it all
## Backs up the global seed
globseed <- .save_rng()
## Reassigns arguments
liparms <- lapply(1:nrow(resuParms[[1]]), function(i) {
lapply(resuParms, function(x) x[i,])
})
resuAcc <- attr(resuParms, "AcceptationRate")
liacc <- lapply(1:nrow(resuAcc[[1]]), function(i) {
lapply(resuAcc, function(x) x[i,])
})
resuAcc <- attr(resuParms, "AcceptationRate")
thinPar <- attr(resuParms, "thinPar")
thinAcc <- attr(resuParms, "thinAcc")
composition <- attr(resuParms, "composition")
nburnin <- attr(resuParms, "nburnin")
liopt <- attr(resuParms, "liopt")
nburnin <- attr(resuParms, "nburnin")
# (prestime-startTime) <- attr(resuParms, "duration")
listUpdating <- attr(resuParms, "listUpdating")
lidat <- attr(resuParms, "lidat")
lipum <- attr(resuParms, "lipum")
logposterior <- attr(resuParms, "logposterior")
verbose <- attr(resuParms, "verbose")
saveResults <- attr(resuParms, "saveResults")
saveEvery <- attr(resuParms, "saveEvery")
fileSave <- attr(resuParms, "fileSave")
seedinit <- attr(resuParms, "seedInit")
seedend <- attr(resuParms, "seedEnd")
## restore the seed
.restore_rng(seedend)
## current value of the parameters
par <- getParameterVector(resuParms, nrow(resuParms[[1]]))
## The names of the parameters
NamesP <- names(par)
## we transform the code for updating parameters into function names
upd <- as.numeric(factor(sapply(names(par), function(x) listUpdating[[x]]),
levels=c("mis","mns","sis")))
UpdatingMechanism <- c("MultipleIndependentSteps","MultiNormalStep","singleStep")[upd]
## measure Time
duration <- attr(resuParms, "duration")
startTime <- proc.time()
## function used for saving
saveMetrop <- function(r)
{
resuParms <- lapply(names(par), function(na) {
do.call(rbind,lapply(liparms, function(x) {
x[[na]]
}))
})
resuAcc <- lapply(names(par), function(na) {
do.call(rbind,lapply(liacc, function(x) {
x[[na]]
}))
})
seedend <- .save_rng()
names(resuParms) <- names(par)
names(resuAcc) <- names(par)
attr(resuParms, "AcceptationRate") <- resuAcc
attr(resuParms, "thinPar") <- thinPar
attr(resuParms, "thinAcc") <- thinAcc
attr(resuParms, "composition") <- composition
attr(resuParms, "nburnin") <- nburnin
prestime <- proc.time()
attr(resuParms, "duration") <- duration+(prestime-startTime)
attr(resuParms, "listUpdating") <- listUpdating
attr(resuParms, "lidat") <- lidat
attr(resuParms, "lipum") <- lipum
attr(resuParms, "logposterior") <- logposterior
attr(resuParms, "verbose") <- verbose
attr(resuParms, "saveEvery") <- saveEvery
attr(resuParms, "saveResults") <- saveResults
attr(resuParms, "fileSave") <- fileSave
attr(resuParms, "seedInit") <- seedinit
attr(resuParms, "seedEnd") <- seedend
if (!is.null(liopt))
attr(resuParms,"liopt") <- liopt
class(resuParms) <- "GSMetrop"
libackup <- resuParms
if (saveResults)
save(libackup, file=fileSave)
return(resuParms)
}
## Main loop
for (r in 1:another) {
if (verbose)
cat("Iteration", r, "\r")
im <- .iterMetrop(par, lidat, logposterior, lipum, NamesP, UpdatingMechanism,
liopt, composition, FALSE, NA)
par <- im$par
accept <- im$accept
## Storage
if (r%%thinPar==0) {
liparms[[length(liparms)+1]] <- par
}
## Acceptation
if (r%%thinAcc==0) {
liacc[[length(liacc)+1]] <- accept
}
## save?
if ((r%%saveEvery==0)&(!is.na(fileSave))) {
resuParms <- saveMetrop(r)
}
}
## Results
if (!is.na(fileSave)) {
resuParms <- saveMetrop(r)
}
## Restores the globalseed
.restore_rng(globseed)
return(resuParms)
}
#' @rdname GeneralSingleMetropolis
#' @export
defaultListUGSM <- function(parInit)
{
if (length(names(parInit))<1)
stop("parInit should be a named list, with one element per parameter")
listUpdating <- lapply(1:length(parInit), function(i) {
if (length(parInit[[i]])==1)
return("sis")
return("mis")
})
names(listUpdating) <- names(parInit)
return(listUpdating)
}
#' @rdname GeneralSingleMetropolis
#' @export
print.GSMetrop <- function(x, ...)
{
if (!inherits(x, "GSMetrop"))
stop("x should inherit the class \"GSMetrop\".")
cat("Object of class \"GSMetrop\"\n\n")
su <- summary(x)
cat(length(x), "parameters or vectors of parameters in the model:\n")
print(names(x))
cat("\n")
if ((attr(x,"nburnin"))<attr(x,"thinPar")) {
nite <- nrow(x[[1]])*attr(x,"thinPar")
} else {
nite <- nrow(x[[1]])*attr(x,"thinPar")+(attr(x,"nburnin"))%/%(attr(x,"thinPar"))
}
cat(nite, "iterations (including",
attr(x,"nburnin"), " burn-in samples)\n")
cat("Thin parameters every:", attr(x,"thinPar"), "iterations\n")
cat("Number of iterations stored:", nrow(x[[1]]),"\n")
cat("Calculation took", round(attr(x,"duration")[3]),"seconds (total time)\n\n")
if (nrow(su[[1]])<10) {
cat("Distribution of the acceptation rates:\n")
print(su[[1]][,2])
} else {
cat("Summary of the acceptation rates: \n")
print(summary(su[[1]]$AcceptationRate))
}
}
#' @title Summary information on a 'GSMetrop' object
#' @export
#'
#' @details Calculate acceptation rates, mean and SD from a
#' 'GSMetrop' object
#'
#' @param object an object of class \code{GSMetrop}
#' @param which the name of the parameters to summarize
#' @param accept logical. whether to calculate acceptation rates
#' @param parameters logical. whether to calculate mean and sd
#' @param \dots additional parameters to be passed
#'
#' @seealso \code{\link{GeneralSingleMetropolis}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
summary.GSMetrop <- function(object, which=names(object),
accept=TRUE, parameters=FALSE, ...)
{
if (!inherits(object, "GSMetrop"))
stop("object should inherit the class \"GSMetrop")
resu <- list()
if (accept) {
ac <- attr(object, "AcceptationRate")[which]
moya <- unlist(lapply(ac, function(y) apply(y,2,mean)))
naac <- unlist(lapply(1:length(ac), function(i) {
if (ncol(ac[[i]])>1)
return(paste0(names(ac)[i], "[", 1:ncol(ac[[i]]),"]"))
return(names(ac[i]))
}))
resu$AcceptationRate <- data.frame(Parameter=naac, AcceptationRate=moya)
row.names(resu$AcceptationRate) <- 1:nrow(resu$AcceptationRate)
}
if (parameters) {
object <- object[which]
napa <- unlist(lapply(1:length(object), function(i) {
if (ncol(object[[i]])>1)
return(paste0(names(object)[i], "[",
1:ncol(object[[i]]),"]"))
return(names(object[i]))
}))
moyp <- unlist(lapply(object, function(y) apply(y,2,mean)))
sdp <- unlist(lapply(object, function(y) apply(y,2,sd)))
resu$Parameters <- data.frame(Parameter=napa, Mean=moyp, SD=sdp)
row.names(resu$Parameters) <- 1:nrow(resu$Parameters)
}
return(resu)
}
#' @title graphical display of the result of a Metropolis run
#' @export
#' @details plot the mixing of a metropolis run
#'
#' @param x an object of class \code{GSMetrop}
#' @param which vector of name of parameters to be displayed
#' @param show the number of plot to show on the graphical device
#' @param \dots additional arguments to be passed to and from other
#' functions
#'
#' @seealso \code{\link{GeneralSingleMetropolis}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
#' @importFrom grDevices n2mfrow
plot.GSMetrop <- function(x, which=names(x), show=100, ...)
{
if (!inherits(x, "GSMetrop"))
stop("object should inherit the class \"GSMetrop")
x <- x[which]
npl <- sum(sapply(x,ncol))
ngr <- min(c(show,npl))
opar <- par(mfrow = n2mfrow(ngr),
ask=show<npl, mar=c(0,0,2,0))
on.exit(par(opar))
na <- lapply(1:length(x), function(i) {
if (ncol(x[[i]])>1)
return(paste0(names(x)[i], "[", 1:ncol(x[[i]]),"]"))
return(names(x[i]))
})
for (i in 1:length(na)) {
for (j in 1:length(na[[i]])) {
plot(x[[i]][,j], ty="l", main=na[[i]][j], axes=FALSE)
box()
}
}
}
#' @title transformations used in the package
#' @export
#'
#' @details \code{logit} calculates the logit of the argument, and
#' \code{invlogit} calculates the inverse logit transform.
#'
#' @param x parameter of a model.
#' @seealso \code{\link{GeneralSingleMetropolis}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
logit <- function(x) return(log(x/(1-x)))
#' @rdname logit
#' @export
invlogit <- function(x) return(exp(x)/(1+exp(x)))
#' @rdname GeneralSingleMetropolis
#' @export
reloadGSM <- function(fileSave)
{
.myDataEnv <- new.env(parent=emptyenv())
load(fileSave, envir=.myDataEnv)
if (!exists("libackup", envir=.myDataEnv))
stop("incorrect file")
libackup <- get("libackup", envir=.myDataEnv)
if (!inherits(libackup, "GSMetrop"))
stop("incorrect file")
return(libackup)
}
#' @title Various functions used to manipulate results of the Metropolis Algorithm
#' @export
#' @details \code{getParameterVector} extracts the parameter vector
#' corresponding to a given iteration. \code{increaseBurnin} can be
#' used to increase a posteriori the size of the burnin
#' sample. \code{nit} returns the number of stored iterations in a
#' GSMetrop object.
#'
#' @param resuParms an object of class \code{GSMetrop}
#' @param r iteration to be extracted
#' @param newBurnin new size of the burnin sample (in number of
#' *performed* iterations: in other words, if the objects results from
#' a calculation which carried out 5000 iterations thinned every 5
#' iterations, with a 10 iterations burnin period, then the original
#' object will contain 990 stored iterations. Setting
#' \code{newBurnin=500} will remove the 500 first performed
#' iterations, i.e. the first (500 - 10 old burnin)/(thin=5) = 98
#' iterations stored in the object).
#' @seealso \code{\link{GeneralSingleMetropolis}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
getParameterVector <- function(resuParms, r)
{
if (!inherits(resuParms, "GSMetrop"))
stop("incorrect class")
par <- lapply(resuParms, function(x) x[r,])
names(par) <- names(resuParms)
return(par)
}
#' @rdname getParameterVector
#' @export
nit <- function(resuParms)
{
if (!inherits(resuParms, "GSMetrop"))
stop("incorrect class")
return(nrow(resuParms[[1]]))
}
#' @rdname getParameterVector
#' @export
increaseBurnin <- function(resuParms, newBurnin=attr(resuParms,"nburnin"))
{
if (!inherits(resuParms, "GSMetrop"))
stop("incorrect class")
if (attr(resuParms,"nburnin")>newBurnin)
stop("newBurnin cannot be lower than the present value of nburnin")
if (((nrow(resuParms[[1]])*attr(resuParms, "thinPar"))+attr(resuParms,"nburnin"))<=(newBurnin))
stop("newBurnin too large")
nf <- 1+(newBurnin-attr(resuParms,"nburnin"))%/%attr(resuParms,"thinPar")
attr(resuParms,"nburnin") <- newBurnin
nr <- nrow(resuParms[[1]])
for (i in 1:length(resuParms))
resuParms[[i]] <- resuParms[[i]][nf:nr,, drop=FALSE]
return(resuParms)
}
#' @title Combine Several Single Metropolis Objects Into One CMM objects
#' @export
#' @aliases CMM
#' @details \code{c.GSMetrop} transforms several objects of class
#' \code{GSMetrop} into an object of class \code{CMM} (combined
#' multiple Metropolis).
#'
#' @param \dots a list of objects of class \code{GSMetrop}
#' @return an object of class "CMM". If only one object is passed,
#' the function returns the input object without transformation.
#' @seealso \code{\link{GeneralSingleMetropolis}}
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
#' @examples
#' #############################################
#' ##
#' ## First start with the same examples as in
#' ## the help page of GeneralSingleMetropolis
#' ##
#' ## Generates a dataset:
#' x <- rnorm(100)
#' y <- 1+ 2*x + rnorm(100, sd = 0.1)
#'
#' ## Linear regression: estimate a, b, sigma in
#' ## y = a + b*x + epsilon
#' ## epsilon ~ dnorm(0, sigma)
#'
#' ## Log-posterior
#' logp <- function(par, lidat, ctrl)
#' {
#' ## Current values of the parameters
#' a <- par$a
#' b <- par$b
#' sigma <- exp(par$lsigma)
#'
#' ## log-Prior:
#' lprior <- dnorm(a, mean=0, sd=100, log=TRUE)
#' lprior <- lprior + dnorm(b, mean=0, sd=100, log=TRUE)
#' ## uniform prior on sigma
#'
#' ## log-likelihood
#' llike <- sum(dnorm(lidat$y, mean=lidat$x*b+a, sd=sigma, log=TRUE))
#'
#' ## log-posterior
#' return(llike+lprior)
#' }
#'
#' ## Elements required for the fit
#' parinit <- list(a=0, b=1, lsigma=1) ## initial values
#' listUpdating <- list(a="sis", b="sis", lsigma="sis") ## all elements are updated with sis
#' lidat <- list(x=x, y=y)
#' lipum <- list(a=0.1, b=0.1, lsigma=1) ## test standard deviation of the proposals
#'
#' \dontrun{
#' #############################################
#' ##
#' ## Then demonstrate the class CMM
#' ##
#' ## Three runs
#' gsm1 <- GeneralSingleMetropolis(parinit, lidat, logp, lipum,
#' nrepet=5000, saveResults=FALSE, nburnin=500)
#' gsm2 <- GeneralSingleMetropolis(parinit, lidat, logp, lipum,
#' nrepet=5000, saveResults=FALSE, nburnin=500)
#' gsm3 <- GeneralSingleMetropolis(parinit, lidat, logp, lipum,
#' nrepet=5000, saveResults=FALSE, nburnin=500)
#'
#' ## Combine these three runs
#' ae <- c(gsm1, gsm2, gsm3)
#'
#' ## Show various information about these models
#' ae
#' summary(ae)
#' plot(ae)
#'
#' ## Extraction features:
#' ae[1:2]
#' ae[2]
#'
#' ## work with the package coda:
#' library(coda)
#' raftery.diag(tocoda(gsm1))
#'
#' gelman.diag(tocoda(ae))
#' }
#'
c.GSMetrop <- function(...)
{
li <- list(...)
## correct class?
if (any(sapply(li,class)!="GSMetrop"))
stop("objects should be of class GSMetrop")
if (length(li)==1)
return(li[[1]])
## Same number of attributes?
liat <- lapply(li, function(x) {
attr <- attributes(x)
attr$AcceptationRate <- NULL
attr$duration <- NULL
return(attr)
})
if (any(sapply(li, length)!=length(li[[1]])))
stop("all objects do not have the same number of attributes")
## Same names of attributes?
if (!all(apply(sapply(liat, names),1, function(x) all(x==x[1]))))
stop("not the same attribute names")
## same values for the attributes
liatb <- lapply(liat, function(x) {x$logposterior <- NULL;return(x)})
liatb <- lapply(liatb, function(x) {x$fileSave <- NULL;return(x)})
liatb <- lapply(liatb, function(x) {x$seedInit <- NULL;return(x)})
liatb <- lapply(liatb, function(x) {x$seedEnd <- NULL;return(x)})
tmp <- lapply(1:length(liatb[[1]]), function(i) {
la <- lapply(1:length(liatb), function(j) liatb[[j]][[i]])
if (!all(sapply(1:length(la), function(j) identical(la[[j]],la[[1]]))))
stop(paste0("Non consistent value for attribute ", names(liatb[[1]])[i]))
})
## same values for the posterior
tmp <- lapply(liat, function(x) x$logposterior)
if (!all(sapply(tmp, function(y) body(y)==body(tmp[[1]]))))
stop("different log-posterior")
## ok, combines
cmb <- lapply(li, function(x) {
acc <- attr(x,"AcceptationRate")
dur <- attr(x,"duration")
fs <- attr(x,"fileSave")
sin <- attr(x,"seedInit")
sen <- attr(x,"seedEnd")
nam <- names(x)
attributes(x) <- NULL
names(x) <- nam
attr(x,"duration") <- dur
attr(x,"AcceptationRate") <- acc
attr(x,"fileSave") <- fs
attr(x,"seedInit") <- sin
attr(x,"seedEnd") <- sen
return(x)
})
liat <- lapply(li, function(x) {
attr(x,"AcceptationRate") <- NULL
attr(x,"duration") <- NULL
attr(x,"fileSave") <- NULL
attr(x,"seedInit") <- NULL
attr(x,"seedEnd") <- NULL
names(x) <- NULL
return(attributes(x))
})
attributes(cmb) <- liat[[1]]
class(cmb) <- "CMM"
return(cmb)
}
#' @title Extract elements from a CMM object
#' @export
#' @details \code{Extract} function can be used to extract one or
#' several elements from an object of class \code{CMM} (combined
#' multiple Metropolis).
#'
#' @param x an object of class \code{CMM}
#' @param i a vector of positive integer values used as indices of the
#' elements to extract.
#' @return an object of class "CMM". If only one object is passed,
#' the function returns the input object without transformation.
#' @note negative indices (for element removal) are not allowed.
#' @seealso \code{\link{c.GSMetrop}} for examples
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
"[.CMM" <- function(x, i)
{
if (!inherits(x, "CMM"))
stop("x should be of class \"CMM\"")
if (max(i)>length(x)) {
stop("too large indices")
}
if (any(i<=0))
stop("negative or zero indices not allowed")
if (length(i)>1) {
atr <- attributes(x)
x <- unclass(x)
y <- x[i]
attributes(y) <- atr
return(y)
}
aa <- attributes(x)
li <- x[[i]]
ab <- attributes(li)
attributes(li) <- aa
for (j in 1:length(ab))
attr(li, names(ab)[j]) <- ab[[j]]
class(li) <- "GSMetrop"
return(li)
}
#' @title Prints a CMM object
#' @export
#' @details \code{print.CMM} function displays a short summary of an
#' object of class \code{CMM} (combined multiple Metropolis).
#'
#' @param x an object of class \code{CMM}
#' @param \dots additional arguments to be passed to other functions.
#' @seealso \code{\link{c.GSMetrop}} for examples
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
print.CMM <- function(x, ...)
{
if (!inherits(x, "CMM"))
stop("x should inherit the class \"CMM\".")
cat("Object of class \"CMM\"\n\n")
cat("Number of chains:", length(x),"\n")
cat(length(x), "parameters or vectors of parameters in the model:\n")
print(names(x[[1]]))
cat("\n")
cat(nrow(x[[1]][[1]])*attr(x,"thinPar")+attr(x,"nburnin"), "iterations per chain (including",
attr(x,"nburnin"), " burn-in samples)\n")
cat("Thin parameters every:", attr(x,"thinPar"), "iterations\n")
cat("Number of iterations stored for each chain:", nrow(x[[1]][[1]]),"\n")
cat("Calculation took", round(sum(sapply(1:length(x), function(i) attr(x[[i]],"duration")[3]))),
"seconds (total time over all chains)\n\n")
}
#' @title graphical display of the result of several Metropolis runs
#' @export
#' @details plot the mixing of several metropolis runs
#'
#' @param x an object of class \code{CMM}
#' @param which vector of name of parameters to be displayed
#' @param show the number of plot to show on the graphical device
#' @param col a character vector giving the colors of the different chains
#' @param \dots additional arguments to be passed to and from other
#' functions
#'
#' @seealso \code{\link{c.GSMetrop}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
#' @importFrom grDevices n2mfrow rainbow
plot.CMM <- function(x, which=names(x[1]), show=100, col=rainbow(length(x)), ...)
{
if (!inherits(x, "CMM"))
stop("object should inherit the class \"CMM")
xt <- x[1][which]
npl <- sum(sapply(xt,ncol))
ngr <- min(c(show,npl))
opar <- par(mfrow = n2mfrow(ngr),
ask=show<npl, mar=c(0,0,2,0))
on.exit(par(opar))
na <- lapply(1:length(xt), function(i) {
if (ncol(xt[[i]])>1)
return(paste0(names(xt)[i], "[", 1:ncol(xt[[i]]),"]"))
return(names(xt[i]))
})
for (i in 1:length(na)) {
for (j in 1:length(na[[i]])) {
gg <- range(unlist(lapply(x, function(y) y[[which[i]]][,j])))
plot(x[[1]][[i]][,j], ty="n", main=na[[i]][j], axes=FALSE, ylim = gg)
tmp <- lapply(1:length(x), function(k) lines(x[[k]][[which[i]]][,j], col=col[k]))
box()
}
}
}
#' @title Summary information on a 'CMM' object
#' @export
#'
#' @details Calculate mean and SD of parameters from a
#' 'CMM' object
#'
#' @param object an object of class \code{CMM}
#' @param which the name of the parameters to summarize
#' @param perchain logical. whether the statistics should be calculated for each chain separately or globally
#' @param \dots additional parameters to be passed
#'
#' @return a data.frame with the required statistics.
#'
#' @seealso \code{\link{c.GSMetrop}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
summary.CMM <- function(object, which=names(object[1]), perchain=TRUE, ...)
{
if (!inherits(object, "CMM"))
stop("object should inherit the class \"CMM\"")
if (perchain) {
ta <- lapply(1:length(object), function(i) {
su <- summary(object[i], which=which, accept = FALSE,
parameters = TRUE)$Parameters
names(su) <- paste0("Chain.",i,".", names(su))
return(su)
})
ta2 <- cbind.data.frame(Parameter=ta[[1]][,1], lapply(ta,function(x) x[,-1]))
return(ta2)
} else {
objectb <- lapply(1:length(object[[1]]), function(i)
do.call(rbind, lapply(object, function(x) x[[i]])))
names(objectb) <- names(object[[1]])
object <- objectb[which]
napa <- unlist(lapply(1:length(object), function(i) {
if (ncol(object[[i]])>1)
return(paste0(names(object)[i], "[",
1:ncol(object[[i]]),"]"))
return(names(object[i]))
}))
moyp <- unlist(lapply(object, function(y) apply(y,2,mean)))
sdp <- unlist(lapply(object, function(y) apply(y,2,sd)))
resu <- data.frame(Parameter=napa, Mean=moyp, SD=sdp)
row.names(resu) <- 1:nrow(resu)
return(resu)
}
}
#' @title Extracts the name of the parameters from a model
#' @export
#'
#' @details Extracts the name of the parameters from a \code{CMM} or
#' \code{GSMetrop} object
#'
#' @param x an object of class \code{CMM} or \code{GSMetrop}
#'
#' @return a character vector giving the names of the parameters.
#'
#' @seealso \code{\link{c.GSMetrop}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
param <- function(x)
{
if ((!inherits(x, "GSMetrop"))&(!inherits(x,"CMM")))
stop("x should inherits GSMetrop or CMM")
if (inherits(x, "GSMetrop"))
return(names(x))
return(names(x[[2]]))
}
#' @title Exports object toward the coda package
#' @export
#'
#' @details \code{tocoda} exports objects created with the package
#' metroponcfs toward classes suitable for further analysis with the
#' coda package.
#'
#' @param x an object of class \code{CMM} or \code{GSMetrop}
#' @param which a character vector indicating which parameters should be exported
#'
#' @return if \code{x} is an object of class GSMetrop, an object of
#' class \code{mcmc}. If \code{x} is an object of class CMM, an object
#' of class {mcmc.list}
#'
#' @seealso \code{\link{c.GSMetrop}} for examples of use.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
#' @importFrom coda mcmc mcmc.list
tocoda <- function(x, which=param(x))
{
if ((!inherits(x, "GSMetrop"))&(!inherits(x,"CMM")))
stop("x should inherits GSMetrop or CMM")
if (inherits(x,"GSMetrop")) {
xt <- x[which]
aa <- do.call(cbind,xt)
napa <- unlist(lapply(1:length(xt), function(i) {
if (ncol(xt[[i]])>1)
return(paste0(names(xt)[i], "[",
1:ncol(xt[[i]]),"]"))
return(names(xt)[i])
}))
colnames(aa) <- napa
## calcul du start
if ((attr(x,"nburnin")+1)<attr(x,"thinPar")) {
start <- attr(x,"thinPar")
} else {
start <- (((attr(x, "nburnin"))%/%attr(x,"thinPar"))+1)*attr(x,"thinPar")
}
return(coda::mcmc(aa, start=start,
thin=attr(x,"thinPar")))
} else {
return(do.call(coda::mcmc.list,lapply(1:length(x), function(i) {
tocoda(x[i], which=which)
})))
}
}
#' @title Deviance Information Criterion
#' @export
#'
#' @details Calculates the deviance information criterion of a model.
#'
#' @param x an object of class \code{CMM} or \code{GSMetrop}
#' @param loglikelihood a function for the calculation of the log
#' likelihood for the current model. This function must rely only on
#' the data provided in \code{lidat}. This function must have three
#' parameters: (i) \code{par} is the list of parameters, (ii)
#' \code{lidat} is the list containing the data, and (iii) \code{ctrl}
#' is a named list (see the help page of
#' \code{GeneralSingleMetropolis}). Note that the argument \code{ctrl}
#' is not used by the function here.
#'
#' @return the value of the DIC.
#'
#' @seealso \code{\link{GeneralSingleMetropolis}}.
#' @author Clement Calenge, \email{clement.calenge@@oncfs.gouv.fr}
DIC <- function(x, loglikelihood)
{
if ((!inherits(x, "GSMetrop"))&(!inherits(x,"CMM")))
stop("x should inherits GSMetrop or CMM")
if (inherits(x, "CMM")) {
dev <- unlist(lapply(1:length(x), function(i) {
mo <- x[i]
dev <- -2*sapply(1:nit(mo), function(i) {
loglikelihood(getParameterVector(mo,i),
attr(mo, "lidat"))
})
}))
} else {
dev <- -2*sapply(1:nit(x), function(i) {
loglikelihood(getParameterVector(x,i),
attr(x, "lidat"))
})
}
return(mean(dev)+var(dev)/2)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.