https://github.com/cran/spatstat
Raw File
Tip revision: 3aca716ce2576a0dab83f08052acd47afed8ee6a authored by Adrian Baddeley on 29 February 2012, 00:00:00 UTC
version 1.25-4
Tip revision: 3aca716
Kmeasure.R
#
#           Kmeasure.R
#
#           $Revision: 1.41 $    $Date: 2011/04/19 00:54:13 $
#
#     Kmeasure()         compute an estimate of the second order moment measure
#
#     Kest.fft()        use Kmeasure() to form an estimate of the K-function
#
#     second.moment.calc()    underlying algorithm
#

Kmeasure <- function(X, sigma, edge=TRUE, ..., varcov=NULL) {
  stopifnot(is.ppp(X))
  
  sigma.given <- !missing(sigma) && !is.null(sigma)
  varcov.given <- !is.null(varcov)
  ngiven <- sigma.given + varcov.given
  if(ngiven == 2)
    stop(paste("Give only one of the arguments",
               sQuote("sigma"), "and", sQuote("varcov")))
  if(ngiven == 0)
    stop(paste("Please specify smoothing bandwidth", sQuote("sigma"),
               "or", sQuote("varcov")))
  if(varcov.given) {
    stopifnot(is.matrix(varcov) && nrow(varcov) == 2 && ncol(varcov)==2 )
    sigma <- NULL
  } else {
    stopifnot(is.numeric(sigma))
    stopifnot(length(sigma) %in% c(1,2))
    stopifnot(all(sigma > 0))
    if(length(sigma) == 2) {
      varcov <- diag(sigma^2)
      sigma <- NULL
    }
  }  

  second.moment.calc(X, sigma=sigma, edge, "Kmeasure", varcov=varcov)
}

second.moment.calc <- function(x, sigma=NULL, edge=TRUE,
                               what="Kmeasure", debug=FALSE, ...,
                               varcov=NULL, expand=FALSE) {
  if(is.null(sigma) && is.null(varcov))
    stop("must specify sigma or varcov")
  choices <- c("kernel", "smooth", "Kmeasure", "Bartlett", "edge", "all", "smoothedge")
  if(!(what %in% choices))
    stop(paste("Unknown choice: what = ", sQuote(what),
               "; available options are:",
               paste(sQuote(choices), collapse=", ")))
  sig <- if(!is.null(sigma)) sigma else max(c(diag(varcov), sqrt(det(varcov))))
  win <- rescue.rectangle(as.owin(x))
  rec <- as.rectangle(win)
  across <- min(diff(rec$xrange), diff(rec$yrange))
  # determine whether to expand window
  if(!expand || (6 * sig < across))
    result <- second.moment.engine(x, sigma=sigma, edge=edge,
                                   what=what, debug=debug, ..., varcov=varcov)
  else {
    # need to expand window
    bigger <- grow.rectangle(rec, (7 * sig - across)/2)
    if(is.ppp(x)) {
      # pixellate first (to preserve pixel resolution)
      X <- pixellate(x, ..., padzero=TRUE)
      np <- npoints(x)
    } else if(is.im(x)) {
      X <- x
      np <- NULL
    } else stop("x should be an image or a point pattern")
    # now expand 
    X <- rebound.im(X, bigger)
    X <- na.handle.im(X, 0)
    out <- second.moment.engine(X, sigma=sigma, edge=edge,
                                what=what, debug=debug, ...,
                                obswin=win, varcov=varcov, npts=np)
    # now clip it
    fbox <- shift(rec, origin="midpoint")
    result <- switch(what,
                     kernel   = out[fbox],
                     smooth   = out[win],
                     Kmeasure = out[fbox],
                     Bartlett = out[fbox],
                     edge     = out[win],
                     smoothedge = list(smooth=out$smooth[win],
                                       edge  =out$edge[win]),
                     all      =
                     list(kernel=out$kernel[fbox],
                          smooth=out$smooth[win],
                          Kmeasure=out$Kmeasure[fbox],
                          Bartlett=out$Bartlett[fbox],
                          edge=out$edge[win]))
  }
  return(result)
}

