| Title: | Conformal Prediction for Spatially and Spatio-Temporally Dependent Data |
|---|---|
| Description: | Provides distribution-free, model-agnostic prediction intervals for spatially and spatio-temporally dependent data using localized conformal calibration. Implements locally weighted split conformal prediction for geostatistical (point-referenced) data based on spatial-distance kernels, and a neighbourhood-weighted conformal procedure for areal (lattice) data based on graph adjacency structures. Relaxes the standard exchangeability assumption using spatial proximity, following the localized conformal framework of Mao, Martin and Reich (2024) <doi:10.1080/01621459.2022.2147531>. Includes comprehensive spatial diagnostic tools to audit empirical coverage, conditional spatial strata, and boundary proximity effects. |
| Authors: | Ahmed Sattar Jabbar [aut, cre] (ORCID: <https://orcid.org/0009-0006-4614-735X>) |
| Maintainer: | Ahmed Sattar Jabbar <[email protected]> |
| License: | GPL-3 |
| Version: | 0.1.2 |
| Built: | 2026-10-14 02:14:38 UTC |
| Source: | https://github.com/amjed-droid/spconform |
Computes neighbourhood-based weights for areal (lattice) data based on shortest-path graph distances.
areal_neighbor_weights(i0, adjacency, decay = 1)areal_neighbor_weights(i0, adjacency, decay = 1)
i0 |
Integer index of the target (unobserved / held-out) areal unit. |
adjacency |
A square 0/1 (or weighted) adjacency matrix describing
the neighbourhood structure of the areal units (e.g. a spatial contiguity
matrix). Row/column |
decay |
Numeric decay rate applied to graph distance (number of hops)
from |
A numeric vector of length nrow(adjacency) with weights
based on graph distance from i0 (self-weight is 0, i.e. the
target unit is excluded from its own calibration set).
Converts a conformal prediction output object of class "spconform"
into a tidy data.frame containing coordinates (if present), point
predictions, lower/upper bounds, and interval widths.
## S3 method for class 'spconform' as.data.frame(x, row.names = NULL, optional = FALSE, ...)## S3 method for class 'spconform' as.data.frame(x, row.names = NULL, optional = FALSE, ...)
x |
An object of class |
row.names |
|
optional |
Logical; passed to |
... |
Additional arguments (currently unused). |
A data.frame with prediction columns pred,
lower, upper, width, and any coordinate columns.
Evaluates empirical coverage, average prediction interval width,
and the strictly proper Winkler interval score for an "spconform" object.
coverage_report(object, y_true, na.rm = TRUE)coverage_report(object, y_true, na.rm = TRUE)
object |
An object of class |
y_true |
Numeric vector of true observed values at the prediction
locations, same length/order as |
na.rm |
Logical; if |
A named list with:
coverage |
Proportion of observations falling within |
.
mean_width |
Average prediction interval width |
.
winkler_score |
Mean Winkler interval score across evaluation points |
.
set.seed(1) n <- 20 adj <- matrix(0, n, n) for (i in 1:(n - 1)) { adj[i, i + 1] <- 1; adj[i + 1, i] <- 1 } y <- cumsum(rnorm(n)) out <- scp_areal(y, adjacency = adj, alpha = 0.2) coverage_report(out, y)set.seed(1) n <- 20 adj <- matrix(0, n, n) for (i in 1:(n - 1)) { adj[i, i + 1] <- 1; adj[i + 1, i] <- 1 } y <- cumsum(rnorm(n)) out <- scp_areal(y, adjacency = adj, alpha = 0.2) coverage_report(out, y)
Produces a comprehensive multi-panel diagnostic report evaluating marginal coverage validity, Winkler Interval Score (WIS) sharpness, conditional coverage across spatial strata, boundary proximity effects, spatial residual autocorrelation (Moran's I), and nonconformity score distributions.
diagnose(object, y_true, s_test = NULL, n_bins = 4, plot = TRUE, ...)diagnose(object, y_true, s_test = NULL, n_bins = 4, plot = TRUE, ...)
object |
An object of class |
y_true |
Numeric vector of true observed responses at target prediction locations.
Must have the same length as |
s_test |
Optional numeric matrix of target prediction coordinates
( |
n_bins |
Integer; number of spatial bins per dimension for conditional coverage (default 4). |
plot |
Logical; if |
... |
Additional graphical parameters passed to internal plotting methods. |
The diagnostic suite performs five rigorous audits on the prediction intervals:
Marginal Validity & Sharpness: Evaluates empirical coverage
, mean/median interval width,
and the strictly proper Winkler Interval Score (WIS):
Conditional Spatial Stratification: Partitions the 2D spatial domain
into equal-area quadrants and assesses local coverage.
Convex Hull Boundary Proximity: Assesses whether edge-effect extrapolations degrade coverage near the boundary of the spatial domain.
Spatial Autocorrelation (Moran's I): Evaluates whether prediction
miscoverages or nonconformity scores exhibit residual spatial clustering via Moran's .
Score Distribution: Analyzes empirical quantiles and normality/QQ structure.
An S3 object of class "spconform_diagnose" containing:
List with coverage, mean width, median width, sd width, Winkler score, and sample size.
Data frame of coverage and widths partitioned by spatial quadrant.
List comparing empirical metrics between interior and boundary units.
List containing Moran's I statistic, expected value, z-score, and p-value.
Summary of nonconformity score moments and quantiles.
Miscoverage level of the evaluated object.
Winkler, R. L. (1972). "A Decision-Theoretic Approach to Interval Estimation." Journal of the American Statistical Association, 67(337), 187-191.
Mao, R., Martin, R., and Reich, B. J. (2023). "Valid Conformal Prediction for Dependent Data." Journal of the American Statistical Association, doi:10.1080/01621459.2022.2147531.
scp_geostatistical, scp_areal, coverage_report
set.seed(42) n <- 100 s_tr <- matrix(runif(n * 2), n, 2) y_tr <- sin(3 * s_tr[, 1]) + rnorm(n, sd = 0.2) s_te <- matrix(runif(30 * 2), 30, 2) y_te <- sin(3 * s_te[, 1]) + rnorm(30, sd = 0.2) pfun <- function(s_tr, y_tr, s_new) rep(mean(y_tr), nrow(s_new)) out <- scp_geostatistical(s_tr, y_tr, s_te, pfun, alpha = 0.1) diag_res <- diagnose(out, y_true = y_te, s_test = s_te, plot = FALSE) print(diag_res)set.seed(42) n <- 100 s_tr <- matrix(runif(n * 2), n, 2) y_tr <- sin(3 * s_tr[, 1]) + rnorm(n, sd = 0.2) s_te <- matrix(runif(30 * 2), 30, 2) y_te <- sin(3 * s_te[, 1]) + rnorm(30, sd = 0.2) pfun <- function(s_tr, y_tr, s_new) rep(mean(y_tr), nrow(s_new)) out <- scp_geostatistical(s_tr, y_tr, s_te, pfun, alpha = 0.1) diag_res <- diagnose(out, y_true = y_te, s_test = s_te, plot = FALSE) print(diag_res)
Plots point predictions and conformal prediction intervals across observation indices, optionally overlaying true target values for visual evaluation.
## S3 method for class 'spconform' plot(x, y_true = NULL, ...)## S3 method for class 'spconform' plot(x, y_true = NULL, ...)
x |
An object of class |
y_true |
Optional numeric vector of true observed responses at prediction locations. |
... |
Further graphical parameters passed to |
Invisibly returns the input object x.
Generates a multi-panel visual audit of marginal coverage, nonconformity score QQ-distribution, conditional spatial strata coverage, and boundary proximity effects.
## S3 method for class 'spconform_diagnose' plot(x, ...)## S3 method for class 'spconform_diagnose' plot(x, ...)
x |
An object of class |
... |
Additional arguments passed to plotting functions. |
Invisibly returns the input object x.
Extracts point predictions or prediction interval bounds from a fitted
spconform object.
## S3 method for class 'spconform' predict(object, interval = c("none", "prediction"), ...)## S3 method for class 'spconform' predict(object, interval = c("none", "prediction"), ...)
object |
An object of class |
interval |
Character string specifying the type of prediction intervals
to extract: |
... |
Further arguments passed to or from other methods. |
If interval = "none", a numeric vector of point predictions.
If interval = "prediction", a numeric matrix with columns fit,
lwr, and upr.
Displays a concise summary of the conformal prediction object including model type, nominal target coverage, and a preview table of prediction intervals.
## S3 method for class 'spconform' print(x, ...)## S3 method for class 'spconform' print(x, ...)
x |
An object of class |
... |
Further arguments passed to or from other methods. |
Invisibly returns the input object x.
Calculates prediction residuals (observed minus fitted values) or
nonconformity scores for an spconform object given true responses.
## S3 method for class 'spconform' residuals(object, y_true, type = c("response", "abs"), ...)## S3 method for class 'spconform' residuals(object, y_true, type = c("response", "abs"), ...)
object |
An object of class |
y_true |
Numeric vector of true observed responses at prediction locations. |
type |
Character string indicating residual type: |
... |
Further arguments passed to or from other methods. |
A numeric vector of residuals.
Constructs distribution-free prediction intervals for areal units (e.g. counties, census tracts, grid cells) using a leave-one-unit-out conformal procedure in which nonconformity scores are weighted by graph (neighbourhood) distance to the held-out unit, via its adjacency structure.
scp_areal(y, X = NULL, adjacency, pred_fun = NULL, alpha = 0.1, decay = 1)scp_areal(y, X = NULL, adjacency, pred_fun = NULL, alpha = 0.1, decay = 1)
y |
Numeric vector of observed responses, one per areal unit. |
X |
Optional numeric design matrix of covariates (one row per areal
unit); passed to |
adjacency |
Square adjacency (contiguity) matrix describing the
neighbourhood structure among the |
pred_fun |
A function with signature |
alpha |
Miscoverage level; intervals target |
decay |
Decay rate for neighbourhood weights; see
|
Uses a full leave-one-out (jackknife-style) conformal scheme:
for each areal unit , a model is fit on all other units and used
to predict unit ; the resulting nonconformity scores across all
units are combined into a graph-distance-weighted quantile specific to
each target unit, so that the calibration set is dominated by
spatially/graph-proximate units rather than treating all units as
exchangeable.
If the effective neighbourhood weight for a target unit is vanishingly small (e.g., an isolated unit with no neighbours), the procedure falls back to an unweighted quantile over all other units and issues a warning.
An object of class "spconform" (areal variant) with the
same structure as scp_geostatistical, using unit indices
in place of coordinates.
set.seed(1) n <- 25 adj <- matrix(0, n, n) for (i in 1:(n - 1)) { adj[i, i + 1] <- 1; adj[i + 1, i] <- 1 } y <- cumsum(rnorm(n)) + rnorm(n, sd = 0.2) out <- scp_areal(y, adjacency = adj, alpha = 0.2) print(out)set.seed(1) n <- 25 adj <- matrix(0, n, n) for (i in 1:(n - 1)) { adj[i, i + 1] <- 1; adj[i + 1, i] <- 1 } y <- cumsum(rnorm(n)) + rnorm(n, sd = 0.2) out <- scp_areal(y, adjacency = adj, alpha = 0.2) print(out)
Constructs distribution-free prediction intervals at new spatial (or spatio-temporal) locations by combining split conformal prediction with spatial-distance kernel weights, relaxing the exchangeability assumption that standard conformal prediction relies on.
scp_geostatistical( s_train, y_train, s0, pred_fun, alpha = 0.1, split = 0.5, bandwidth = NULL, t_train = NULL, t0 = NULL, temporal_bandwidth = NULL, k_neighbors = NULL, seed = NULL )scp_geostatistical( s_train, y_train, s0, pred_fun, alpha = 0.1, split = 0.5, bandwidth = NULL, t_train = NULL, t0 = NULL, temporal_bandwidth = NULL, k_neighbors = NULL, seed = NULL )
s_train |
Numeric matrix of training coordinates, one row per observation (e.g. longitude/latitude, or projected easting/northing). |
y_train |
Numeric vector of training responses, same length as
|
s0 |
Numeric matrix of coordinates at which prediction intervals are desired, one row per target location. |
pred_fun |
A function with signature |
alpha |
Miscoverage level; prediction intervals target
|
split |
Proportion of the training data used for model fitting; the remainder is used as the calibration set for conformal scores. Default 0.5. |
bandwidth |
Optional spatial kernel bandwidth passed to
|
t_train, t0, temporal_bandwidth
|
Optional time indices (and temporal
bandwidth) for spatio-temporal weighting; see
|
k_neighbors |
Optional integer; number of nearest calibration neighbours
to retain for localized calibration. Defaults to |
seed |
Optional integer seed for the train/calibration split, for reproducibility. |
This implements a localized split-conformal procedure in the spirit of Mao, Martin and Reich (2020, "Valid model-free spatial prediction") and subsequent spatio-temporal extensions: nonconformity scores from a held-out calibration set are combined into a weighted empirical quantile where weights decay with distance (in space, and optionally time) from the target location, so nearby, more relevant observations dominate the calibration of the interval while validity is retained under a local exchangeability argument.
An object of class "spconform", a list with components:
Point predictions at s0.
Lower/upper prediction interval bounds at
s0.
The requested miscoverage level.
The target coordinates.
The matched call.
Mao, H., Martin, R., and Reich, B. J. (2020). Valid model-free spatial prediction. arXiv:2006.15640.
set.seed(42) n <- 200 s <- matrix(runif(2 * n), ncol = 2) f <- function(s) sin(4 * s[, 1]) + cos(3 * s[, 2]) y <- f(s) + rnorm(n, sd = 0.3) idx <- sample(n, 150) s_train <- s[idx, ]; y_train <- y[idx] s0 <- s[-idx, ][1:10, ] pred_fun <- function(s_train, y_train, s_new) { fit <- lm(y_train ~ s_train[, 1] + s_train[, 2] + I(s_train[, 1]^2) + I(s_train[, 2]^2)) nd <- data.frame(s_train = I(s_new)) as.numeric(cbind(1, s_new[, 1], s_new[, 2], s_new[, 1]^2, s_new[, 2]^2) %*% coef(fit)) } out <- scp_geostatistical(s_train, y_train, s0, pred_fun, alpha = 0.1, seed = 1) print(out)set.seed(42) n <- 200 s <- matrix(runif(2 * n), ncol = 2) f <- function(s) sin(4 * s[, 1]) + cos(3 * s[, 2]) y <- f(s) + rnorm(n, sd = 0.3) idx <- sample(n, 150) s_train <- s[idx, ]; y_train <- y[idx] s0 <- s[-idx, ][1:10, ] pred_fun <- function(s_train, y_train, s_new) { fit <- lm(y_train ~ s_train[, 1] + s_train[, 2] + I(s_train[, 1]^2) + I(s_train[, 2]^2)) nd <- data.frame(s_train = I(s_new)) as.numeric(cbind(1, s_new[, 1], s_new[, 2], s_new[, 1]^2, s_new[, 2]^2) %*% coef(fit)) } out <- scp_geostatistical(s_train, y_train, s0, pred_fun, alpha = 0.1, seed = 1) print(out)
Computes Gaussian-kernel spatial weights between a target location and a set of reference locations.
spatial_kernel_weights( s0, s, bandwidth = NULL, t0 = NULL, t = NULL, temporal_bandwidth = NULL )spatial_kernel_weights( s0, s, bandwidth = NULL, t0 = NULL, t = NULL, temporal_bandwidth = NULL )
s0 |
A numeric vector (or 1-row matrix) giving the coordinates of the target (prediction) location. |
s |
A numeric matrix of coordinates (one row per observation) for the reference (calibration) set. |
bandwidth |
Positive numeric bandwidth of the Gaussian kernel. If
|
t0 |
Optional numeric scalar: time index of the target observation,
for spatio-temporal weighting. If supplied, |
t |
Optional numeric vector: time indices for the reference set,
same length as |
temporal_bandwidth |
Positive numeric bandwidth for the temporal
kernel; only used if |
Weights are computed as
for the spatial-only case, and multiplied by an analogous temporal
kernel term when t0/t are provided. This is the core
device used to relax the exchangeability assumption required by
standard conformal prediction: observations located near the point to
be predicted contribute more to the calibration of the prediction
interval than distant ones (Mao, Martin and Reich, 2020).
A numeric vector of weights (not necessarily summing to one),
one per row of s, giving higher weight to reference points that
are spatially (and, if requested, temporally) closer to s0.
set.seed(1) s <- matrix(runif(20), ncol = 2) s0 <- c(0.5, 0.5) w <- spatial_kernel_weights(s0, s) round(w, 3)set.seed(1) s <- matrix(runif(20), ncol = 2) s0 <- c(0.5, 0.5) w <- spatial_kernel_weights(s0, s) round(w, 3)
Computes and displays summary statistics of the prediction interval widths
generated by an spconform object.
## S3 method for class 'spconform' summary(object, ...)## S3 method for class 'spconform' summary(object, ...)
object |
An object of class |
... |
Further arguments passed to or from other methods. |
Invisibly returns the input object object.