https://github.com/cran/spatstat
Tip revision: 3aca716ce2576a0dab83f08052acd47afed8ee6a authored by Adrian Baddeley on 29 February 2012, 00:00:00 UTC
version 1.25-4
version 1.25-4
Tip revision: 3aca716
envelope.R
#
# envelope.R
#
# computes simulation envelopes
#
# $Revision: 2.23 $ $Date: 2012/01/09 02:42:11 $
#
envelope <- function(Y, fun, ...) {
UseMethod("envelope")
}
# .................................................................
# A 'simulation recipe' contains the following variables
#
# type = Type of simulation
# "csr": uniform Poisson process
# "rmh": simulated realisation of fitted Gibbs or Poisson model
# "kppm": simulated realisation of fitted cluster model
# "expr": result of evaluating a user-supplied expression
# "list": user-supplied list of point patterns
#
# expr = expression that is repeatedly evaluated to generate simulations
#
# envir = environment in which to evaluate the expression `expr'
#
# 'csr' = TRUE iff the model is (known to be) uniform Poisson
#
# pois = TRUE if model is known to be Poisson
#
# ...................................................................
simulrecipe <- function(type, expr, envir, csr, pois=csr) {
if(csr && !pois) warning("Internal error: csr=TRUE but pois=FALSE")
out <- list(type=type,
expr=expr,
envir=envir,
csr=csr,
pois=pois)
class(out) <- "simulrecipe"
out
}
envelope.ppp <-
function(Y, fun=Kest, nsim=99, nrank=1, ...,
simulate=NULL, verbose=TRUE, clipdata=TRUE,
transform=NULL, global=FALSE, ginterval=NULL,
savefuns=FALSE, savepatterns=FALSE, nsim2=nsim,
VARIANCE=FALSE, nSD=2,
Yname=NULL, maxnerr=nsim) {
cl <- match.call()
if(is.null(Yname)) Yname <- deparse(substitute(Y))
envir.user <- parent.frame()
envir.here <- sys.frame(sys.nframe())
if(is.null(simulate)) {
# ...................................................
# Realisations of complete spatial randomness
# will be generated by rpoispp
# Data pattern X is argument Y
# Data pattern determines intensity of Poisson process
X <- Y
sY <- summary(Y, checkdup=FALSE)
Yintens <- sY$intensity
Ywin <- Y$window
# expression that will be evaluated
simexpr <-
if(!is.marked(Y)) {
# unmarked point pattern
expression(rpoispp(Yintens, win=Ywin))
} else {
# marked point pattern
Ymarx <- marks(Y, dfok=FALSE)
expression({A <- rpoispp(Yintens, win=Ywin);
A %mark% sample(Ymarx, A$n, replace=TRUE)})
}
# evaluate in THIS environment
simrecipe <- simulrecipe(type = "csr",
expr = simexpr,
envir = envir.here,
csr = TRUE,
pois = TRUE)
} else {
# ...................................................
# Simulations are determined by 'simulate' argument
# Processing is deferred to envelopeEngine
simrecipe <- simulate
# Data pattern is argument Y
X <- Y
}
envelopeEngine(X=X, fun=fun, simul=simrecipe,
nsim=nsim, nrank=nrank, ...,
verbose=verbose, clipdata=clipdata,
transform=transform, global=global, ginterval=ginterval,
savefuns=savefuns, savepatterns=savepatterns, nsim2=nsim2,
VARIANCE=VARIANCE, nSD=nSD,
Yname=Yname, maxnerr=maxnerr, cl=cl,
envir.user=envir.user)
}
envelope.ppm <-
function(Y, fun=Kest, nsim=99, nrank=1, ...,
simulate=NULL, verbose=TRUE, clipdata=TRUE,
start=NULL, control=default.rmhcontrol(Y, nrep=nrep), nrep=1e5,
transform=NULL, global=FALSE, ginterval=NULL,
savefuns=FALSE, savepatterns=FALSE, nsim2=nsim,
VARIANCE=FALSE, nSD=2,
Yname=NULL, maxnerr=nsim) {
cl <- match.call()
if(is.null(Yname)) Yname <- deparse(substitute(Y))
envir.user <- parent.frame()
envir.here <- sys.frame(sys.nframe())
# Extract data pattern X from fitted model Y
X <- data.ppm(Y)
if(is.null(simulate)) {
# ...................................................
# Simulated realisations of the fitted model Y
# will be generated
pois <- is.poisson(Y)
csr <- is.stationary(Y) && pois
type <- if(csr) "csr" else "rmh"
# Set up parameters for rmh
rmodel <- rmhmodel(Y, verbose=FALSE)
if(is.null(start))
start <- list(n.start=X$n)
rstart <- rmhstart(start)
rcontr <- rmhcontrol(control)
# pre-digest arguments
rmhinfolist <- rmh(rmodel, rstart, rcontr, preponly=TRUE, verbose=FALSE)
# expression that will be evaluated
simexpr <- expression(rmhEngine(rmhinfolist, verbose=FALSE))
envir <- envir.here
# evaluate in THIS environment
simrecipe <- simulrecipe(type = type,
expr = simexpr,
envir = envir.here,
csr = csr,
pois = pois)
} else {
# ...................................................
# Simulations are determined by 'simulate' argument
# Processing is deferred to envelopeEngine
simrecipe <- simulate
}
envelopeEngine(X=X, fun=fun, simul=simrecipe,
nsim=nsim, nrank=nrank, ...,
verbose=verbose, clipdata=clipdata,
transform=transform, global=global, ginterval=ginterval,
savefuns=savefuns, savepatterns=savepatterns, nsim2=nsim2,
VARIANCE=VARIANCE, nSD=nSD,
Yname=Yname, maxnerr=maxnerr, cl=cl,
envir.user=envir.user)
}
envelope.kppm <-
function(Y, fun=Kest, nsim=99, nrank=1, ...,
simulate=NULL, verbose=TRUE, clipdata=TRUE,
transform=NULL, global=FALSE, ginterval=NULL,
savefuns=FALSE, savepatterns=FALSE, nsim2=nsim,
VARIANCE=FALSE, nSD=2, Yname=NULL, maxnerr=nsim)
{
cl <- match.call()
if(is.null(Yname)) Yname <- deparse(substitute(Y))
envir.user <- parent.frame()
envir.here <- sys.frame(sys.nframe())
# Extract data pattern X from fitted model Y
X <- Y$X
if(is.null(simulate)) {
# Simulated realisations of the fitted model Y
# will be generated using simulate.kppm
kmodel <- Y
# expression that will be evaluated
simexpr <- expression(simulate(kmodel)[[1]])
# evaluate in THIS environment
simrecipe <- simulrecipe(type = "kppm",
expr = simexpr,
envir = envir.here,
csr = FALSE,
pois = FALSE)
} else {
# ...................................................
# Simulations are determined by 'simulate' argument
# Processing is deferred to envelopeEngine
simrecipe <- simulate
}
envelopeEngine(X=X, fun=fun, simul=simrecipe,
nsim=nsim, nrank=nrank, ...,
verbose=verbose, clipdata=clipdata,
transform=transform, global=global, ginterval=ginterval,
savefuns=savefuns, savepatterns=savepatterns, nsim2=nsim2,
VARIANCE=VARIANCE, nSD=nSD,
Yname=Yname, maxnerr=maxnerr, cl=cl,
envir.user=envir.user)
}
## .................................................................
## Engine for simulating and computing envelopes
## .................................................................
#
# X is the data point pattern, which could be ppp, pp3, ppx etc
# X determines the class of pattern expected from the simulations
#
envelopeEngine <-
function(X, fun, simul,
nsim=99, nrank=1, ...,
verbose=TRUE, clipdata=TRUE,
transform=NULL, global=FALSE, ginterval=NULL,
savefuns=FALSE, savepatterns=FALSE, nsim2=nsim,
VARIANCE=FALSE, nSD=2,
Yname=NULL, maxnerr=nsim, internal=NULL, cl=NULL,
envir.user=envir.user) {
#
envir.here <- sys.frame(sys.nframe())
# ----------------------------------------------------------
# Determine Simulation
# ----------------------------------------------------------
# Identify class of patterns to be simulated, from data pattern X
Xclass <- if(is.ppp(X)) "ppp" else
if(is.pp3(X)) "pp3" else
if(is.ppx(X)) "ppx" else
stop("Unrecognised class of point pattern")
Xobjectname <- paste("point pattern of class", sQuote(Xclass))
# Identify type of simulation from argument 'simul'
if(inherits(simul, "simulrecipe")) {
# ..................................................
# simulation recipe is given
simtype <- simul$type
simexpr <- simul$expr
envir <- simul$envir
csr <- simul$csr
pois <- simul$pois
} else {
# ...................................................
# simulation is specified by argument `simulate' to envelope()
simulate <- simul
# which should be an expression, or a list of point patterns,
# or an envelope object.
csr <- FALSE
# override
if(!is.null(icsr <- internal$csr)) csr <- icsr
pois <- csr
model <- NULL
if(inherits(simulate, "envelope")) {
# envelope object: see if it contains stored point patterns
simpat <- attr(simulate, "simpatterns")
if(!is.null(simpat))
simulate <- simpat
else
stop(paste("The argument", sQuote("simulate"),
"is an envelope object but does not contain",
"any saved point patterns."))
}
if(is.expression(simulate)) {
# The user-supplied expression 'simulate' will be evaluated repeatedly
simtype <- "expr"
simexpr <- simulate
envir <- envir.user
} else if(is.list(simulate) &&
( (is.ppp(X) && all(unlist(lapply(simulate, is.ppp))))
|| (is.pp3(X) && all(unlist(lapply(simulate, is.pp3))))
|| (is.ppx(X) && all(unlist(lapply(simulate, is.ppx)))))) {
# The user-supplied list of point patterns will be used
simtype <- "list"
SimDataList <- simulate
# expression that will be evaluated
simexpr <- expression(SimDataList[[i]])
envir <- envir.here
# ensure that `i' is defined
i <- 1
# any messages?
if(!is.null(mess <- attr(simulate, "internal"))) {
# determine whether these point patterns are realisations of CSR
csr <- !is.null(mc <- mess$csr) && mc
}
} else stop(paste(sQuote("simulate"),
"should be an expression, or a list of point patterns"))
}
# -------------------------------------------------------------------
# Determine clipping window
# ------------------------------------------------------------------
if(clipdata) {
# Generate one realisation
Xsim <- eval(simexpr, envir=envir)
if(!inherits(Xsim, Xclass))
switch(simtype,
csr=stop(paste("Internal error:", Xobjectname, "not generated")),
rmh=stop(paste("Internal error: rmh did not return an",
Xobjectname)),
kppm=stop(paste("Internal error: simulate.kppm did not return an",
Xobjectname)),
expr=stop(paste("Evaluating the expression", sQuote("simulate"),
"did not yield an", Xobjectname)),
list=stop(paste("Internal error: list entry was not an",
Xobjectname)),
stop(paste("Internal error:", Xobjectname, "not generated"))
)
# Extract window
clipwin <- Xsim$window
if(!is.subset.owin(clipwin, X$window))
warning("Window containing simulated patterns is not a subset of data window")
}
# ------------------------------------------------------------------
# Summary function to be applied
# ------------------------------------------------------------------
if(is.null(fun))
stop("Internal error: fun is NULL")
# Name of function, for error messages
fname <- if(is.name(substitute(fun))) deparse(substitute(fun))
else if(is.character(fun)) fun else "fun"
fname <- sQuote(fname)
# R function to apply
if(is.character(fun)) {
gotfun <- try(get(fun, mode="function"))
if(inherits(gotfun, "try-error"))
stop(paste("Could not find a function named", sQuote(fun)))
fun <- gotfun
} else if(!is.function(fun))
stop(paste("unrecognised format for function", fname))
fargs <- names(formals(fun))
if(!any(c("r", "...") %in% fargs))
stop(paste(fname, "should have an argument",
sQuote("r"), "or", sQuote("...")))
usecorrection <- any(c("correction", "...") %in% fargs)
# ---------------------------------------------------------------------
# validate other arguments
if((nrank %% 1) != 0)
stop("nrank must be an integer")
stopifnot(nrank > 0 && nrank < nsim/2)
rgiven <- "r" %in% names(list(...))
if(tran <- !is.null(transform)) {
stopifnot(is.expression(transform))
# 'transform' is an expression
aa <- substitute(substitute(tt, list(.=as.name("funX"))),
list(tt=transform))
# 'aa' is a language expression invoking 'substitute'
bb <- eval(parse(text=deparse(aa)))
# 'bb' is an expression obtained by replacing "." by "funX"
transform.funX <- as.call(bb)
transform.funX[[1]] <- as.name("eval.fv")
# 'transform.funX' is an unevaluated call to eval.fv
aa <- substitute(substitute(tt, list(.=as.name("funXsim"))),
list(tt=transform))
bb <- eval(parse(text=deparse(aa)))
transform.funXsim <- as.call(bb)
transform.funXsim[[1]] <- as.name("eval.fv")
}
if(!is.null(ginterval))
stopifnot(is.numeric(ginterval) && length(ginterval) == 2)
# ---------------------------------------------------------------------
# Evaluate function for data pattern X
# ------------------------------------------------------------------
Xarg <- if(!clipdata) X else X[clipwin]
funX <- do.call(fun,
resolve.defaults(list(Xarg),
list(...),
if(usecorrection) list(correction="best") else NULL))
if(!inherits(funX, "fv"))
stop(paste("The function", fname,
"must return an object of class", sQuote("fv")))
argname <- fvnames(funX, ".x")
valname <- fvnames(funX, ".y")
has.theo <- "theo" %in% fvnames(funX, "*")
csr.theo <- csr && has.theo
if(tran) {
# extract only the recommended value
if(csr.theo)
funX <- funX[, c(argname, valname, "theo")]
else
funX <- funX[, c(argname, valname)]
# apply the transformation to it
funX <- eval(transform.funX)
}
rvals <- funX[[argname]]
fX <- funX[[valname]]
# default domain over which to maximise
alim <- attr(funX, "alim")
if(global && is.null(ginterval))
ginterval <- if(rgiven) range(rvals) else alim
#--------------------------------------------------------------------
# Determine number of simulations
# ------------------------------------------------------------------
#
## determine whether dual simulations are required
## (one set of simulations to calculate the theoretical mean,
## another independent set of simulations to obtain the critical point.)
dual <- (global && !csr.theo && !VARIANCE)
Nsim <- if(!dual) nsim else (nsim + nsim2)
# if taking data from a list of point patterns,
# check there are enough of them
if(simtype == "list" && Nsim > length(SimDataList))
stop(paste("Number of simulations",
paren(if(!dual)
paste(nsim) else
paste(nsim, "+", nsim2, "=", Nsim)
),
"exceeds number of point pattern datasets supplied"))
# Undocumented secret exit
# ------------------------------------------------------------------
if(!is.null(eject <- internal$eject) && eject == "patterns") {
# generate simulated realisations and return only these patterns
if(verbose) {
action <- if(simtype == "list") "Extracting" else "Generating"
descrip <- switch(simtype,
csr = "simulations of CSR",
rmh = paste("simulated realisations of fitted",
if(pois) "Poisson" else "Gibbs",
"model"),
kppm = "simulated realisations of fitted cluster model",
expr = "simulations by evaluating expression",
list = "point patterns from list",
"simulated realisations")
explan <- if(dual) paren(paste(nsim2, "to estimate the mean and",
nsim, "to calculate envelopes")) else ""
cat(paste(action, Nsim, descrip, explan, "...\n"))
}
XsimList <- list()
# start simulation loop
for(i in 1:Nsim) {
if(verbose) progressreport(i, Nsim)
Xsim <- eval(simexpr, envir=envir)
if(!inherits(Xsim, Xclass))
switch(simtype,
csr={
stop(paste("Internal error:", Xobjectname, "not generated"))
},
rmh={
stop(paste("Internal error: rmh did not return an",
Xobjectname))
},
kppm={
stop(paste("Internal error: simulate.kppm did not return an",
Xobjectname))
},
expr={
stop(paste("Evaluating the expression", sQuote("simulate"),
"did not yield an", Xobjectname))
},
list={
stop(paste("Internal error: list entry was not an",
Xobjectname))
},
stop(paste("Internal error:", Xobjectname, "not generated"))
)
XsimList[[i]] <- Xsim
}
if(verbose) {
cat(paste("Done.\n"))
flush.console()
}
attr(XsimList, "internal") <- list(csr=csr)
return(XsimList)
}
# capture main decision parameters
EnvelopeInfo <- list(call=cl,
Yname=Yname,
valname=valname,
csr=csr,
csr.theo=csr.theo,
pois=pois,
simtype=simtype,
nrank=nrank,
nsim=nsim,
Nsim=Nsim,
global=global,
dual=dual,
nsim2=nsim2,
VARIANCE=VARIANCE,
nSD=nSD)
# ----------------------------------------
######### SIMULATE #######################
# ----------------------------------------
if(verbose) {
action <- if(simtype == "list") "Extracting" else "Generating"
descrip <- switch(simtype,
csr = "simulations of CSR",
rmh = paste("simulated realisations of fitted",
if(pois) "Poisson" else "Gibbs",
"model"),
kppm = "simulated realisations of fitted cluster model",
expr = "simulations by evaluating expression",
list = "point patterns from list",
"simulated patterns")
explan <- if(dual) paren(paste(nsim2, "to estimate the mean and",
nsim, "to calculate envelopes")) else ""
cat(paste(action, Nsim, descrip, explan, "...\n"))
}
# determine whether simulated point patterns should be saved
catchpatterns <- savepatterns && simtype != "list"
Caughtpatterns <- list()
# allocate space for computed function values
nrvals <- length(rvals)
simvals <- matrix(, nrow=nrvals, ncol=Nsim)
# arguments for function
funargs <-
resolve.defaults(if(rgiven) NULL else list(r=rvals),
list(...),
if(usecorrection) list(correction="best") else NULL)
# start simulation loop
nerr <- 0
for(i in 1:Nsim) {
ok <- FALSE
# safely generate a random pattern and apply function
while(!ok) {
Xsim <- eval(simexpr, envir=envir)
# check valid point pattern
if(!inherits(Xsim, Xclass))
switch(simtype,
csr=stop(paste("Internal error:", Xobjectname, "not generated")),
rmh=stop(paste("Internal error: rmh did not return an",
Xobjectname)),
kppm=stop(paste("Internal error:",
"simulate.kppm did not return an",
Xobjectname)),
expr=stop(paste("Evaluating the expression", sQuote("simulate"),
"did not yield an", Xobjectname)),
list=stop(paste("Internal error: list entry was not an",
Xobjectname)),
stop(paste("Internal error:", Xobjectname, "not generated"))
)
if(catchpatterns)
Caughtpatterns[[i]] <- Xsim
# apply function safely
funXsim <- try(do.call(fun, append(list(Xsim), funargs)))
ok <- !inherits(funXsim, "try-error")
if(!ok) {
nerr <- nerr + 1
if(nerr > maxnerr)
stop("Exceeded maximum number of errors")
cat("[retrying]\n")
}
}
# sanity checks
if(!inherits(funXsim, "fv"))
stop(paste("When applied to a simulated pattern, the function",
fname, "did not return an object of class",
sQuote("fv")))
argname.sim <- fvnames(funXsim, ".x")
valname.sim <- fvnames(funXsim, ".y")
if(argname.sim != argname)
stop(paste("The objects returned by", fname,
"when applied to a simulated pattern",
"and to the data pattern",
"are incompatible. They have different argument names",
sQuote(argname.sim), "and", sQuote(argname),
"respectively"))
if(valname.sim != valname)
stop(paste("When", fname, "is applied to a simulated pattern",
"it provides an estimate named", sQuote(valname.sim),
"whereas the estimate for the data pattern is named",
sQuote(valname),
". Try using the argument", sQuote("correction"),
"to make them compatible"))
if(tran) {
# extract only the recommended value
if(csr.theo)
funXsim <- funXsim[, c(argname, valname, "theo")]
else
funXsim <- funXsim[, c(argname, valname)]
# apply the transformation to it
funXsim <- eval(transform.funXsim)
}
# extract the values for simulation i
simvals.i <- funXsim[[valname]]
if(length(simvals.i) != nrvals)
stop("Vectors of function values have incompatible lengths")
simvals[ , i] <- funXsim[[valname]]
if(verbose)
progressreport(i, Nsim)
}
## end simulation loop
if(verbose) {
cat("\nDone.\n")
flush.console()
}
# ...........................................................
# save functions and/or patterns if so commanded
if(savefuns) {
alldata <- cbind(rvals, simvals)
simnames <- paste("sim", 1:nsim, sep="")
colnames(alldata) <- c("r", simnames)
alldata <- as.data.frame(alldata)
SimFuns <- fv(alldata,
argu="r",
ylab=attr(funX, "ylab"),
valu="sim1",
fmla= deparse(. ~ r),
alim=attr(funX, "alim"),
labl=names(alldata),
desc=c("distance argument r",
paste("Simulation ", 1:nsim, sep="")))
fvnames(SimFuns, ".") <- simnames
}
if(savepatterns)
SimPats <- if(simtype == "list") SimDataList else Caughtpatterns
######### COMPUTE ENVELOPES #######################
etype <- if(global) "global" else if(VARIANCE) "variance" else "pointwise"
if(dual) {
jsim <- 1:nsim
jsim.mean <- nsim + 1:nsim2
} else {
jsim <- jsim.mean <- NULL
}
result <- envelope.matrix(simvals, funX=funX,
jsim=jsim, jsim.mean=jsim.mean,
type=etype, csr=csr, use.theory=csr.theo,
nrank=nrank, ginterval=ginterval, nSD=nSD,
Yname=Yname)
# tack on envelope information
attr(result, "einfo") <- EnvelopeInfo
# tack on functions and/or patterns if so commanded
if(savefuns)
attr(result, "simfuns") <- SimFuns
if(savepatterns) {
attr(result, "simpatterns") <- SimPats
attr(result, "datapattern") <- X
}
return(result)
}
plot.envelope <- function(x, ..., shade=c("hi", "lo")) {
xname <- deparse(substitute(x))
argh <- list(...)
if(missing(shade) &&
length(argh) > 0 &&
any(isfo <- unlist(lapply(argh, inherits, what="formula")))) {
# the plot is specified by a formula;
# check whether columns 'hi' and 'lo' are used
fmla <- argh[[min(which(isfo))]]
vars <- variablesinformula(fmla)
hilo <- c("hi", "lo")
dotnames <- fvnames(x, ".")
present <- (all(hilo %in% vars) ||
(all(hilo %in% dotnames) && ("." %in% vars)))
if(!present)
shade <- NULL
}
do.call("plot.fv", resolve.defaults(list(x), list(...),
list(main=xname, shade=shade)))
}
print.envelope <- function(x, ...) {
e <- attr(x, "einfo")
g <- e$global
csr <- e$csr
pois <- e$pois
if(is.null(pois)) pois <- csr
simtype <- e$simtype
nr <- e$nrank
nsim <- e$nsim
V <- e$VARIANCE
fname <- deparse(attr(x, "ylab"))
type <- if(V) paste("Pointwise", e$nSD, "sigma") else
if(g) "Simultaneous" else "Pointwise"
cat(paste(type, "critical envelopes for", fname, "\n"))
if(!is.null(valname <- e$valname))
cat(paste("Edge correction:",
dQuote(valname), "\n"))
# determine *actual* type of simulation
descrip <-
if(csr) "simulations of CSR"
else if(!is.null(simtype)) {
switch(simtype,
csr="simulations of CSR",
rmh=paste("simulations of fitted",
if(pois) "Poisson" else "Gibbs",
"model"),
kppm="simulations of fitted cluster model",
expr="evaluations of user-supplied expression",
list="point pattern datasets in user-supplied list",
funs="columns of user-supplied data")
} else "simulations of fitted model"
#
cat(paste("Obtained from", nsim, descrip, "\n"))
#
if(!is.null(e$dual) && e$dual)
cat(paste("Theoretical (i.e. null) mean value of", fname,
"estimated from a separate set of",
e$nsim2, "simulations\n"))
if(!is.null(attr(x, "simfuns")))
cat("(All simulated function values are stored)\n")
if(!is.null(attr(x, "simpatterns")))
cat("(All simulated point patterns are stored)\n")
alpha <- if(g) { nr/(nsim+1) } else { 2 * nr/(nsim+1) }
if(!V) {
# significance interpretation!
cat(paste("Significance level of",
if(!g) "pointwise",
"Monte Carlo test:",
paste(if(g) nr else 2 * nr,
"/", nsim+1, sep=""),
"=", alpha, "\n"))
}
cat(paste("Data:", e$Yname, "\n"))
NextMethod("print")
}
summary.envelope <- function(object, ...) {
e <- attr(object, "einfo")
g <- e$global
nr <- e$nrank
nsim <- e$nsim
csr <- e$csr
pois <- e$pois
if(is.null(pois)) pois <- csr
has.theo <- "theo" %in% fvnames(object, "*")
csr.theo <- csr && has.theo
simtype <- e$simtype
fname <- deparse(attr(object, "ylab"))
V <- e$VARIANCE
type <- if(V) paste("Pointwise", e$nSD, "sigma") else
if(g) "Simultaneous" else "Pointwise"
cat(paste(type, "critical envelopes for", fname, "\n"))
# determine *actual* type of simulation
descrip <-
if(csr) "simulations of CSR"
else if(!is.null(simtype)) {
switch(simtype,
csr="simulations of CSR",
rmh=paste("simulations of fitted",
if(pois) "Poisson" else "Gibbs",
"model"),
kppm="simulations of fitted cluster model",
expr="evaluations of user-supplied expression",
list="point pattern datasets in user-supplied list",
funs="columns of user-supplied data",
"simulated point patterns")
} else "simulations of fitted model"
#
cat(paste("Obtained from", nsim, descrip, "\n"))
#
if(!is.null(attr(object, "simfuns")))
cat(paste("(All", nsim, "simulated function values",
"are stored in attr(,", dQuote("simfuns"), ") )\n"))
if(!is.null(attr(object, "simpatterns")))
cat(paste("(All", nsim, "simulated point patterns",
"are stored in attr(,", dQuote("simpatterns"), ") )\n"))
#
if(V) {
# nSD envelopes
cat(paste("Envelopes computed as sample mean plus/minus",
e$nSD, "sample standard deviations\n"))
} else {
# critical envelopes
lo.ord <- if(nr == 1) "minimum" else paste(ordinal(nr), "smallest")
hi.ord <- if(nr == 1) "maximum" else paste(ordinal(nsim-nr+1), "largest")
if(g)
cat(paste("Envelopes computed as",
if(csr.theo) "theoretical curve" else "mean of simulations",
"plus/minus", hi.ord,
"simulated value of maximum absolute deviation\n"))
else {
cat(paste("Upper envelope: pointwise", hi.ord,"of simulated curves\n"))
cat(paste("Lower envelope: pointwise", lo.ord,"of simulated curves\n"))
}
alpha <- if(g) { nr/(nsim+1) } else { 2 * nr/(nsim+1) }
cat(paste("Significance level of Monte Carlo test:",
paste(if(g) nr else 2 * nr,
"/", nsim+1, sep=""),
"=", alpha, "\n"))
}
cat(paste("Data:", e$Yname, "\n"))
return(invisible(NULL))
}
# envelope.matrix
# core functionality to compute envelope values
# theory = funX[["theo"]]
# observed = fX
envelope.matrix <- function(Y, ...,
rvals=NULL, observed=NULL, theory=NULL,
funX=NULL,
nsim=NULL, nsim2=NULL,
jsim=NULL, jsim.mean=NULL,
type=c("pointwise", "global", "variance"),
csr=FALSE, use.theory = csr,
nrank=1, ginterval=NULL, nSD=2,
check=TRUE,
Yname=NULL) {
if(is.null(Yname))
Yname <- deparse(substitute(Y), 60, nlines=1)
type <- match.arg(type)
if(!is.null(funX)) stopifnot(is.fv(funX))
if(is.null(rvals) && is.null(observed) && !is.null(funX)) {
# assume funX is summary function for observed data
rvals <- with(funX, .x)
observed <- with(funX, .y)
theory <- if(use.theory && "theo" %in% names(funX)) with(funX, theo) else NULL
} else if(check) {
# validate vectors of data
if(is.null(rvals)) stop("rvals must be supplied")
if(is.null(observed)) stop("observed must be supplied")
stopifnot(length(rvals) == nrow(Y))
stopifnot(length(observed) == length(rvals))
}
if(use.theory) {
use.theory <- !is.null(theory)
if(use.theory && check) stopifnot(length(theory) == length(rvals))
}
atr <- if(!is.null(funX)) attributes(funX) else
list(alim=range(rvals),
ylab=quote(f(r)),
yexp=quote(f(r)),
fname="f")
simvals <- Y
fX <- observed
# determine numbers of columns used
Ncol <- ncol(simvals)
if(Ncol < 2)
stop("Need at least 2 columns of function values")
if(is.null(jsim) && !is.null(nsim)) {
# usual case - 'nsim' determines 'jsim'
if(nsim > Ncol)
stop(paste(nsim, "simulations are not available; only",
Ncol, "columns provided"))
jsim <- 1:nsim
if(!is.null(nsim2)) {
# 'nsim2' determines 'jsim.mean'
if(nsim + nsim2 > Ncol)
stop(paste(nsim, "+", nsim2, "=", nsim+nsim2,
"simulations are not available; only",
Ncol, "columns provided"))
jsim.mean <- nsim + 1:nsim2
}
}
restrict.columns <- !is.null(jsim)
dual <- !is.null(jsim.mean)
switch(type,
pointwise = {
# ....... POINTWISE ENVELOPES ...............................
simvals[is.infinite(simvals)] <- NA
if(restrict.columns)
simvals <- simvals[,jsim]
nsim <- ncol(simvals)
nsim.mean <- NULL
if(nrank == 1) {
lohi <- apply(simvals, 1, range)
} else {
lohi <- apply(simvals, 1,
function(x, n) { sort(x)[n] },
n=c(nrank, nsim-nrank+1))
}
lo <- lohi[1,]
hi <- lohi[2,]
lo.name <- paste("lower pointwise envelope of %s from simulations")
hi.name <- paste("upper pointwise envelope of %s from simulations")
#
if(use.theory) {
results <- data.frame(r=rvals,
obs=fX,
theo=theory,
lo=lo,
hi=hi)
} else {
m <- apply(simvals, 1, mean, na.rm=TRUE)
results <- data.frame(r=rvals,
obs=fX,
mmean=m,
lo=lo,
hi=hi)
}
},
global = {
# ..... SIMULTANEOUS ENVELOPES ..........................
if(!is.null(ginterval)) {
domain <- (rvals >= ginterval[1]) & (rvals <= ginterval[2])
funX <- funX[domain, ]
simvals <- simvals[domain, ]
} else domain <- rep(TRUE, length(rvals))
simvals[is.infinite(simvals)] <- NA
if(use.theory) {
reference <- theory[domain]
if(restrict.columns)
simvals <- simvals[, jsim]
nsim.mean <- NULL
} else if(dual) {
# Estimate the mean from one set of columns
# Form envelopes from another set of columns
simvals.mean <- simvals[, jsim.mean]
reference <- mmean <- apply(simvals.mean, 1, mean, na.rm=TRUE)
nsim.mean <- ncol(simvals.mean)
simvals <- simvals[, jsim]
} else {
# Estimate the mean and form the envelopes using the same data
if(restrict.columns)
simvals <- simvals[, jsim]
reference <- mmean <- apply(simvals, 1, mean, na.rm=TRUE)
nsim.mean <- NULL
}
nsim <- ncol(simvals)
# compute max absolute deviations
deviations <- sweep(simvals, 1, reference)
suprema <- apply(abs(deviations), 2, max, na.rm=TRUE)
# ranked deviations
dmax <- sort(suprema)[nsim-nrank+1]
# simultaneous bands
lo <- reference - dmax
hi <- reference + dmax
lo.name <- "lower critical boundary for %s"
hi.name <- "upper critical boundary for %s"
if(use.theory)
results <- data.frame(r=rvals[domain],
obs=fX[domain],
theo=reference,
lo=lo,
hi=hi)
else
results <- data.frame(r=rvals[domain],
obs=fX[domain],
mmean=reference,
lo=lo,
hi=hi)
},
variance={
# ....... POINTWISE MEAN, VARIANCE etc ......................
simvals[is.infinite(simvals)] <- NA
if(restrict.columns)
simvals <- simvals[, jsim]
nsim <- ncol(simvals)
nsim.mean <- NULL
Ef <- apply(simvals, 1, mean, na.rm=TRUE)
varf <- apply(simvals, 1, var, na.rm=TRUE)
# derived quantities
sd <- sqrt(varf)
stdres <- (fX-Ef)/sd
stdres[!is.finite(stdres)] <- NA
# critical limits
lo <- Ef - nSD * sd
hi <- Ef + nSD * sd
lo.name <- paste("lower", nSD, "sigma critical limit for %s")
hi.name <- paste("upper", nSD, "sigma critical limit for %s")
# confidence interval
loCI <- Ef - nSD * sd/sqrt(nsim)
hiCI <- Ef + nSD * sd/sqrt(nsim)
loCI.name <- paste("lower", nSD, "sigma confidence bound",
"for mean of simulated %s")
hiCI.name <- paste("upper", nSD, "sigma confidence bound",
"for mean of simulated %s")
# put together
if(use.theory) {
results <- data.frame(r=rvals,
obs=fX,
theo=theory,
lo=lo,
hi=hi)
morestuff <- data.frame(mmean=Ef,
var=varf,
res=fX-Ef,
stdres=stdres,
loCI=loCI,
hiCI=hiCI)
mslabl <- c("bar(%s)(r)",
"paste(var,%s)(r)",
"paste(res,%s)(r)",
"paste(stdres,%s)(r)",
"%s[loCI](r)", "%s[hiCI](r)")
msdesc <- c("sample mean of %s from simulations",
"sample variance of %s from simulations",
"raw residual",
"standardised residual",
loCI.name, hiCI.name)
} else {
results <- data.frame(r=rvals,
obs=fX,
mmean=Ef,
lo=lo,
hi=hi)
morestuff <- data.frame(var=varf,
res=fX-Ef,
stdres=stdres,
loCI=loCI,
hiCI=hiCI)
mslabl <- c("paste(var,%s)(r)",
"paste(res,%s)(r)",
"paste(stdres,%s)(r)",
"%s[loCI](r)", "%s[hiCI](r)")
msdesc <- c("sample variance of %s from simulations",
"raw residual",
"standardised residual",
loCI.name, hiCI.name)
}
}
)
############ WRAP UP #########################
if(use.theory) {
# reference is computed curve `theo'
reflabl <- "%s[theo](r)"
refdesc <- "theoretical value of %s"
if(csr) refdesc <- paste(refdesc, "for CSR")
} else {
# reference is sample mean of simulations
reflabl <- "bar(%s)(r)"
refdesc <- "sample mean of %s from simulations"
}
result <- fv(results,
argu="r",
ylab=atr$ylab,
valu="obs",
fmla= deparse(. ~ r),
alim=atr$alim,
labl=c("r", "%s[obs](r)",
reflabl,
"%s[lo](r)", "%s[hi](r)"),
desc=c("distance argument r",
"observed value of %s for data pattern",
refdesc, lo.name, hi.name),
fname=atr$fname,
yexp =atr$yexp)
# columns to be plotted by default
dotty <- c("obs", if(use.theory) "theo" else "mmean", "hi", "lo")
if(type == "variance") {
# add more stuff
result <- bind.fv(result, morestuff, mslabl, msdesc)
if(use.theory) dotty <- c(dotty, "mmean")
}
fvnames(result, ".") <- dotty
unitname(result) <- unitname(funX)
class(result) <- c("envelope", class(result))
# tack on envelope information
attr(result, "einfo") <- list(global = (type =="global"),
csr = csr,
simtype = "funs",
nrank = nrank,
nsim = nsim,
VARIANCE = (type == "variance"),
nSD = nSD,
valname = NULL,
dual = dual,
nsim = nsim,
nsim2 = nsim.mean,
Yname = Yname)
return(result)
}
envelope.envelope <- function(Y, fun=NULL, ...) {
Yname <- deparse(substitute(Y), 60, nlines=1)
stopifnot(inherits(Y, "envelope"))
csr <- attr(Y, "einfo")$csr
if(!is.null(fun)) {
# apply new function
# point patterns are required
sp <- attr(Y, "simpatterns")
X <- attr(Y, "datapattern")
if(is.null(sp))
stop(paste("Object Y does not contain simulated point patterns",
"(attribute", dQuote("simpatterns"), ");",
"cannot apply a new", sQuote("fun")))
if(is.null(X))
stop(paste("Cannot apply a new", sQuote("fun"),
"; object Y generated by an older version of spatstat"))
result <- do.call(envelope,
resolve.defaults(list(Y=X, fun=fun, simulate=sp),
list(...),
list(Yname=Yname,
nsim=length(sp)),
.StripNull=TRUE))
copyacross <- c("Yname", "csr", "csr.theo", "simtype")
attr(result, "einfo")[copyacross] <- attr(Y, "einfo")[copyacross]
return(result)
}
# compute new envelope with existing simulated functions
sf <- attr(Y, "simfuns")
if(is.null(sf))
stop(paste("Y does not contain a", dQuote("simfuns"), "attribute",
"(it was not generated with savefuns=TRUE)"))
# extract simulated function values
df <- as.data.frame(sf)
rname <- fvnames(sf, ".x")
df <- df[, (names(df) != rname)]
result <- do.call(envelope.matrix,
resolve.defaults(list(Y=as.matrix(df)),
list(...),
list(funX=Y,
Yname=Yname),
.StripNull=TRUE))
copyacross <- c("Yname", "csr", "csr.theo", "simtype")
attr(result, "einfo")[copyacross] <- attr(Y, "einfo")[copyacross]
return(result)
}
pool <- function(...) {
UseMethod("pool")
}
pool.envelope <- function(...) {
Yname <- paste(deparse(sys.call()), collapse="")
if(nchar(Yname) > 60) Yname <- paste(substr(Yname, 1, 40), "[..]")
Elist <- list(...)
nE <- length(Elist)
if(nE == 0) return(NULL)
# validate....
# All arguments must be envelopes
notenv <- !unlist(lapply(Elist, inherits, what="envelope"))
if(any(notenv)) {
n <- sum(notenv)
why <- paste(ngettext(n, "Argument", "Arguments"),
commasep(which(notenv)),
ngettext(n, "does not", "do not"),
"belong to the class",
dQuote("envelope"))
stop(why)
}
if(nE == 1) return(Elist[[1]])
# All arguments must have 'simfuns'
SFlist <- lapply(Elist, attr, which="simfuns")
isnul <- unlist(lapply(SFlist, is.null))
if(any(isnul)) {
n <- sum(isnul)
why <- paste(ngettext(n, "Argument", "Arguments"),
commasep(which(isnul)),
ngettext(n, "does not", "do not"),
"contain a", dQuote("simfuns"), "attribute",
"(not generated with savefuns=TRUE)")
stop(why)
}
# vectors of r values must be identical
rlist <- lapply(SFlist, function(z) { with(z, .x) })
rvals <- rlist[[1]]
samer <- unlist(lapply(rlist, identical, y=rvals))
if(!all(samer))
stop(paste("Simulated function values are not compatible",
"(different values of function argument)"))
# functions must be the same
fnames <- unique(unlist(lapply(SFlist, attr, which="fname")))
if(length(fnames) > 1)
stop(paste("Envelopes were computed for different functions:",
commasep(sQuote(fnames))))
# ... reconcile parameters .......
einfolist <- lapply(Elist, attr, which="einfo")
global <- unique(unlist(lapply(einfolist, function(z) { z$global } )))
VARIANCE <- unique(unlist(lapply(einfolist, function(z) { z$VARIANCE } )))
simtype <- unique(unlist(lapply(einfolist, function(z) { z$simtype } )))
csr <- unique(unlist(lapply(einfolist, function(z) { z$csr } )))
csr.theo <- unique(unlist(lapply(einfolist, function(z) { z$csr.theo } )))
resolve <- function(x, x0, warn) {
xname <- deparse(substitute(x))
if(length(x) == 1)
return(x)
if(missing(warn))
warn <- paste("Envelopes were generated using different values",
"of argument", paste(sQuote(xname), ";", sep=""),
"reverting to default value")
if(!is.null(warn))
warning(warn, call.=FALSE)
return(x0)
}
global <- resolve(global, FALSE)
VARIANCE <- resolve(VARIANCE, FALSE)
simtype <- resolve(simtype, "funs",
"Envelopes were generated using different types of simulation")
csr <- resolve(csr, FALSE, NULL)
csr.theo <- resolve(csr.theo, FALSE, NULL)
type <- if(global) "global" else if(VARIANCE) "variance" else "pointwise"
# ..... ready to compute
getsimvals <- function(z) {
rname <- fvnames(z, ".x")
as.matrix(as.data.frame(z)[, names(z) != rname])
}
matlist <- lapply(SFlist, getsimvals)
bigmat <- do.call(cbind, matlist)
result <- envelope(bigmat, funX=Elist[[1]], type=type, csr=csr, Yname=Yname)
return(result)
}