https://github.com/cran/spatstat
Raw File
Tip revision: ace26c246ee6feb8779515fa668bec59b24a1fcc authored by Adrian Baddeley on 12 March 2007, 13:35:27 UTC
version 1.11-2
Tip revision: ace26c2
mincontrast.R
#
#  mincontrast.R
#
#  Functions for estimation by minimum contrast
#

##################  base ################################

mincontrast <- function(observed, theoretical, startpar,
                        ...,
                        ctrl=list(q = 1/4, p = 2, rmin=NULL, rmax=NULL),
                        fvlab=list(label=NULL, desc="minimum contrast fit"),
                        explain=list(dataname=NULL, modelname=NULL, fname=NULL)) {
  verifyclass(observed, "fv")
  stopifnot(is.function(theoretical))
  if(!any("par" %in% names(formals(theoretical))))
    stop(paste("Theoretical function does not include an argument called",
               sQuote("par")))

  # enforce defaults
  ctrl <- resolve.defaults(ctrl, list(q = 1/4, p = 2, rmin=NULL, rmax=NULL))
  fvlab <- resolve.defaults(fvlab,
                            list(label=NULL, desc="minimum contrast fit"))
  explain <- resolve.defaults(explain,
                              list(dataname=NULL, modelname=NULL, fname=NULL))
  
  # determine range of r values
  rmin <- ctrl$rmin
  rmax <- ctrl$rmax
  if(!is.null(rmin) && !is.null(rmax))
    stopifnot(rmin < rmax && rmin >= 0)
  else {
    alim <- attr(observed, "alim")
    if(is.null(rmin)) rmin <- alim[1]
    if(is.null(rmax)) rmax <- alim[2]
  }
  # extract vector of r values
  argu <- attr(observed, "argu")
  rvals <- observed[[argu]]
  # extract vector of observed values of statistic
  valu <- attr(observed, "valu")
  obs <- observed[[valu]]
  # restrict to [rmin, rmax]
  if(max(rvals) < rmax)
    stop(paste("rmax=", signif(rmax,4),
               "exceeds the range of available data",
               "= [", signif(min(rvals),4), ",", signif(max(rvals),4), "]"))
  sub <- (rvals >= rmin) & (rvals <= rmax)
  rvals <- rvals[sub]
  obs <- obs[sub]
  # for efficiency
  obsq <- obs^(ctrl$q)
  # define objective function
  objective <- function(par, obsq, theoretical, rvals, qq, pp, rmin, rmax, ...) {
    theo <- theoretical(par=par, rvals, ...)
    if(!is.vector(theo) || !is.numeric(theo))
      stop("theoretical function did not return a numeric vector")
    if(length(theo) != length(obs))
      stop("theoretical function did not return the correct number of values")
    discrep <- (abs(theo^qq - obsq))^pp
    return(sum(discrep))
  }
  # go
  minimum <- optim(startpar, fn=objective,
                   obsq=obsq, theoretical=theoretical,
                   rvals=rvals,
                   qq=ctrl$q, pp=ctrl$p, rmin=rmin, rmax=rmax, ...)
  # evaluate the fitted theoretical curve
  fittheo <- theoretical(minimum$par, rvals, ...)
  # pack it up as an `fv' object
  label <- fvlab$label
  desc  <- fvlab$desc
  if(is.null(label))
    label <- paste("fit(", argu, ")", collapse="")
  fitfv <- bind.fv(observed[sub, ],
                   data.frame(fit=fittheo),
                   label, desc)
  result <- list(par=minimum$par,
                 fit=fitfv,
                 opt=minimum,
                 ctrl=list(p=ctrl$p,q=ctrl$q,rmin=rmin,rmax=rmax),
                 info=explain
                 )
  
  class(result) <- c("minconfit", class(result))
  return(result)
}

