Skip to content

Repository files navigation

spconform

R-CMD-check License: GPL v3 DOI

Conformal Prediction for Spatially and Spatio-Temporally Dependent Data in R

spconform provides distribution-free prediction intervals with finite-sample coverage properties for spatial and spatio-temporal data. It relaxes the exchangeability assumption of standard conformal prediction by using spatial-distance and graph-neighbourhood kernel weights, offering a unified framework for both geostatistical (point-referenced) and areal (lattice) data structures.


Why spconform?

Standard conformal prediction assumes exchangeable data — an assumption routinely violated in spatial settings where nearby observations are more similar than distant ones. Existing R packages address either purely temporal dependence (conformalForecast, AdaptiveConformal) or i.i.d./exchangeable data (conformalInference, conformalClassification, cfcausal), but none provides a documented, unit-tested, unified solution for spatial data on CRAN.

Feature conformalInference conformalForecast scp (GitHub) geoconformal (Python) spconform
Language R R R Python R
Geostatistical (point-ref.)
Areal (lattice)
Spatio-temporal — (temp. only) ✓ (opt.)
Model-agnostic
Unit-tested / CRAN-ready

spconform is, to our knowledge, the first R package to offer conformal prediction spanning both major spatial data structures with optional spatio-temporal extension.

> Status: spconform has passed R CMD check --as-cran with 0 errors, > 0 warnings, and 0 notes on Windows 11 (R 4.6.1), win-builder (R-devel), > and R-hub v2 (Linux, Windows, macOS, donttest). The package is CRAN-ready > and will be submitted to CRAN as soon as the submission form re-opens. > A permanent, citable snapshot of version 0.1.0 is archived on Zenodo > (DOI above). The accompanying manuscript is currently in preparation > for submission to the Journal of Statistical Software.


Installation

# Install the development version from GitHub
remotes::install_github("amjed-droid/spconform")

# Once accepted on CRAN:
# install.packages("spconform")

**Dependencies: The package imports only stats (base R). Suggested packages ('sp', 'knitr', 'rmarkdown') are used to build and run the vignette; 'mgcv', 'ranger', and 'bmstdr' are only needed to reproduce the extended examples shown in the accompanying paper and are not required for core package functionality.


Quick start

1. Geostatistical data (point-referenced)

library(spconform)
library(sp)

data(meuse)
s <- as.matrix(meuse[, c("x", "y")])
y <- log(meuse$zinc)

# Any user-supplied point predictor
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))
  cbind(1, s_new[, 1], s_new[, 2],
        s_new[, 1]^2, s_new[, 2]^2) %*% coef(fit)
}

# 90% locally weighted conformal intervals
set.seed(123)
idx <- sample(nrow(s), floor(0.7 * nrow(s)))
out <- scp_geostatistical(s[idx, ], y[idx], s[-idx, ], pred_fun, alpha = 0.1)

print(out)
#> <spconform> geostatistical conformal prediction
#> Target coverage: 90.0%
#> Number of prediction points: 47

coverage_report(out, y[-idx])
#> $coverage
#> [1] 0.957
#> $mean_width
#> [1] 2.21

2. Comprehensive Spatial Diagnostics

'spconform' includes a multi-panel diagnostic suite (diagnose()) to evaluate marginal coverage, conditional coverage across spatial strata, boundary effects, and the distribution of nonconformity scores:

Run diagnostics and produce publication-quality multi-panel plot

diag <- diagnose(out, y_true = y[-idx], s_test = s[-idx], plot = TRUE)

View textual diagnostic summary

print(diag) === spconform Diagnostic Report ===

Marginal coverage: Empirical: 0.9574 (nominal: 0.9) Mean width: 2.2105 n = 47 , covered = 45

Conditional coverage by spatial bin: Q1-1: 1.0000 (n=7, width=3.529) Q1-2: 1.0000 (n=6, width=2.975) Q4-4: 0.8889 (n=9, width=1.930)

Boundary effect: Near boundary: 0.9583 (n=24) Far from boundary: 0.9565 (n=23)

3. Areal / lattice data

# Aggregate Meuse to a 6x6 grid (21 occupied cells)
xbreaks <- seq(min(meuse$x), max(meuse$x), length.out = 7)
ybreaks <- seq(min(meuse$y), max(meuse$y), length.out = 7)
meuse$cell_x  <- cut(meuse$x, xbreaks, include.lowest = TRUE, labels = FALSE)
meuse$cell_y  <- cut(meuse$y, ybreaks, include.lowest = TRUE, labels = FALSE)
meuse$cell_id <- (meuse$cell_y - 1) * 6 + meuse$cell_x