second.moment.engine <- function(x, sigma=NULL, edge=TRUE,
                                 what="Kmeasure", debug=FALSE, ...,
                                 obswin = as.owin(x), varcov=NULL,
                                 npts=NULL)
{
  stopifnot(what %in% c("kernel", "smooth", "Kmeasure",
                        "Bartlett", "edge", "all", "smoothedge"))
  if(is.ppp(x)) 
    # convert list of points to mass distribution
    X <- pixellate(x, ..., padzero=TRUE)
  else if(is.im(x))
    X <- x
  else stop("internal error: unrecognised format for x")
  # ensure obswin has same bounding frame as X
  if(!missing(obswin))
    obswin <- rebound.owin(obswin, as.rectangle(X))
  #
  if(is.null(npts) && is.ppp(x))
      npts <- npoints(x)
  # go to work
  Y <- X$v
  xw <- X$xrange
  yw <- X$yrange
  # pad with zeroes
  nr <- nrow(Y)
  nc <- ncol(Y)
  Ypad <- matrix(0, ncol=2*nc, nrow=2*nr)
  Ypad[1:nr, 1:nc] <- Y
  lengthYpad <- 4 * nc * nr
  # corresponding coordinates
  xw.pad <- xw[1] + 2 * c(0, diff(xw))
  yw.pad <- yw[1] + 2 * c(0, diff(yw))
  xcol.pad <- X$xcol[1] + X$xstep * (0:(2*nc-1))
  yrow.pad <- X$yrow[1] + X$ystep * (0:(2*nr-1))
  # set up Gauss kernel
  xcol.G <- X$xstep * c(0:(nc-1),-(nc:1))
  yrow.G <- X$ystep * c(0:(nr-1),-(nr:1))
  xx <- matrix(xcol.G[col(Ypad)], ncol=2*nc, nrow=2*nr)
  yy <- matrix(yrow.G[row(Ypad)], ncol=2*nc, nrow=2*nr)
  if(!is.null(sigma)) {
#    if(max(abs(diff(xw)),abs(diff(yw))) < 6 * sigma)
#      warning("sigma is too large for this window")
    Kern <- exp(-(xx^2 + yy^2)/(2 * sigma^2))/(2 * pi * sigma^2) * X$xstep * X$ystep
  } else if(!is.null(varcov)) {
    # anisotropic kernel
    detSigma <- det(varcov)
    Sinv <- solve(varcov)
    const <- X$xstep * X$ystep/(2 * pi * sqrt(detSigma))
    Kern <- const * exp(-(xx * (xx * Sinv[1,1] + yy * Sinv[1,2])
                          + yy * (xx * Sinv[2,1] + yy * Sinv[2,2]))/2)
  } else 
    stop("Must specify either sigma or varcov")

  # these options call for several image outputs
  if(what %in% c("all", "smoothedge"))
    result <- list()
  
  if(what %in% c("kernel", "all")) {
    # return the kernel
    # first rearrange it into spatially sensible order (monotone x and y)
    rtwist <- ((-nr):(nr-1)) %% (2 * nr) + 1
    ctwist <- (-nc):(nc-1) %% (2*nc) + 1
    if(debug) {
      if(any(order(xcol.G) != rtwist))
        cat("something round the twist\n")
    }
    Kermit <- Kern[ rtwist, ctwist]
    ker <- im(Kermit, xcol.G[ctwist], yrow.G[ rtwist], unitname=unitname(x))
    if(what == "kernel")
      return(ker)
    else 
      result$kernel <- ker
  }
  # convolve using fft
  fK <- fft(Kern)
  if(what != "edge") {
    fY <- fft(Ypad)
    sm <- fft(fY * fK, inverse=TRUE)/lengthYpad
    if(debug) {
      cat(paste("smooth: maximum imaginary part=",
                signif(max(Im(sm)),3), "\n"))
      if(!is.null(npts))
        cat(paste("smooth: mass error=",
                  signif(sum(Mod(sm))-npts,3), "\n"))
    }
  }
  if(what %in% c("smooth", "all", "smoothedge")) {
    # return the smoothed point pattern without edge correction
    smo <- im(Re(sm)[1:nr, 1:nc], xcol.pad[1:nc], yrow.pad[1:nr],
              unitname=unitname(x))
    if(what == "smooth")
      return(smo)
    else
      result$smooth <- smo
  }

  if(what != "edge")
    bart <- Mod(fY)^2 * fK
  
  if(what %in% c("Bartlett", "all")) {
     # return Bartlett spectrum
     # rearrange into spatially sensible order (monotone x and y)
    rtwist <- ((-nr):(nr-1)) %% (2 * nr) + 1
    ctwist <- (-nc):(nc-1) %% (2*nc) + 1
    Bart <- bart[ rtwist, ctwist]
    Bartlett <- im(Mod(Bart),(-nc):(nc-1), (-nr):(nr-1))
    if(what == "Bartlett")
      return(Bartlett)
    else
      result$Bartlett <- Bartlett
  }
  
  #### ------- Second moment measure --------------
  #
  if(what != "edge") {
    mom <- fft(bart, inverse=TRUE)/lengthYpad
    if(debug) {
      cat(paste("2nd moment measure: maximum imaginary part=",
                signif(max(Im(mom)),3), "\n"))
      if(!is.null(npts))
        cat(paste("2nd moment measure: mass error=",
                  signif(sum(Mod(mom))-npts^2, 3), "\n"))
    }
    mom <- Mod(mom)
    # subtract (delta_0 * kernel) * npts
    if(is.null(npts))
      stop("Internal error: second moment measure requires npts")
    mom <- mom - npts* Kern
  }
  # edge correction
  if(edge || what %in% c("edge", "all", "smoothedge")) {
    # compute kernel-smoothed set covariance
    M <- as.mask(obswin, dimyx=c(nr, nc))$m
    # previous line ensures M has same dimensions and scale as Y 
    Mpad <- matrix(0, ncol=2*nc, nrow=2*nr)
    Mpad[1:nr, 1:nc] <- M
    lengthMpad <- 4 * nc * nr
    fM <- fft(Mpad)
    if(edge && what != "edge") {
      co <- fft(Mod(fM)^2 * fK, inverse=TRUE)/lengthMpad
      co <- Mod(co) 
      a <- sum(M)
      wt <- a/co
      me <- spatstat.options("maxedgewt")
      weight <- matrix(pmin(me, wt), ncol=2*nc, nrow=2*nr)
#      if(debug) browser()
      #
      # apply edge correction
      mom <- mom * weight
      # set to NA outside 'reasonable' region
      mom[wt > 10] <- NA
    }
  }
  if(what != "edge") {
    # rearrange into spatially sensible order (monotone x and y)
    rtwist <- ((-nr):(nr-1)) %% (2 * nr) + 1
    ctwist <- (-nc):(nc-1) %% (2*nc) + 1
    mom <- mom[ rtwist, ctwist]
    if(debug) {
      if(any(order(xcol.G) != rtwist))
        cat("something round the twist\n")
    }
  }
  if(what %in% c("edge", "all", "smoothedge")) {
    # return convolution of window with kernel
    # (evaluated inside window only)
    con <- fft(fM * fK, inverse=TRUE)/lengthMpad
    edg <- Mod(con[1:nr, 1:nc])
    edg <- im(edg, xcol.pad[1:nc], yrow.pad[1:nr], unitname=unitname(x))
    if(what == "edge") 
      return(edg)
    else
      result$edge <- edg
  }
  if(what == "smoothedge")
    return(result)
  # Second moment measure, density estimate
  # Divide by number of points * lambda and convert mass to density
  pixarea <- with(X, xstep * ystep)
  mom <- mom * area.owin(obswin) / (pixarea * npts^2)
  # this is the second moment measure
  mm <- im(mom, xcol.G[ctwist], yrow.G[rtwist], unitname=unitname(x))
  if(what == "Kmeasure")
    return(mm)
  else 
    result$Kmeasure <- mm
  # what = "all", so return all computed objects
  return(result)
}

