Raw File
pcf.R
#
#   pcf.R
#
#   $Revision: 1.26 $   $Date: 2007/10/24 09:41:15 $
#
#
#   calculate pair correlation function
#   from point pattern (pcf.ppp)
#   or from estimate of K or Kcross (pcf.fv)
#   or from fasp object
#
#
pcf <- function(X, ...) {
  UseMethod("pcf")
}

pcf.ppp <- function(X, ..., r=NULL,
                    kernel="epanechnikov", bw=NULL, stoyan=0.15,
                    correction=c("translate", "ripley"))
{
  verifyclass(X, "ppp")
  r.override <- !is.null(r)

  win <- X$window
  area <- area.owin(win)
  lambda <- X$n/area

  stopifnot(is.character(correction))
  if(!all(ok <- correction %in% c("translate", "ripley")))
    stop(paste("unrecognised correction",
               if(sum(!ok) > 1) "s", ": ",
               paste(correction[!ok], collapse=", "),
               sep=""))
  
  if(is.null(bw) && kernel=="epanechnikov") {
    # Stoyan & Stoyan 1995, eq (15.16), page 285
    h <- stoyan /sqrt(lambda)
    # conversion to standard deviation
    bw <- h/sqrt(5)
  }

  ########## r values ############################
  # handle arguments r and breaks 

  rmaxdefault <- rmax.rule("K", win, lambda)        
  breaks <- handle.r.b.args(r, NULL, win, rmaxdefault=rmaxdefault)
  if(!(breaks$even))
    stop("r values must be evenly spaced")
  # extract r values
  r <- breaks$r
  rmax <- breaks$max
  # recommended range of r values for plotting
  alim <- c(0, min(rmax, rmaxdefault))

  # arguments for 'density'
  from <- 0
  to <- max(r)
  nr <- length(r)
  #################################################
  
  
  # compute pairwise distances
  
  close <- closepairs(X, max(r))
  dIJ <- close$d
  XI <- ppp(close$xi, close$yi, window=win, check=FALSE)

  # how to process the distances
  
  doit <- function(w, d, out, symb, desc, key, otherargs, lambda, area) {
    wtot <- sum(w)
    kden <- do.call("density.default",
                    append(list(x=d, weights=w/wtot), otherargs))
                                     
    r <- kden$x
    y <- kden$y * wtot
    g <- y/(2 * pi * r * (lambda^2) * area)
    if(is.null(out)) {
      df <- data.frame(r=r, theo=rep(1,length(r)), g)
      colnames(df)[3] <- key
      out <- fv(df, "r", substitute(g(r), NULL), key, , alim, c("r","gPois(r)", symb),
                c("distance argument r", "theoretical Poisson g(r)", desc))
    } else {
      df <- data.frame(g)
      colnames(df) <- key
      out <- bind.fv(out, df, symb, desc, key)
    }
    return(out)
  }

  otherargs <- resolve.defaults(list(kernel=kernel, bw=bw),
                                list(...),
                                list(n=nr, from=from, to=to))

  ###### compute #######

  out <- NULL

  if(any(correction=="translate")) {
    # translation correction
    XJ <- ppp(close$xj, close$yj, window=win, check=FALSE)
    edgewt <- edge.Trans(XI, XJ, paired=TRUE)
    out <- doit(edgewt, dIJ, out, "gTrans(r)",
                "translation-corrected estimate of g", "trans", otherargs,
                lambda, area)
  }
  if(any(correction=="ripley")) {
    # Ripley isotropic correction
    edgewt <- edge.Ripley(XI, matrix(dIJ, ncol=1))
    out <- doit(edgewt, dIJ, out, "gRipley(r)",
                "Ripley-corrected estimate of g", "ripl", otherargs,
                lambda, area)
  }
  
  # sanity check
  if(is.null(out)) {
    warning("Nothing computed - no edge corrections chosen")
    return(NULL)
  }
  
  # which corrections have been computed?
  nama2 <- names(out)
  corrxns <- rev(nama2[nama2 != "r"])

  # default is to display them all
  attr(out, "fmla") <- deparse(as.formula(paste(
                       "cbind(",
                        paste(corrxns, collapse=","),
                        ") ~ r")))
  unitname(out) <- unitname(X)
  return(out)
}


"pcf.fasp" <- function(X, ..., method="c") {
  verifyclass(X, "fasp")
  Y <- X
  Y$title <- paste("Array of pair correlation functions",
                   if(!is.null(X$dataname)) "for",
                   X$dataname)
  # go to work on each function
  for(i in seq(X$fns)) {
    Xi <- X$fns[[i]]
    PCFi <- pcf.fv(Xi, ..., method=method)
    Y$fns[[i]] <- as.fv(PCFi)
    if(is.fv(PCFi))
      Y$default.formula[[i]] <- attr(PCFi, "fmla")
  }
  return(Y)
}


"pcf.fv" <-
function(X, ..., method="c") {
  verifyclass(X, "fv")
  
  callmatched <- function(fun, argue) {
    formalnames <- names(formals(fun))
    formalnames <- formalnames[formalnames != "..."]
    do.call("fun", argue[names(argue) %in% formalnames])
  }

  # extract r and the recommended estimate of K
  r <- X[[attr(X, "argu")]]
  K <- X[[attr(X, "valu")]]
  alim <- attr(X, "alim")

  # remove NA's
  ok <- !is.na(K)
  K <- K[ok]
  r <- r[ok]
  switch(method,
         a = {
           ss <- callmatched(smooth.spline,
                             list(x=r, y=K, ...))
           dK <- predict(ss, r, deriv=1)$y
           g <- dK/(2 * pi * r)
         },
         b = {
           y <- K/(2 * pi * r)
           y[!is.finite(y)] <- 0
           ss <- callmatched(smooth.spline,
                             list(x=r, y=y, ...))
           dy <- predict(ss, r, deriv=1)$y
           g <- dy + y/r
         },
         c = {
           z <- K/(pi * r^2)
           z[!is.finite(z)] <- 1
           ss <- callmatched(smooth.spline,
                             list(x=r, y=z, ...))
           dz <- predict(ss, r, deriv=1)$y
           g <- (r/2) * dz + z
         },
         d = {
           z <- sqrt(K)
           z[!is.finite(z)] <- 0
           ss <- callmatched(smooth.spline,
                             list(x=r, y=z, ...))
           dz <- predict(ss, r, deriv=1)$y
           g <- z * dz/(pi * r)
         },
         stop(paste("unrecognised method", sQuote(method)))
         )

  # pack result into "fv" data frame
  Z <- fv(data.frame(r=r, pcf=g, theo=rep(1, length(r))),
          "r", substitute(pcf(r), NULL), "pcf", cbind(pcf, theo) ~ r, alim,
          c("r", "pcf(r)", "1"),
          c("distance argument r",
            "estimate of pair correlation function pcf(r)",
            "theoretical Poisson value, pcf(r) = 1"))
  unitname(Z) <- unitname(X)
  return(Z)
}
back to top