Modelling geostatistical data
In many ecological and environmental settings, measurements are taken from fixed sampling units, aiming to quantify spatial variation and interpolate values at unobserved sites.
We treat the observed process of interest as being measured with error
\[ (\text{observed value})_i = (\text{true value at location } i) + (\text{error})_i \]
alternatively
\[ y_i = Z(\mathbf{s}_i) + \varepsilon_i \]
When geostatistical data are considered, we can often assume that there is a spatially continuous variable underlying the observations that can be modelled using a random field.
we have a process that is occurring everywhere in space \(\rightarrow\) natural to try to model it using some sort of function (of space)
a random field is a random function that generates smooth surfaces
this is hard
we typically make our lives easier by making everything Gaussian
A Gaussian random field (GRF) is a collection of random variables, where observations occur in a continuous domain, and where every finite collection of random variables has a multivariate normal distribution
\[ \mathbf{z} = (z(\mathbf{s}_1),\ldots,z(\mathbf{s}_m)) \sim N(\mu(\mathbf{s}_1),\ldots,\mu(\mathbf{s}_m),\Sigma), \]
where \(\Sigma_{ij} = \mathrm{Cov}(z(\mathbf{s}_i),z(\mathbf{s}_j))\) is a dense \(m \times m\) matrix.
The first step in defining a model for a random field in a hierarchical framework is to identify a probability distribution for the observations available at \(m\) sampled spatial locations and represented by the vector \(\mathbf{y} = y_1,\ldots,y_m\).
For example, if we assume our observations follow a Gaussian distribution then
\[ \begin{aligned} Y_i &\sim N(\mu_i,\tau_e^{-1})\\ \eta_i &=\mu_i = \beta_0 + \ldots + Z(\mathbf{s}_i) \end{aligned} \]
\(\tau_e^{-1} = \sigma^2_e\) represents the variance of the zero-mean measurement error
The response mean \(\mu_i\), which coincides with the linear predictor \(\eta_i\), is defined based on:
the intercept \(\beta_0\) and any additional covariates
the realization of the latent (unobservable) GF \(Z(\mathbf{s}) \sim \mathrm{MVN}(0,\Sigma)\), which accounts for the spatial correlation through \(\Sigma = C_z(\cdot,\cdot)\).
A commonly used covariance function is the Matérn covariance function. The covariance of two points which are a distance \(h\) apart is:
\[ \Sigma =C_{\nu}(h) = \sigma^2 \frac{2^{1-\nu}}{\Gamma(\nu)} \left( \frac{\sqrt{2\nu} h}{\rho} \right)^{\nu} K_{\nu} \left( \frac{\sqrt{2\nu} h}{\rho} \right) \]
\(\Gamma(\cdot)\) is the gamma function
\(K_{\nu}(\cdot)\) is the modified Bessel function of the second kind.
Parameters \(\sigma^2\), \(\rho\) and \(\nu\) are non-negative values of the covariance function.
\(\sigma^2\) is the spatially structured variance component
\(\rho\) is the range of the spatial process
\(\nu\) controls smoothness of the spatial process.
The disadvantage of the modelling approach involving the spatial covariance function is known as “big n problem” and concerns the computational costs required for algebra operations with dense covariance matrices (such as \(\Sigma\)).
In particular dense matrix operations scale cubically with the matrix size, given by the number of locations where the process is observed. A computationally effective alternative is given by the stochastic partial differential equation (SPDE) approach
a different approach to GRF models:
forget about the covariance function!
express spatial structure through an SPDE (stochastic partial differential equation):
a simple differential equation describes change in time
a partial differential equation describes change in space (in more than 1 direction!)
a stochastic partial differential equation makes this random,i.e. not deterministic
need to approximate spatial structures in a clever way.
We define a (Matérn) GRF as the solution of a stochastic partial differential equation (SPDE)
\[ (\kappa^2-\Delta)^{\alpha/2}Z(t) = W(t) \]
What is this?
Imagine a guitar string stretched from left to right.
Now imagine someone randomly taps along it at many locations:
This is pure randomness. But a real string does not behave like this
Imagine a guitar string stretched from left to right.
\[(\color{red}{\kappa^2}-\Delta)Z(t) = W(t), ~~~\text{for } \alpha=2\]
Imagine a guitar string stretched from left to right.
\[ (\kappa^2-\Delta)^{\color{red}{\alpha}/2}Z(t) = W(t) \]
Ok…but we still need to solve the SPDE to find \(Z(t)\)!
Now we need to discretize the domain into T points (we cannot represent continuous space in the computer!)
We represent our solution as
\[ Z(t) = \sum_{i = 1}^T\psi_i(t)w_i, \]
where
\(\psi_i(t)\) are (known) basis functions for nodes \(i=1,\ldots,T\)
Ok…but we still need to solve the SPDE to find \(Z(t)\)!
Now we need to discretize the domain into T points (we cannot represent continuous space in the computer!)
We represent our solution as
\[ Z(t) = \sum_{i = 1}^T\psi_i(t)w_i \]
Where
\(\psi_i(t)\) are (known) basis functions for nodes \(i=1,\ldots,T\)
\(w_i\) are (unknown) weights
Ok…but we still need to solve the SPDE to find \(Z(t)\)!
Now we need to discretize the domain into T points (we cannot represent continuous space in the computer!)
We represent our solution as
\[ Z(t) = \sum_{i = 1}^T\psi_i(t)w_i \]
Where
\(\psi_i(t)\) are (known) basis functions for nodes \(i=1,\ldots,T\)
\(w_i\) are (unknown) weights
This solution is then approximated using a finite combination of piece-wise linear basis functions.
The solution is completely defined by a Gaussian vector of weights with zero mean and a sparse precision matrix.
Now we approximate the GRF using a triangulated mesh.
The SPDE approach represents the continuous spatial process as a continuously indexed Gaussian Markov Random Field (GMRF)
Now we approximate the GRF using a triangulated mesh.
The SPDE approach represents the continuous spatial process as a continuously indexed Gaussian Markov Random Field (GMRF)
We construct an appropriate lower-resolution approximation of the surface by sampling it in a set of well designed points and constructing a piece-wise linear interpolant.
Note that \(\nu = \alpha - d/2\). For \(\alpha=2 \Rightarrow \nu= 1\) since \(d=2\) we have that:
Note
\[ \begin{aligned} Z(s) &= \sum_{i = 1}^K\psi_i(s)w_i \\ \mathbf{w} &\sim N(\mathbf{0},Q^{-1}) \leftarrow \text{GMRF}\\ Q^{-1} &= \tau^2(\kappa^4 \mathbf{C} + 2\kappa^2 \mathbf{G}+\mathbf{G}\mathbf{C}^{-1}\mathbf{G}) \end{aligned} \]
\(\mathbf{C}\) is diagonal with entries \(C_{ii} =\int \psi_i(s)\mathrm{d}s\) and measures how much of the domain each basis function covers.
\(G_{ij} = \int \nabla \psi_i(s) \nabla \psi_j(s) \mathrm{d}s\) reflects the connectivity of the mesh nodes.
because each basis function overlaps only with nearby ones, the resulting precision matrix is sparse, meaning each coefficient depends directly only on its neighbors
\[ Z(s) = \sum_{i = 1}^K\psi_i(s)w_i \]
The weights vector \(\mathbf{w} = (w_1,\dots,w_K)\) is Gaussian with a sparse precision matrix \(\longrightarrow\) Computational convenience
The field has two parameters
These parameters are linked to the parameters of the SPDE
We need to assign prior to them
Penalized Complexity (PC) priors proposed by Simpson et al. (2017) allow us to control the amount of spatial smoothing and avoid overfitting.
D. Miller, R. Glennie and A. Seaton. (2020). Understanding the Stochastic Partial Differential Equation Approach to Smoothing. Journal of Agricultural, Biological and Environmental Statistics, 25(1), 1-16.
F. Lindgren, H. Rue, and J. Lindström. An explicit link between Gaussian fields and Gaussian Markov random fields: The SPDE approach (with discussion). In: Journal of the Royal Statistical Society, Series B 73.4 (2011), pp. 423–498.
H. Bakka, H. Rue, G. A. Fuglstad, A. Riebler, D. Bolin, J. Illian, E. Krainski, D. Simpson, and F. Lindgren. Spatial modelling with R-INLA: A review. In: WIREs Computational Statistics 10:e1443.6 (2018). (Invited extended review). DOI: 10.1002/wics.1443.
E.T. Krainski, V. Gómez-Rubio, H. Bakka, A. Lenzi, D. Castro-Camilio, D. Simpson, F. Lindgren, and H. Rue. Advanced Spatial Modeling with Stochastic Partial Differential Equations using R and INLA. Github version . CRC press, Dec. 20
In the next example, we will explore data on the Pacific Cod (Gadus macrocephalus) from a trawl survey in Queen Charlotte Sound.
Stage 1 Model for the response \[ y(s)|\eta(s)\sim\text{Binomial}(p(s)) \]
Stage 2 Latent field model \[ \eta(s) = \mathrm{logit}(p(s)) = \beta_0 + \beta_1 \,\log \text{depth}(s)+ Z(s) \]
Stage 3 Hyperparameters
Stage 1 Model for the response \[ y(s)|\eta(s)\sim\text{Binomial}(p(s)) \]
Stage 2 Latent field model \[ \eta(s) = \mathrm{logit}(p(s)) = \beta_0 +\beta_1 \,\log \text{depth}(s) + Z(s) \]
Stage 3 Hyperparameters
Stage 1 Model for the response \[ y(s)|\eta(s)\sim\text{Binomial}(p(s)) \]
Stage 2 Latent field model \[ \eta(s) = \mathrm{logit}(p(s)) = \beta_0 + \beta_1 \,\log \text{depth}(s) + Z(s) \]
Stage 3 Hyperparameters
First, we need to create the mesh used to approximate the random field.
max.edge for maximum triangle edge lengthsoffset for inner and outer extensions (to prevent edge effects)cutoff to avoid overly small triangles in clustered areasAll random field models need to be discretised for practical calculations.
The SPDE models were developed to provide a consistent model definition across a range of discretisations.
We use finite element methods with local, piecewise linear basis functions defined on a triangulation of a region of space containing the domain of interest.
Deviation from stationarity is generated near the boundary of the region.
The choice of region and choice of triangulation affects the numerical accuracy.
If the mesh is too fine \(\rightarrow\) heavy computation
If the mesh is to coarse \(\rightarrow\) not accurate enough
Some guidelines
Create triangulation meshes with fm_mesh_2d():
edge length should be around a third to a tenth of the spatial range
Move undesired boundary effects away from the domain of interest by extending to a smooth external boundary:
Use a coarser resolution in the extension to reduce computational cost (max.edge=c(inner, outer)), i.e., add extra, larger triangles around the border
Use a fine resolution (subject to available computational resources) for the domain of interest (inner correlation range) and avoid small edges ,i.e., filter out small input point clusters (0 \(<\) `cutoff \(<\) inner)
Coastlines and similar can be added to the domain specification in fm_mesh_2d() through the boundary argument.
simplify the border
We use the inla.spde2.pcmatern to define the SPDE model using PC priors through the following probability statements
\(P(\rho < 100) = 0.5\)
\(P(\sigma > 1) = 0.5\)
The Model
\[ \begin{aligned} y(s)|\eta(s) & \sim\text{Binom}(1, p(s))\\ \eta(s) & = \color{#FF6B6B}{\boxed{\beta_0}} + \color{#FF6B6B}{\boxed{ \beta_1 \,\log \text{depth}(s) }} + \color{#FF6B6B}{\boxed{ Z(s)}}\\ \end{aligned} \]
depth is a column in our sf datageometry column allow us to use spatial objects as inputSimple feature collection with 232 features and 2 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 343.0617 ymin: 5639.857 xmax: 576.9152 ymax: 5837.211
Projected CRS: +proj=utm +zone=9 +datum=WGS84 +no_defs +type=crs +units=km
# A tibble: 232 × 3
depth present geometry
<dbl> <dbl> <POINT [km]>
1 201 1 (446.4752 5793.426)
2 212 1 (446.4594 5800.136)
3 220 0 (448.5987 5801.687)
# ℹ 229 more rows
The code
# define model component
cmp = ~ -1 + Intercept(1) + depth_eff(log(depth), model='linear') +
space(geometry, model = spde_model)
# define model predictor
eta = present ~ Intercept + depth_eff + space
# build the observation model
lik = bru_obs(formula = eta,
data = pcod_sf,
family = "binomial")
# fit the model
fit = bru(cmp, lik)The Model \[ \begin{aligned} y(s)|\eta(s) & \sim\text{Binom}(1, p(s))\\ \eta(s) & = \beta_0 + \beta_1 \,\log \color{#FF6B6B}{\boxed{\text{depth}(s) } } + Z(s)\\ \end{aligned} \]
depth_r is a raster and depth a layer (be careful with NA’s)..data. is a keyword referring to whatever data object is passed to bru_obs() or bru()eval_spatial() extracts the value of the spatial covariate at the locations in the dataSpatial covariates
The code
# define model component
cmp = ~ -1 + Intercept(1) + depth_eff(log(depth_r$depth), model='linear') +
space(geometry, model = spde_model)
# define model predictor
eta = present ~ Intercept + depth_eff + space
# build the observation model
lik = bru_obs(formula = eta,
data = pcod_sf,
family = "binomial")
# fit the model
fit = bru(cmp, lik)The Model \[ \begin{aligned} y(s)|\eta(s) & \sim\text{Binom}(1, p(s))\\ \color{#FF6B6B}{\boxed{\eta(s)}} & = \color{#FF6B6B}{\boxed{\beta_0 + \beta_1 \,\log \text{depth}(s) + Z(s)}}\\ \end{aligned} \]
Simple feature collection with 232 features and 2 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 343.0617 ymin: 5639.857 xmax: 576.9152 ymax: 5837.211
Projected CRS: +proj=utm +zone=9 +datum=WGS84 +no_defs +type=crs +units=km
# A tibble: 232 × 3
depth present geometry
<dbl> <dbl> <POINT [km]>
1 201 1 (446.4752 5793.426)
2 212 1 (446.4594 5800.136)
3 220 0 (448.5987 5801.687)
# ℹ 229 more rows
The code
# define model component
cmp = ~ -1 + Intercept(1) + depth_eff(log(depth), model='linear') +
space(geometry, model = spde_model)
# define model predictor
eta = present ~ Intercept + depth_eff + space
# build the observation model
lik = bru_obs(formula = eta,
data = pcod_sf,
family = "binomial")
# fit the model
fit = bru(cmp, lik)The Model
\[ \begin{aligned} \color{#FF6B6B}{\boxed{y(s)|\eta(s)}} & \sim \color{#FF6B6B}{\boxed{\text{Binom}(1, p(s))}}\\ \eta(s) & = \beta_0 + \beta_1 \,\log \text{depth}(s) + Z(s)\\ \end{aligned} \]
Simple feature collection with 232 features and 2 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 343.0617 ymin: 5639.857 xmax: 576.9152 ymax: 5837.211
Projected CRS: +proj=utm +zone=9 +datum=WGS84 +no_defs +type=crs +units=km
# A tibble: 232 × 3
depth present geometry
<dbl> <dbl> <POINT [km]>
1 201 1 (446.4752 5793.426)
2 212 1 (446.4594 5800.136)
3 220 0 (448.5987 5801.687)
# ℹ 229 more rows
The code
# define model component
cmp = ~ -1 + Intercept(1) + depth_eff(log(depth), model='linear') +
space(geometry, model = spde_model)
# define model predictor
eta = present ~ Intercept + depth_eff + space
# build the observation model
lik = bru_obs(formula = eta,
data = pcod_sf,
family = "binomial")
# fit the model
fit = bru(cmp, lik)We can plot the posterior density of the model components via plot(model, "model component") and also look at the correlation/covariance function as follows:
In geostatistical applications, the main interest resides in the spatial prediction of the spatial latent field or of the response variable in new locations
Suppose we observe a spatial process \({Z(s): s \in \mathcal{D}}\) at locations \(s_1,\dots,s_n\).
Our goal: predict the variable of interest at an unobserved location \(s_0 \in \mathcal{D}\).
given the data \(y = (y_1,\dots,y_n)\), what can we say about \(Z(s_0)\)?
Rather than a single guess, we want a full uncertainty-aware prediction.
In a Bayesian setting, prediction is a probabilistic task.
The key lies in the posterior predictive distribution
\[ \pi(\tilde{Y} \mid y) = \int \pi(\tilde{Y} \mid \Theta, y)\, \pi(\Theta \mid y)\, d\Theta, \] where \(\Theta\) denotes all latent components and hyperparameters.
Define a grid of points where we want to predict. fm_pixel() creates a regular grid of points covering the mesh (we will see in the practical how your the raster can be used instead)
Append the values of the covariate(s) (if any) at the prediction locations
inlabru will create a projector matrix \(A\) linking the latent Gaussian field to the prediction locations via the predict function.
We can do predictions for \(Z(s), \eta(s), g^{-1}(\eta(s))\)
pxl = fm_pixels(mesh)
# 2. extract raster values at the point locations
vals <- terra::extract(depth_r$depth, pxl, ID = FALSE)
# 3. bind the extracted values onto the sf object
pred_data <- cbind(pxl, vals) %>% filter(!is.na(depth))
preds = predict(fit, pred_data,
~data.frame(spde = space,
eta = Intercept +
depth_eff +
space,
p_s = plogis(Intercept +
depth_eff +
space)))Outputs include posterior means, variances, and credible intervals.
spde_pred <- ggplot() +
gg(preds$spde, aes(fill = q0.5),
geom = "tile") +
scale_fill_scico(name= "median SPDE" )
eta_pred <- ggplot() +
gg(preds$eta, aes(fill = sd),
geom = "tile") +
scale_fill_scico(name=expression(eta~"sd"),
palette = "lapaz")
p_pred <- ggplot() +
gg(preds$p_s, aes(fill = mean),
geom = "tile") +
scale_fill_scico(palette = "roma")
spde_pred + eta_pred + p_pred + plot_layout(ncol=1)Gaussian random fields provide components that reflect spatial structure in continuous space
the SPDE approach allows us to approximate these efficiently and flexibly
the models are still GMRFs ⇝ computationally efficient representation
pc-priors are also relevant for spatial models, e.g. to avoid overfitting
Bakka et al. (2019) Cameletti et al. (2013) Bryce et al. (2022) Fichera et al. (2023) Lamouroux et al. (2025) Lindgren et al. (2010) Lindgren et al. (2022) Miller et al. (2020) Moraga et al. (2021) Simpson et al. (2017)