The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
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.
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.
# 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.
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.21spconform 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)# 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.79library(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.7spconform 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.
stats; no
heavy spatial-modelling dependencies.print,
summary, plot, coverage_report)
included, plus a full introductory vignette.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")?scp_geostatistical,
?scp_areal,
vignette("spconform-intro", package = "spconform")inst/scripts/ in the package source.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.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.