Function Repository Resource:

MoranI

Source Notebook

Compute Moran's I spatial autocorrelation coefficient

Contributed by: Roman Parker

ResourceFunction["MoranI"][values]

evaluates Moran's I statistic of values using a predefined weights matrix.

ResourceFunction["MoranI"][values,weights]

evaluates Moran's I statistic using the matrix of the spatial weights.

Details

Moran's I can be computed by , where wij is the value at position (i,j) in w (the weights matrix), n is the length of the values list, is the mean of the values list and yi is the ith element of the values list.
Moran's I can also be computed using , where n and w are identical to the previous definition and z is equal to y-Mean[y].
values can be any (1D) list of real numbers, each of which are values of a different region, such as rainfall, area or elevation.
In Moranl[values,weights], weights must be a square matrix with length equal to the length of values and zeroes on the top-left–bottom-right diagonal, whose values represent the distance between the regions.
ResourceFunction["MoranI"] returns a real value n between -1 and 1, as follows:
n = 1perfect correlation (values with a nonzero weight have identical values)
n ≈ 0no correlation with the weight matrix used (usually corresponding to random data, an improper weights matrix or no correlation)
n=-1perfect anticorrelation (values which have a nonzero weight have exactly opposite values)
Often, although not always, weights is symmetrical along this diagonal, since the distance function used in its computation is usually commutative.
Moran's I is primarily used for geography/GIS, but can be applied in other fields.
ResourceFunction["MoranI"] can be used for data in any number of dimensions, since the input is only the values and distances, but multivariate autocorrelation is not supported.
When weights is not provided, the weights matrix is predefined such that the weights value at location (x,y) in the table is 1 if x and y are "next to" each other in values, and 0 otherwise.
There is no good way to select a weights matrix without knowing the source and shape of the data, but as long as the values are in a resonable order, the default weights matrix should provide somewhat meaningful results.
AutocorrelationTest and ResourceFunction["MoranI"] both use a values list and calculate the correlation of that list with itself, but unlike AutocorrelationTest, which uses either adjacent elements in the list or a lag of arbitrary length, ResourceFunction["MoranI"] can be used with an arbitrary weights matrix to define the relations between elements.
Moran's I allows for setting weight values other than 0 and 1 (especially useful if the weight of correlation is proportional to the distance), while AutocorrelationTest assumes that elements exactly 1 "step" away (or exactly n steps, if you are using a lag value) have a weight of 1 and everything else has a weight of 0.
Moran's I is very specific to an individual weight matrix, giving completely different results if the weights are changed. Due to this, values returned by ResourceFunction["MoranI"] cannot be compared with each other if the weights matrix is different, making it less useful for surveys across multiple regions.
ResourceFunction["MoranI"] supports symbolic computation.

Examples

Basic Examples (4) 

Define and show our values list:

In[1]:=
valuesListExample1 = {1, -1, -1, 1}; ArrayPlot[{valuesListExample1}, ColorRules -> {-1 -> Blue, 1 -> Orange}]
Out[1]=

Use the default weights matrix to compute MoranI:

In[2]:=
ResourceFunction["MoranI"][valuesListExample1]
Out[2]=

Show a custom weights matrix:

In[3]:=
weightsListExample1 = {{0, 1, 1, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}, {1, 1, 1, 0}}; MatrixPlot[{{0, 1, 1, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}, {1, 1, 1, 0}}]
Out[3]=

Compute MoranI using this matrix instead:

In[4]:=
ResourceFunction[
 "MoranI"][{1, -1, -1, 1}, {{0, 1, 1, 1}, {1, 0, 0, 1}, {1, 0, 0, 1}, {1, 1, 1, 0}}]
Out[4]=

Scope (2) 

Define 25 square regions in a grid and display them:

In[5]:=
testReg1 = Flatten@Table[Rectangle[{i, j}], {i, 0, 4}, {j, 0, 4}]; Graphics@
 Riffle[RandomColor[Length[testReg1]], testReg1]
Out[5]=

Define a values list where every other cell has a value of 1 and the rest have a value of 0:

In[6]:=
testVals1 = Mod[Total /@ testReg1[[All, 1]], 2]; MatrixPlot[
 Partition[testVals1, 5]]
Out[6]=

Use a predefined weights matrix instead of a manual one for computing MoranI:

In[7]:=
ResourceFunction["MoranI"][testVals1]
Out[7]=

MoranI supports symbolic computation:

In[8]:=
ResourceFunction[
 "MoranI"][{x, y, z}, {{a, b, c}, {d, e, f}, {g, h, i}}]
