Skip to content

nlme: spatial autocorrelation on a sphere

2 messages · Dan Bebber, Spencer Graves

#
This message from Malcolm Fairbrother at R-sig-ME, who has a potential solution...

[Begin forwarded message]
I struggled with this a couple years ago, and the best solution I could find (or at least that I think I found) was to modify corStruct.R, gls.R, and lme.R, adding a new function "dist2" which can calculate distances using "rdist.earth" from the "fields" package. I re-compiled nlme with these modifications, to make dist2 load automatically with nlme (and "fields" loads when required). This makes rdist.earth a valid choice of metrics, for corSpatial and similar functions.
The only change to gls.R and lme.R is to replace:
      distance <- lapply(covar,
                         function(el, metric) dist(as.matrix(el), metric),
                         metric = metric)
with:
      distance <- lapply(covar,
                         function(el, metric) dist2(as.matrix(el), metric),
                         metric = metric)
In corStruct.R you just need to replace calls to "dist" with calls to "dist2", and add the code below to create the function dist2. Then the call looks like, for example:
lme(y ~ x, random = ~ 1 | group, correlation = corExp(form = ~ LONG + LAT | group, metric="rdist.earth"), data=dat)
There may be more elegant ways of doing this (and obviously so for more professional coders), but I believe this works. Maybe this will save you some time, Ben.
Cheers,
Malcolm

dist2 <- function (x, method = "euclidean", diag = FALSE, upper = FALSE, 
    p = 2) 
{
    if (!is.na(pmatch(method, "euclidian"))) 
        method <- "euclidean"
    METHODS <- c("euclidean", "maximum", "manhattan", "canberra", 
        "binary", "minkowski", "rdist.earth")
    method <- pmatch(method, METHODS)
    if (is.na(method)) 
        stop("invalid distance method")
    if (method == -1) 
        stop("ambiguous distance method")
    N <- nrow(x <- as.matrix(x))
    if (method == 7) {
        require(fields)
        d <- rdist.earth(x, miles=F, R=6371)[lower.tri(rdist.earth(x, miles=F, R=6371))]
        } else {
        d <- .C("R_distance", x = as.double(x), nr = N, nc = ncol(x), 
          d = double(N * (N - 1)/2), diag = as.integer(FALSE), 
          method = as.integer(method), p = as.double(p), DUP = FALSE, 
          NAOK = TRUE, PACKAGE = "stats")$d
        }
    attr(d, "Size") <- N
    attr(d, "Labels") <- dimnames(x)[[1L]]
    attr(d, "Diag") <- diag
    attr(d, "Upper") <- upper
    attr(d, "method") <- METHODS[method]
    if (method == 6) 
        attr(d, "p") <- p
    attr(d, "call") <- match.call()
    class(d) <- "dist"
    return(d)
}
[End forwarded message]
#
Hi, Dan:


       That's great.


       Do you have a package that would be a reasonable home for this 
solution?  If yes, I would encourage you to add functions similar to 
those described by Malcolm Fairbrother.  If it were mine, I think I 
might prefer to call rdist.earth directly rather than make a local copy, 
and name the new functions something like corStruct.earth, gls.earth, 
and lme.earth.  (If you haven't done this before but are willing to try, 
you may want to look at the two "contributed" documents on "Creating R 
Packages" on CRAN.)


       I've also had moderate luck writing to the maintainer of another 
contributed package that might logically accept things like this and 
offering to do the work if they would consider such a friendly addition 
to their package.  Some have said, "yes". When that happened, I felt the 
addition would likely be more likely to reach its target audience -- and 
not contribute another package to be kept separate on CRAN.  When 
they've said, "no", I've created another package.


       Thanks for pursuing this issue and publicizing the solution you 
found.
       Spencer
On 10/3/2012 4:34 PM, Dan Bebber wrote: