ivspatial — user guide

Nonparametric tests of spatial independence on neighbourhood graphs, version 1.0.0. Companion software to E. Ashtari Nezhad, A nonparametric test of spatial independence on neighbourhood graphs.
Install: pip install ivspatial · Source: github.com/emaad7/ivspatial · R version: github.com/emaad7/ivspatial-r

1. What the package does

ivspatial tests whether the values of a variable observed at spatial units — cells of a grid, polygons such as counties, irregularly located points, or nodes of a network — are independent and identically distributed (i.i.d.) across the units. The tests are rank based and exact: under independence the P-value is valid for every sample size, every graph and every continuous distribution, with no weight matrix, bandwidth or model to choose. The package reports

2. Two ways to use it

Without programming: the window
Open a file, define the neighbourhood graph, pick a variable, press Run test.
With code
spatial_test(x, graph) in Python, or ivspatial data.csv ... in a terminal.
TaskWindowPythonCommand line
Build the graphNeighbourhood graph box, Build graphknn_graph, delaunay_graph, lattice_graph, polygon_contiguity_graph, graph_from_edges--graph knn --x X --y Y --k 4
Test one variablechoose Variable, Run testspatial_test(x, g)--column NAME
Screen many variablestick test allscreen(dict, g)--all
Residuals of a regressiontick test OLS residuals, select regressorsresidual_test(y, X, g)--residuals-on A,B
Data-driven resolution, lagsResolution auto, Lags 2resolution="auto", lags=2--resolution auto --lags 2
MapsMaplocal_features(x, g)--local local.csv
Null distributionNull distribution tabnull_limit(g, d, s)ivspatial null ...

3. Installation