agg <- aggregate(log(zinc) ~ cell_id, data = meuse, FUN = mean)
names(agg) <- c("cell_id", "y")
cell_coords <- unique(meuse[, c("cell_id", "cell_x", "cell_y")])
agg <- merge(agg, cell_coords, by = "cell_id")
agg <- agg[order(agg$cell_id), ]

# Build adjacency matrix (Queen contiguity)
n_cells <- nrow(agg)
adj <- matrix(0, n_cells, n_cells)
for (i in 1:n_cells) {
  for (j in 1:n_cells) {
    if (i != j) {
      dx <- abs(agg$cell_x[i] - agg$cell_x[j])
      dy <- abs(agg$cell_y[i] - agg$cell_y[j])
      if (dx <= 1 && dy <= 1) adj[i, j] <- 1
    }
  }
}

# 80% neighbourhood-weighted conformal intervals
out_areal <- scp_areal(agg$y, adjacency = adj, alpha = 0.2, decay = 0.5)
coverage_report(out_areal, agg$y)
#> $coverage
#> [1] 0.81
#> $mean_width
#> [1] 1.79

4. Spatio-temporal data

library(mgcv)
library(bmstdr)

data("nysptime")
df <- nysptime[complete.cases(nysptime[, c("utmx", "utmy", "y8hrmax", "Day", "Month")]), ]
df$day_idx <- ifelse(df$Month == 7, df$Day, 31 + df$Day)

s <- as.matrix(df[, c("utmx", "utmy")])
t <- df$day_idx
s_3d <- cbind(s, t)
y <- df$y8hrmax

# Spatio-temporal GAM predictor
pred_fun_st <- function(s_train, y_train, s_new) {
  train_df <- data.frame(x = s_train[, 1], y = s_train[, 2],
                         day = s_train[, 3], z = y_train)
  fit <- gam(z ~ te(x, y, day, k = c(8, 8, 4)), data = train_df)
  new_df <- data.frame(x = s_new[, 1], y = s_new[, 2], day = s_new[, 3])
  as.numeric(predict(fit, newdata = new_df))
}

set.seed(123)
n <- nrow(s_3d)
train_idx <- sample(n, floor(0.7 * n))
out_st <- scp_geostatistical(
  s_train = s_3d[train_idx, ],
  y_train = y[train_idx],
  s0 = s_3d[-train_idx, ],
  pred_fun = pred_fun_st,
  t_train = t[train_idx],
  t0 = t[-train_idx],
  temporal_bandwidth = 5,
  alpha = 0.1,
  split = 0.5
)

coverage_report(out_st, y[-train_idx])
#> $coverage
#> [1] 0.909
#> $mean_width
#> [1] 41.7

Quality Assurance

spconform has been rigorously tested across all major platforms to ensure CRAN-readiness:

Platform R Version Status
Linux (Ubuntu) R-devel ✅ Pass
macOS (Sequoia) R-devel ✅ Pass
Windows R-devel ✅ Pass

The package passes R CMD check --as-cran with 0 errors, 0 warnings, and 0 notes on all tested platforms. Continuous integration is monitored via GitHub Actions.


Key features

  • Model-agnostic: Works with any user-supplied point predictor (kriging, GAM, random forest, linear model, ...).
  • Finite-sample coverage: Maintains coverage close to nominal level regardless of predictor misspecification (under local exchangeability).
  • Lightweight: Imports only stats; no heavy spatial-modelling dependencies.
  • Fully documented: S3 methods (print, summary, plot, coverage_report) included, plus a full introductory vignette.

Citation

If you use spconform in your research, please cite:

Jabbar, A. S. (2026). spconform: Conformal Prediction for Spatially and Spatio-Temporally Dependent Data in R (Version 0.1.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.21862025

A companion manuscript describing the package methodology is currently submitted to the Journal of Statistical Software and will be cited here upon acceptance.

citation("spconform")

Getting help

  • Bug reports & feature requests: GitHub Issues
  • Documentation: ?scp_geostatistical, ?scp_areal, vignette("spconform-intro", package = "spconform")
  • Reproducible scripts: See inst/scripts/ in the package source.

License

This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

About

Distribution-free conformal prediction intervals for spatial and spatio-temporal data in R

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages