The formula for global Moran's I is:
where i is an index of analysis units (basically, measurement units of of your map, or in your case pixels in the raster) and j is an index of the neighbors of each map unit. The formula for local Moran's I is extremely similar, except that since local Moran's I is calculated separately for each analysis unit indexed by i, in the top part of the fraction you don't need to sum over i:
Values for and will be distributed around the mean, so, intuitively, over the entire study area high and low clusters will offset each other and global Moran's I will be constrained to lie between -1 and 1. But for local Moran's I, a cluster (high, low, doesn't matter) will be comprised of values where and deviate significantly from the mean, and therefore the top part of the fraction in the second equation will be large in absolute value, much larger than the global deviation from the mean captured in the bottom part of the fraction by .
In your constructed example, you can see this clearly. The top rows are low values, the middle rows are near the mean, and the bottom rows are high values. Therefore, as demonstrated in your second plot, local Moran's I is high in the top and bottom rows, because those rows contain values far from the mean. Local Moran's I is near 0 in the middle rows, because those values are all near the mean. Your example does not show dispersion (the classic checkerboard pattern), so local Moran's I is not negative anywhere.
Let's calculate by hand for one of the pixels. Pixel number 15 has eight neighbors with values 4, 5, 6, 14, 16, 24, 25, 26. So:
x = 1:100
Ii = length(x) *
(15 - mean(x)) *
sum(1 * (c(4, 5, 6, 14, 16, 24, 25, 26) - mean(x))) /
sum((x - mean(x))^2)
Ii
# [1] 12.09961
Incidentally, this does not equal the same value for pixel 15 produced by MoranLocal
:
x1[15]
# 1.512451
At first I thought I did something wrong, so I created a vector 10x10 grid in vector format that was an exact analog of the 10x10 raster and ran it through the localmoran
function in package spdep
. It turns out that MoranLocal
is calculating using a row-standardized weights matrix, whereas the formula I included above is based on using a simple binary queen's contiguity matrix. spdep
gives you control over these options. Using the row-standardized matrix, the are 1/8 (eight neighbors at 1/8 each sum to 1), so:
x = 1:100
Ii = length(x) *
(15 - mean(x)) *
sum(0.125 * (c(4, 5, 6, 14, 16, 24, 25, 26) - mean(x))) /
sum((x - mean(x))^2)
Ii
# [1] 1.512451
The original source for local Moran's I is Anselin (1995), "Local Indicators of Spatial Association—LISA" (appears to be open access).
The expected value of Moran's I is -1/(N-1)
, which for your sample of 38 cases equals -1/(38-1) = -0.02702703
. This is what the software spit out, so that is a good start! So this means that there is really no evidence of negative auto-correlation here, as with random data you would expect it to be a negative value more often than positive.
You interpret the hypothesis test the same way you do any others. That is, you fail to reject the null hypothesis that there is no spatial auto-correlation in the values of year2009
for this sample.
Your spatial weights matrix code looks fine to me to estimate an inverse distance matrix. The biggest thing to look out for when using inverse distances are very short distances, which can make the weights explode. Spatial weights are often arbitrary though, so it is often domain knowledge that helps you choose whether to use inverse distance, or contiguity, or nearest neighbor, etc. type of a spatial weights matrix. So it appears the code to estimate the inverse distance weighted matrix is fine, but I can't say if it is the correct type of spatial weights matrix to use for your situation.
Best Answer
Make a vector of your variable names either by subsetting the column names or programmatically, eg:
For an example, I'm using the
COL.OLD
data you get from?moran.mc
and this set of variables:then repeat using
lapply
over the variable names, and give the returned list the names of the vars:Then extract the statistic and p-value, making a data frame with the correct names:
Which produces:
I don't get how you want the output to be a table, since you only get one statistic per column, and not by
id
as implied in your sample table output. This is a global statistic.Note this only uses base R packages (plus
spdep
) so should work in any R installation.