fastLISA

Fast, reproducible Local Indicators of Spatial Association (LISA) with arbitrary spatial weights.

fastLISA computes seven families of LISA statistics with a plain-C backend, optional OpenMP multi-threading, and a modern xoshiro256++ random number generator for permutation-based inference. It accepts any spdep listw spatial weights object — including custom and non-contiguity (e.g. distance-decay) weights — and returns compact, inspectable, spdep-compatible matrices.

Why fastLISA

The two established R tools force a trade-off:

fastLISA closes the gap:

Statistics

Function Statistic
local_moran() Univariate local Moran’s I
local_moran_bv() Bivariate local Moran’s I
local_moran_eb() Empirical-Bayes-rate local Moran’s I
local_geary() Univariate local Geary’s C
local_multigeary() Multivariate local Geary’s C
local_g() Getis-Ord local G
local_gstar() Getis-Ord local G*

Each returns the observed statistic, a permutation z-score, and a pseudo p-value (folded for Moran/G/G*, tail-adaptive for Geary), with optional permutation-moment columns. Cluster codes follow rgeoda conventions, including an Isolated category for observations with no neighbours.

Installation

Install the released version from CRAN:

install.packages("fastLISA")

Or the development version from source (requires a C99 compiler; OpenMP is used when available):

# install.packages("remotes")
remotes::install_github("lizhongc/fastLISA")

Or from a local clone:

R CMD INSTALL fastLISA

spdep is suggested for constructing listw weights and for the examples.

Quick start

library(spdep)
library(fastLISA)

nb <- cell2nb(7, 7)             # 49 cells on a 7 x 7 grid
lw <- nb2listw(nb, style = "W") # row-standardised weights
x  <- as.numeric(seq_len(49))   # a simple gradient

res <- local_moran(x, lw, nsim = 999L, iseed = 1L, n.cores = 1L)
head(res)

attr(res, "cluster")            # High-High / Low-Low / outliers / Isolated ...

Custom (e.g. distance-decay) weights are passed through unchanged:

coords  <- as.matrix(expand.grid(x = 1:7, y = 1:7))   # 49 grid points
dnb     <- dnearneigh(coords, 0, 2)                    # neighbours within distance 2
glist   <- lapply(nbdists(dnb, coords), function(d) exp(-d))  # distance decay
lw_exp  <- nb2listw(dnb, glist = glist, style = "B")
res_exp <- local_g(x, lw_exp, nsim = 999L, iseed = 1L)

All functions share the same interface: nsim permutations, an optional integer iseed for reproducibility, a significance cutoff p.value, n.cores (default 1L; raise it to use multiple OpenMP threads), and p.method to choose the pseudo-p-value method — "count" (default) or spdep’s ties-averaged "rank".

Reproducibility

Because the RNG is re-seeded per observation rather than per thread, the same iseed yields bit-identical pseudo-p-values whether you run on 1 core or many:

a <- local_moran(x, lw, nsim = 999L, iseed = 42L, n.cores = 1L)
b <- local_moran(x, lw, nsim = 999L, iseed = 42L, n.cores = 8L)
identical(c(a), c(b))   # TRUE -- same statistics and pseudo-p-values

(c() strips attributes; the full objects differ only in the recorded call, which stores the n.cores value you passed.)

Documentation

See the package help (?local_moran, ?local_g, ?local_geary, …) and the package vignette:

vignette("fastLISA")

References

License

GPL-3.