Out[8]=
In[9]:=
ResourceFunction["MoranI"][{x, 1, 2}]
Out[9]=

Applications (4) 

Create a list of four points:

In[10]:=
points = {{1, 0}, {1, 2}, {5, 5}, {3, 2}}; ListPlot[points]
Out[10]=

Create a weights matrix based on how close together the points are:

In[11]:=
weights = Table[EuclideanDistance[points[[i]], points[[j]]], {i, 4}, {j, 4}];
MatrixPlot[weights]
Out[12]=

Create random numbers as values for each point:

In[13]:=
valueTable = RandomReal[{0, 1}, {3, 4}]
Out[13]=

Find the mean spatial autocorrelation of several different value lists over the same weights matrix:

In[14]:=
Mean[ResourceFunction["MoranI"][#, weights] & /@ valueTable]
Out[14]=

Possible Issues (2) 

The weights matrix cannot total to zero, since that would cause ΣiΣjwij (the denominator of the second fraction) to equal zero and lead to an indeterminate answer:

In[15]:=
ResourceFunction[
 "MoranI"][{1, 3, 5}, {{0, -1, 0}, {0, 0, 0}, {0, 1, 0}}]
Out[15]=

The first input cannot have all elements be equal, since that leads to (the first denominator) to also equal zero, giving an indeterminate answer:

In[16]:=
ResourceFunction["MoranI"][{1, 1, 1, 1, 1}]
Out[16]=

Neat Examples (3) 

Define a weights matrix corresponding to whether each pair of squares touch and show it:

In[17]:=
testReg1 = Flatten@Table[Rectangle[{i, j}], {i, 0, 4}, {j, 0, 4}]; Graphics@
 Riffle[RandomColor[Length[testReg1]], testReg1];
In[18]:=
weightTest1 = Sign@ReplacePart[
   RegionMeasure[#, 1] & /@ Table[RegionIntersection[testReg1[[i]], testReg1[[j]]], {i, Length[testReg1]}, {j, Length[testReg1]}], Table[{i, i}, {i, Length[testReg1]}] -> 0]; MatrixPlot[weightTest1]
Out[18]=

Compute the extent to which squares touching each other corresponds to them having similar values:

In[19]:=
testVals1 = Mod[Total /@ testReg1[[All, 1]], 2]; MatrixPlot[
 Partition[testVals1, 5]]; ResourceFunction[
 "MoranI"][testVals1, weightTest1]
Out[19]=

Show the default weights matrix with length 25:

In[20]:=
MatrixPlot[
 Table[If[Abs[i - j] == 1, 1, 0], {i, Length[testVals1]}, {j, Length[testVals1]}]]
Out[20]=

Define a function to compute the length of the border between two countries:

In[21]:=
border[reg1_, reg2_] := Module[{boundary1, boundary2, ref, index0, index1, psRef},
   boundary1 = CountryData[
    reg1, "FullCoordinates"]; boundary2 = CountryData[
    reg2, "FullCoordinates"]; ref = Part[
MinimalBy[{boundary1, boundary2}, Length], 1]; index0 = Map[Flatten[
Position[ref, #]]& , 
Intersection[
Flatten[boundary1, 1], 
Flatten[boundary2, 1]]]; index1 = GroupBy[
    index0, First -> Last, FindClusters[
Sort[#], Method -> "NeighborhoodContraction"]& ]; psRef[
Pattern[x, 
Blank[]], 
Pattern[y, 
Blank[]]] := Part[ref, x, y]; (Flatten[#, 2]& )[
KeyValueMap[Outer[psRef, {#}, #2]& , index1]]]

Compute the border length between every pair of countries in South America:

In[22]:=
saCountries = EntityList[EntityClass["Country", "SouthAmerica"]];
len = Length[saCountries];
borderlength = Table[If[i == j, 0, GeoLength@
     GeoPath[GeoPosition /@ Flatten[border[saCountries[[i]], saCountries[[j]]], 1], "Geodesic"]], {i, 1, len}, {j, 1, len}];

Compute the army size of each country:

In[23]:=
armysize = #["Army"] & /@ saCountries /. Missing["NotAvailable"] -> Quantity[0, "People"];

Find how much sharing a long border is correlated to similar army size:

In[24]:=
ResourceFunction["MoranI"][QuantityMagnitude[armysize], QuantityMagnitude[
  If[Length[#] == 2, UnitConvert[#, "Feet"], #] & /@ # & /@ borderlength]]
Out[24]=

Publisher

Roman Parker

Version History

  • 1.0.1 – 20 September 2021

Source Metadata

Related Resources

License Information