You need Python 3.8 or later (https://www.python.org/downloads/; on Windows tick "Add python.exe to PATH"). Then, in a terminal (Command Prompt on Windows):

pip install ivspatial                                   # from the Python Package Index
pip install "ivspatial[all]"                            # with Excel, shapefile and map support
pip install git+https://github.com/emaad7/ivspatial    # latest version from GitHub

numpy and scipy are installed automatically. Optional extras: pandas and openpyxl for Excel files, pyshp for shapefiles (GeoJSON needs nothing), matplotlib for maps. After installation you have the module import ivspatial, the command ivspatial, and the command ivspatial-gui, which opens the window (also python -m ivspatial). Check with ivspatial --version. On Linux the window may need sudo apt install python3-tk.

Without installing: download the ZIP from GitHub, unzip, run pip install numpy scipy once, and double-click run_gui_windows.bat (Windows), run_gui_mac_linux.command (macOS, Linux) or ivspatial_gui.pyw.

4. Preparing your data

Examples: mercer_hall_wheat.csv (grain and straw yields of 500 plots on a 20 × 25 grid; columns row, col) and baltimore_houses.csv (211 house sales with coordinates X, Y, price and characteristics) in the folder examples.

5. The neighbourhood graph

The neighbourhood of a site is the site and its neighbours. The graph is part of the hypothesis being tested: it says which sites are considered close. Common choices:

DataGraphTypical choice
pointsk nearest neighboursk = 4 to 6; each site has the same number of neighbours
pointsDelaunay triangulationthe contiguity of the Voronoi cells of the points; about 6 neighbours
pointsdistance bandall sites within a distance; check that few sites are isolated
gridrook or queen contiguityrook (4 neighbours) unless diagonal neighbours matter
polygonsqueen (shared vertex) or rook (shared edge)queen
networkedge listthe links of the network

The report states the number of sites and the mean, minimum and maximum number of neighbours; isolated sites contribute nothing. For dependence that reaches beyond immediate neighbours use lags (Section 10) rather than very large neighbourhoods.

6. The window (GUI)

Start it with ivspatial-gui (optionally followed by a file name), python -m ivspatial, or by double-clicking as described above. The Test tab:

  1. Open data file...: a table or a polygon file. Coordinate and index columns named x/y, lon/lat, row/col are recognized automatically.
  2. Neighbourhood graph: choose the type and its columns or parameters and press Build graph; the line below shows the number of sites and neighbours.
  3. Variable and options: the variable, or test all to screen every variable; optionally the log transform; optionally test OLS residuals with the selected regressors. Defaults: B = 9999 random rank arrangements, seed 1 (reproducible P-values), level 0.05, lags 1, resolution C4.
  4. Run test. Save report... writes text or JSON; Map draws the data and the local level and scale features (needs matplotlib).

ivspatial window

The Null distribution tab gives the weighted chi-square limit of T_V(d, s) on the current graph: its mean, variance, critical values and the P-value of any value.

7. Python and the command line

import numpy as np
import ivspatial as iv

w = iv.read_table("examples/mercer_hall_wheat.csv")
g = iv.lattice_from_indices(w["row"], w["col"], "rook")
res = iv.spatial_test(w["grain"], g, seed=1)
print(res)                         # the report below
res.p_value, res.reject            # P-value of C4 and the decision
res.components                     # statistics and P-values of the four components
res.features                       # standardized features
res.to_dict()                      # everything, for saving as JSON

tab = iv.read_table("examples/baltimore_houses.csv")
g = iv.knn_graph(np.c_[tab["X"], tab["Y"]], k=4)          # or iv.delaunay_graph(...)
print(iv.spatial_test(np.log(tab["PRICE"]), g, seed=1))

polys, att = iv.read_polygons("counties.shp")             # or a .geojson file
gq = iv.polygon_contiguity_graph(polys, "queen")
results, adjusted = iv.screen({"HR80": att["HR80"], "HR90": att["HR90"]}, gq, seed=1)

Command line (one line per call):

ivspatial examples/mercer_hall_wheat.csv --graph lattice --row row --col col --column grain --seed 1
ivspatial examples/baltimore_houses.csv --x X --y Y --graph knn --k 4 --column PRICE --transform log --seed 1
ivspatial counties.shp --column HR90 --residuals-on RD90,PS90,UE90,DV90,MA90,SOUTH --seed 1
ivspatial data.csv --x x --y y --graph delaunay --all --out report.txt --json results.json
ivspatial data.csv --x x --y y --column y --resolution auto --lags 2 --local local.csv
ivspatial null data.csv --x x --y y --graph delaunay --d 4 --s 2 --value 35

8. Reading the output

lattice (20 x 25 cells), rook contiguity: n = 500 sites, degree mean 3.82 (min 2, max 4)

Nonparametric test of spatial independence  [grain]
====================================================
Observations n = 500
Neighbourhood graph: lattice (20 x 25 cells), rook contiguity: n = 500 sites, degree mean 3.82 (min 2, max 4)
Random rank arrangements B = 9999

Test                                                      Statistic    P-value    asympt.
C4 = min-P(V43, V42, RM, RS)  [proposed]                                0.0001           
  T_V(4,3): 43 features of order 2..3, 4 classes            586.104     0.0001   < 0.0001
  T_V(4,2): 15 features of order 2, 4 classes               455.889     0.0001   < 0.0001
  RM: rank Moran (level)                                      0.406     0.0001           
  RS: Moran of squared ranks (scale)                          0.156     0.0001           

Spatial independence is REJECTED at the 0.05 level (C4 = min-P(V43, V42, RM, RS)).

Standardized features of T_V(3,2) (approximately N(0,1) under independence):
  n.lin^2             8.21   level: neighbours similar to each other
  n.lin*quad         -2.15   mixed level-scale between neighbours
  n.quad^2           -0.42   scale: neighbours jointly extreme
  c.lin|n.lin        10.93   level: site and neighbours similar
  c.lin|n.quad       -1.16   low/high site among extreme neighbours
  c.quad|n.lin       -1.16   extreme site among low/high neighbours
  c.quad|n.quad       0.58   scale: extremes next to extremes
  |value| > 3 is notable; the sign of mixed features depends on the coding direction.

9. Maps of local features

Every feature is a scaled sum of local contributions, one per site, each with mean 0 and variance 1 under independence. Mapping them shows where a feature receives its contribution, much as local indicators of spatial association decompose Moran's I. Red marks sites that agree with their neighbours in level (level feature) or that are extreme together with their neighbours (scale feature). The maps are descriptive: the local values are dependent and are not tested one by one.

local feature map

10. Data-driven resolution and spatial lags

Resolution. C4 uses four classes and orders two and three, a choice that is close to the best against level and higher-order dependence. With resolution = "auto" the data choose among T_V(d, s), d = 2 to 6, s = 2, 3, by the minimum-P rule; the selection is repeated in every random rank arrangement, so the P-value is exact after selection, and the report states the resolution chosen. "schwarz" uses a penalized choice that prefers simple statistics; it is more powerful when the dependence is low-dimensional and weaker against dependence in the tails.

Lags. With lags = 2 the components are also computed for the neighbours at graph distance two and all are combined by the minimum-P rule; use it when dependence may reach beyond immediate neighbours.

11. Residuals of regressions

residual_test(y, X, g) tests the OLS residuals of y on the columns of X (an intercept is added). With exogenous regressors — including coordinates, trend surfaces and fixed effects — the tests remain valid without any correction. They are not valid for spatial-lag models, in which y is regressed on its spatially lagged values; use a model-based bootstrap there.

12. Troubleshooting

13. Reference and citation

Ashtari Nezhad, E. (2026). A nonparametric test of spatial independence on neighbourhood graphs. Manuscript.
Ashtari Nezhad, E. (2026). ivspatial: nonparametric tests of spatial independence. Python package version 1.0.0. https://github.com/emaad7/ivspatial