[R-sig-Geo] directional mantel correlogram
Tyler Smith
tyler.smith at mail.mcgill.ca
Mon Feb 25 18:25:02 CET 2008
Hi,
First off, I'm just learning spatial statistics, so I apologise if I'm
not using the correct terminology.
I'm working on an ecological problem, and I'm trying to distinguish
between two models of community assembly. The first is a completely
intrinsic process, with no relationship between species and the
environment. As such, I expect this process should produce an
isotropic pattern. The alternative process links species abundances to
the underlying environmental gradient, which is spatially structured.
In this case I think the community composition should be anisotropic,
since it is based on an underlying spatial gradient.
It seems to me that I could evaluate this problem with a directional
mantel correlogram, using a measure of community similarity contrasted
against varying distance and direction classes. I found the mgram()
function in package ecodist that computes omnidirectional mantel
correlograms. I've extended this function by adding a second loop to
'mask' the distance matrix for each of the selected angles. I've
pasted my work below, along with a simple test case (which runs in a
minute or two on my machine).
Questions for r-geo gurus:
Is this an appropriate approach to the problem?
Is my implementation correct?
Thanks for your time and patience,
Tyler
dmgram <-
function (species.d, space.xy, breaks, nclass, stepsize,
nperm = 1000, mrank = FALSE, nboot = 500, pboot = 0.9,
cboot = 0.95, alternative = "two.sided", trace = FALSE,
alpha = c(0, 45, 90, 135))
{
space.d <- dist(space.xy)
space.ang <- pw.angle(space.xy)
species.d <- as.vector(species.d)
space <- as.vector(space.d)
## calculate break points for specified angles, given that breaks
## are at the midway between successive angles
ang.breaks <- numeric(length(alpha))
for (i in 1:(length(alpha) - 1))
ang.breaks[i] <- mean(alpha[i:(i+1 )])
ang.breaks[length(ang.breaks)] <- mean(c(180 + alpha[1],
alpha[length(alpha)]))
## any angles between the last break point and 180 are flipped so
## that the first angle class is contiguous
space.ang[space.ang > ang.breaks[length(ang.breaks)]] <-
space.ang[space.ang > ang.breaks[length(ang.breaks)]] - 180
if (missing(breaks)) {
if (missing(nclass)) {
if (missing(stepsize)) {
## reduce the calculated number of classes to account for
## additional angle categories
nclass <- round(1 + 3.3 * log10(length(space.d))/length(alpha))
stepsize <- (max(space.d) - min(space.d))/nclass
}
else {
nclass <- round((max(space.d) - min(space.d))/stepsize)
}
}
else {
if (missing(stepsize)) {
stepsize <- (max(space.d) - min(space.d))/nclass
}
}
breaks <- seq(0, stepsize * nclass, stepsize)
}
else {
nclass <- length(breaks) - 1
}
answer.m <- matrix(0, ncol = 7, nrow = nclass * length(alpha))
dimnames(answer.m) <- list(NULL, c("lag", "ngroup", "mantelr",
"pval", "llim", "ulim","dir"))
answer.m[, 4] <- rep(1, nrow(answer.m))
for (j in 1:length(ang.breaks)) {
if (j == 1) {
amin <- -180 ## set the minimum value for the first angle class
} else
amin <- ang.breaks[j - 1]
amax <- ang.breaks[j]
space.aclass <- matrix(1, nrow = nrow(space.ang),
ncol = ncol(space.ang))
space.aclass[space.ang <= amin] <- 0
space.aclass[space.ang > amax] <- 0
diag(space.aclass) <- 0
space.aclass <- as.dist(space.aclass)
## space.aclass serves as a mask to zero out all distances that
## are not in the current angle class
for (i in 1:nclass) {
row.num <- (j - 1) * nclass + i
dmin <- breaks[i]
dmax <- breaks[i + 1]
answer.m[row.num, 1] <- (dmin + dmax)/2
space.dclass <- rep(0, length(space.d))
space.dclass[(space.d * space.aclass) <= dmin] <- 1
space.dclass[(space.d * space.aclass) > dmax] <- 1
ngroup <- length(space.dclass) - sum(space.dclass)
answer.m[row.num, 2] <- ngroup
if (ngroup > 0) {
mant <- mantel(species.d ~ space.dclass, nperm = nperm,
mrank = mrank, nboot = nboot, pboot = pboot,
cboot = cboot)
answer.m[row.num, 3] <- mant[1]
if (alternative == "two.sided")
answer.m[row.num, 4] <- mant[4]
else answer.m[row.num, 4] <- mant[2]
answer.m[row.num, 5] <- mant[5]
answer.m[row.num, 6] <- mant[6]
}
answer.m[row.num,7] <- alpha[j]
if (trace)
cat(i, "\t", answer.m[i, 2], "\t", answer.m[i, 3],
"\n")
}
}
results <- list(mgram = answer.m, resids = NA)
class(results) <- "dmgram"
results
}
pw.angle <- function(x) {
res <- matrix(NA, nrow = nrow(x), ncol = nrow(x))
for (i in 1:(nrow(x)-1))
for (j in (i+1):nrow(x))
res[i,j] <- atan((x[i,2] - x[j,2])/(x[i,1] - x[j,1])) * 180 / pi
res[lower.tri(res)] <- t(res)[lower.tri(t(res))]
diag(res) <- 0
res[res < 0] = 180 + res[res < 0]
return(res)
}
plot.dmgram <- function (x, pval = 0.05, xlab = "Distance",
ylab = "Mantel r", ...) {
x <- x$mgram
pval.v <- x[, 4]
ang.v <- x[,7]
ang.lev <- unique(ang.v)
ang.num <- length(ang.lev)
plot(x[,1], x[,3], type = 'n', xlab = xlab, ylab = ylab, ...)
for (i in 1:ang.num) {
points(x[x[,7] == ang.lev[i], 1], x[x[,7] == ang.lev[i], 3],
col = i, type = 'l')
points(x[pval.v <= pval & ang.v == ang.lev[i], 1],
x[pval.v <= pval & ang.v == ang.lev[i], 3],
pch = 16, col = i)
points(x[pval.v > pval & ang.v == ang.lev[i], 1],
x[pval.v > pval & ang.v == ang.lev[i], 3],
pch = 1, col = i)
}
invisible()
}
## test case for an artificial isotropic pattern:
coords <- cbind(rep(1:20, each = 20), rep(1:20, 20))
coords2 <- cbind(jitter(coords[,1], factor = 10), jitter(coords[,2],
factor = 10))
coords.dist <- dist(coords)
coords2.dist <- dist(coords2)
dmangram.coords <- dmgram(coords2.dist, coords)
plot(dmangram.coords)
More information about the R-sig-Geo
mailing list