Kest.fft <- function(X, sigma, r=NULL, breaks=NULL) {
  verifyclass(X, "ppp")
  W <- X$window
  lambda <- X$n/area.owin(W)
  rmaxdefault <- rmax.rule("K", W, lambda)        
  bk <- handle.r.b.args(r, breaks, W, rmaxdefault=rmaxdefault)
  breaks <- bk$val
  rvalues <- bk$r
  u <- Kmeasure(X, sigma)
  xx <- rasterx.im(u)
  yy <- rastery.im(u)
  rr <- sqrt(xx^2 + yy^2)
  tr <- whist(rr, breaks, u$v)
  K  <- cumsum(tr)
  rmax <- min(rr[is.na(u$v)])
  K[rvalues >= rmax] <- NA
  result <- data.frame(r=rvalues, theo=pi * rvalues^2, border=K)
  w <- X$window
  alim <- c(0, min(diff(w$xrange), diff(w$yrange))/4)
  out <- fv(result,
            "r", substitute(K[fft](r), NULL),
            "border",
             . ~ r, alim,
            c("r", "{%s^{pois}}(r)", "{hat(%s)^{bord}}(r)"),
            c("distance argument r",
              "theoretical Poisson %s",
              "border-corrected estimate of %s"),
            fname="K[fft]",
            unitname=unitname(X)
            )
  return(out)
}

back to top