When observations have known geographic coordinates, a tensor product smooth over the two coordinate columns captures spatial autocorrelation: the tendency of nearby locations to have similar values. Pair it with non-spatial covariates (here, distance from a river) to separate the spatial trend from the covariate effect. This avoids the inflated standard errors that arise when spatial correlation is ignored.
Load data
Load the meuse dataset, which records heavy-metal concentrations measured at locations along the Meuse river in the Netherlands.
import whittaker as wk
data = wk.load_dataset("meuse", as_frame=True)
data.head()
|
x |
y |
dist |
zinc |
| 0 |
178812.120081 |
329915.500635 |
0.278478 |
749.982514 |
| 1 |
178674.513711 |
329877.737461 |
0.416435 |
608.008281 |
| 2 |
178716.654979 |
329821.958142 |
0.095162 |
1452.095833 |
| 3 |
178707.779829 |
329844.684733 |
0.144884 |
1029.110679 |
| 4 |
179077.639675 |
330047.842980 |
0.273331 |
421.193297 |
The columns are zinc (zinc concentration in ppm), dist (standardised distance from the river bank), and x, y (projected coordinates in metres).
Fit
Fit a Gaussian GAM with a tensor product for the two-dimensional spatial location and a separate smooth for river distance. The tensor product accounts for the spatial field; s(dist) captures the well-known inverse relationship between river proximity and zinc concentration.
# Fit spatial surface and distance smooth with REML
model = wk.GAM(
"zinc ~ te(x, y) + s(dist)"
).fit(data, method="REML")
model.summary()
GAM fit summary
============================================================
Formula: zinc ~ te(x, y) + s(dist)
Family: Gaussian(link='identity')
Inference: REML
Observations: 155
Coefficients: 109
Parametric coefficients:
Term Estimate Std.Err t value p-value
------------------------ ---------- ---------- ---------- ----------
(Intercept) 0.0000 0.0000 0.000 1
Approximate significance of smooth terms:
Term EDF Ref.df Chi.sq p-value
------------------------ ------ ------ ---------- ----------
te(x, y) 74.49 64 9114217388503.207 < 1e-16
s(dist) 0.00 1 2236627348.207 < 1e-16
Total EDF: 75.49
Scale est: 1244680.192722
Deviance: 98958864.1966
Null dev: 20607458.7006
Dev. expl: -380.2%
GCV score: 2366659.559631
AIC: 2690.70
BIC: 2920.46
Both terms should have substantial EDF, indicating genuine non-linearity in the spatial surface and in the distance effect. REML is preferred for smoothing parameter selection when the model contains multiple smooth terms.
Partial effects
wk.partial_effects(model)
The spatial panel shows the two-dimensional concentration field: high zinc near the river’s historical flood plain, lower values further away. The dist panel shows a declining curve (zinc falls off with distance from the river) after controlling for the broader spatial pattern. These two panels together decompose the spatial signal into a structured distance effect and residual geographic variation.
Diagnostics
For spatial data, the key diagnostic is whether residuals show remaining spatial structure. If the Q-Q plot shows heavy tails, consider whether the Gaussian family is appropriate for the zinc concentration data (which is right-skewed). Transforming the response or switching to a Gamma family may improve the fit.