print.minconfit <- function(x, ...) {
  # explanatory
  cat(paste("Minimum contrast fit ",
            "(",
            "object of class ",
            dQuote("minconfit"),
            ")",
            "\n", sep=""))
  mo <- x$info$modelname
  fu <- x$info$fname
  da <- x$info$dataname
  if(!is.null(mo))
    cat(paste("Model:", mo, "\n"))
  if(!is.null(fu) && !is.null(da))
    cat(paste("Fitted by matching theoretical", fu, "function to", da))
  else {
    if(!is.null(fu))
      cat(paste(" based on", fu))
    if(!is.null(da))
      cat(paste(" fitted to", da))
  }
  cat("\n")
  # Values
  cat("Parameters fitted by minimum contrast ($par):\n")
  print(x$par, ...)
  mp <- x$modelpar
  if(!is.null(mp)) {
    cat(paste("Derived parameters of",
              if(!is.null(mo)) mo else "model",
              "($modelpar):\n"))
    print(mp)
  }
  # Diagnostics
  cgce <- x$opt$convergence
  switch(paste(cgce),
         "0"={
           cat(paste("Converged successfully after",
                     x$opt$counts[["function"]],
                     "iterations.\n"))
         },
         "1"={
           cat("Warning: iteration limit maxit was reached.\n")
         },
         "10"={
           cat("Warning: Nelder-Mead simplex was degenerate\n")
         },
         "51"={
           cat(paste("Warning from L-BGFS-B method:\n",
                     x$opt$message, "\n"))
         },
         "52"={
           cat(paste("Error message from L-BGFS-B method:\n",
                     x$opt$message, "\n"))
         },
         cat(paste("Unrecognised error code", cgce, "\n"))
         )
  # Algorithm parameters
  ct <- x$ctrl
  cat(paste("Domain of integration:",
            "[",
            signif(ct$rmin,4),
            ",",
            signif(ct$rmax,4),
            "]\n"))
  cat(paste("Exponents:",
            "p=", paste(signif(ct$p, 3), ",",  sep=""),
            "q=", signif(ct$q,3), "\n"))
  invisible(NULL)
}
              

plot.minconfit <- function(x, ...) {
  xname <- deparse(substitute(x))
  do.call("plot.fv",
          resolve.defaults(list(x$fit),
                           list(...),
                           list(main=xname)))
}


############### applications (specific models) ##################


thomas.estK <- function(X, startpar=c(kappa=1,sigma2=1),
                        lambda=NULL, q=1/4, p=2, rmin=NULL, rmax=NULL){

  dataname <- deparse(substitute(X))

  if(inherits(X, "fv")) {
    K <- X
  } else if(inherits(X, "ppp")) {
    K <- Kest(X)
    dataname <- paste("Kest(", dataname, ")", sep="")
    lambda <- summary(X)$intensity
  } else 
    stop("Unrecognised format for argument X")

  startpar <- check.named.vector(startpar, c("kappa","sigma2"))

  theoret <- function(par,rvals){
    if(any(par <= 0))
      return(rep(Inf, length(rvals)))
    pi*rvals^2+(1-exp(-rvals^2/(4*par[2])))/par[1]
  }
  result <- mincontrast(K, theoret, startpar,
                        ctrl=list(q=q, p=p,rmin=rmin, rmax=rmax),
                        fvlab=list(label="Kfit(r)",
                          desc="minimum contrast fit of Thomas process"),
                        explain=list(dataname=dataname,
                          fname="K", modelname="Thomas process"))
  # imbue with meaning
  par <- result$par
  names(par) <- c("kappa", "sigma2")
  result$par <- par
  # infer model parameters
  mu <- if(is.numeric(lambda) && length(lambda) == 1)
           lambda/result$par[["kappa"]] else NA
  result$modelpar <- c(kappa=par[["kappa"]],
                       sigma=sqrt(par[["sigma2"]]),
                       mu=mu)
  result$internal <- list(model="Thomas")
  return(result)
}


