Spatial autocorrelation¶
Spatial autocorrelation statistics measure whether observations close to each other
have similar values. Each needs a spatial weights matrix, which says how strongly
each pair of observations counts as neighbours. The functions can be imported from
pygeostats or pygeostats.spatial_autocorrelation.
The examples use 80 samples of a spatially correlated field:
import numpy as np
rng = np.random.default_rng(42)
coords = rng.uniform(0, 10, size=(80, 2))
distances = np.linalg.norm(coords[:, None, :] - coords[None, :, :], axis=-1)
values = rng.multivariate_normal(
np.zeros(len(coords)), np.exp(-distances / 2.0) + 0.05 * np.eye(len(coords))
)
Spatial weights¶
from pygeostats import (
spatial_weights_distance_band,
spatial_weights_inverse_distance,
spatial_weights_knn,
)
knn = spatial_weights_knn(coords, k=6)
band = spatial_weights_distance_band(coords, threshold=2.0)
inverse = spatial_weights_inverse_distance(coords, power=2.0, max_distance=3.0)
spatial_weights_knngives each observation'sknearest neighbours a weight of 1.spatial_weights_distance_bandgives a weight of 1 to every observation withinthreshold, or, withbinary=False, inverse-distance weights within it. Pick a threshold at which every observation has at least one neighbour.spatial_weights_inverse_distanceweights every pair by distance to the power-power, optionally only withinmax_distance.
Each returns a dense array of shape (n, n), with row i holding the weights of
observation i's neighbours. Rows are standardised to sum to 1 unless
row_standardize=False is passed, and row_standardize_weights() standardises a
matrix built another way.
Weights are dense
Memory grows with the square of the number of observations: 10,000 observations take about 800 MB per matrix.
Moran's I¶
from pygeostats import morans_i
result = morans_i(values, knn)
print(result["I"], result["expected_I"], result["z_score"], result["p_value"])
permuted = morans_i(values, knn, permutations=999, random_state=0)
print(permuted["p_value"])
Moran's I above its expectation under no autocorrelation, \(-1/(n-1)\), indicates that neighbours have similar values; below it, dissimilar values.
The p-value is two-sided either way, and is computed in one of two ways:
- Without permutations, from
z_scoreunder a normal approximation. - With
permutations, from the share of random relabellings of the values that give a statistic at least as extreme. It can be no smaller than1 / (permutations + 1), and noz_scoreis returned.
Geary's C¶
from pygeostats import gearys_c
geary = gearys_c(values, knn, permutations=999, random_state=0)
print(geary["C"], geary["expected_C"], geary["p_value"])
Geary's C is more sensitive to differences between close neighbours than Moran's I. Its expectation is 1; below 1 indicates similar neighbours. P-values work as for Moran's I.
Local statistics¶
Local statistics give one value per observation, to locate clusters rather than test for them overall:
from pygeostats import local_gearys_c, local_morans_i
local_i = local_morans_i(values, knn) # {"I_local": ..., "z": ...}
local_c = local_gearys_c(values, knn) # {"C_local": ..., "z": ...}
They come without p-values, and with many observations some stand out by chance.
Getis-Ord statistics¶
Getis-Ord statistics measure whether high or low values concentrate in space. They are meant for positive values and binary weights that are not row-standardised:
from pygeostats import global_getis_ord_g, local_getis_ord_g
binary_band = spatial_weights_distance_band(coords, threshold=2.0, row_standardize=False)
positive = values - values.min() + 1.0
general_g = global_getis_ord_g(positive, binary_band, permutations=999, random_state=0)
print(general_g["G"], general_g["expected_G"], general_g["p_value"])
local_g = local_getis_ord_g(positive, binary_band)
hot_spots = local_g["z_score"] > 1.96
local_getis_ord_g returns z-scores, under both z_score and G_local, which hold
the same array. With include_self=True, the default, each observation counts in its
own neighbourhood, which gives the Gi* statistic.
For hot spots among events given as coordinates, without building a weights matrix,
see getis_ord_gi_star in Point patterns.