lgcp.estK <- function(X, startpar=list(sigma2=1,alpha=1),
                      lambda=NULL, q=1/4, p=2, rmin=NULL, rmax=NULL) {
  
  dataname <- deparse(substitute(K))
  if(inherits(X, "fv")) {
    K <- X
  } else if(inherits(X, "ppp")) {
    K <- Kest(X)
    dataname <- paste("Kest(", dataname, ")", sep="")
    lambda <- summary(X)$intensity
  } else 
    stop("Unrecognised format for argument X")

  startpar <- check.named.vector(startpar, c("sigma2","alpha"))

  integrand<-function(r,par){
    2*pi*r*exp(par[1]*exp(-r/par[2]))
  }
  theoret <- function(par, rvals) {
    if(any(par <= 0))
      return(rep(Inf, length(rvals)))
    th <- numeric(length(rvals))
    th[1] <- if(rvals[1] == 0) 0 else 
             integrate(integrand,lower=0,upper=rvals[1],par=par)$value
    for (i in 2:length(rvals))
      th[i]=th[i-1]+integrate(integrand,lower=rvals[i-1],upper=rvals[i],par=par)$value
    return(th)
  }
  result <- mincontrast(K, theoret, startpar,
                        ctrl=list(q=q, p=p, rmin=rmin, rmax=rmax),
                        fvlab=list(label="Kfit(r)",
                          desc="minimum contrast fit of LGCP"),
                        explain=list(dataname=dataname,
                          fname="K",
                          modelname="log-Gaussian Cox process"))
  # imbue with meaning
  par <- result$par
  names(par) <- c("sigma2", "alpha")
  result$par <- par
  # infer model parameters
  mu <- if(is.numeric(lambda) && length(lambda) == 1 && lambda > 0)
           log(lambda) - par[["sigma2"]]/2 else NA
  result$modelpar <- c(sigma2=par[["sigma2"]],
                       alpha=par[["alpha"]],
                       mu=mu)
  result$internal <- list(model="lcgp")
  return(result)
}


matclust.estK <- function(X, startpar=c(kappa=1,R=1),
                        lambda=NULL, q=1/4, p=2, rmin=NULL, rmax=NULL){

  dataname <- deparse(substitute(X))

  if(inherits(X, "fv")) {
    K <- X
  } else if(inherits(X, "ppp")) {
    K <- Kest(X)
    dataname <- paste("Kest(", dataname, ")", sep="")
    lambda <- summary(X)$intensity
  } else 
    stop("Unrecognised format for argument X")

  startpar <- check.named.vector(startpar, c("kappa","R"))

  hfun <- function(zz) {
    ok <- (zz < 1)
    h <- numeric(length(zz))
    h[!ok] <- 1
    z <- zz[ok]
    h[ok] <- 2 + (1/pi) * (
                           (8 * z^2 - 4) * acos(z)
                           - 2 * asin(z)
                           + 4 * z * sqrt((1 - z^2)^3)
                           - 6 * z * sqrt(1 - z^2)
                           )
    return(h)
  }
  theoret <- function(par,rvals){
    if(any(par <= 0))
      return(rep(Inf, length(rvals)))
    pi * rvals^2 + (1/par[1]) * hfun(rvals/(2 * par[2]))
  }
  result <- mincontrast(K, theoret, startpar,
                        ctrl=list(q=q, p=p,rmin=rmin, rmax=rmax),
                        fvlab=list(label="Kfit(r)",
                          desc="minimum contrast fit of Matern Cluster process"),
                        explain=list(dataname=dataname,
                          fname="K", modelname="Matern Cluster process"))
  # imbue with meaning
  par <- result$par
  names(par) <- c("kappa", "R")
  result$par <- par
  # infer model parameters
  mu <- if(is.numeric(lambda) && length(lambda) == 1)
           lambda/result$par[["kappa"]] else NA
  result$modelpar <- c(kappa=par[["kappa"]],
                       R=par[["R"]],
                       mu=mu)
  result$internal <- list(model="MatClust")
  return(result)
}


check.named.vector <- function(x, nam) {
  xname <- deparse(substitute(x))
  if(!is.numeric(x) || length(x) != 2 || !all(nam %in% names(x))) {
    whinge <- paste(xname,
                    "must be a named vector, with components",
                    commasep(nam))
    stop(whinge)
  }
  return(x[nam])
}

back to top