Implements the distance-adjusted propensity score with interference (iDAPS) and recoverU+ methods for estimating the average treatment effect on the treated (ATT) from spatial observational data in the presence of both spatial confounding and spatial interference. iDAPS matches units on a data-driven composite of propensity-score distance, spatial proximity and neighbourhood-exposure distance. recoverU+ is a doubly robust estimator that augments the propensity-score and control-outcome models with a partially recovered spatial confounder and a neighbourhood-exposure term. The package also provides the naive propensity score, DAPS and recoverU comparators, a simulator for the spatial confounding/interference data-generating process and spatial inference tools.
spaci — SPAtial Causal Inference under confounding and interference.
spaci implements two unified methods for estimating the average treatment
effect on the treated (ATT) from spatial observational data when spatial
confounding (SC) and spatial interference (SI) occur together — a setting
where existing methods that address only one of the two are biased:
idaps()) — distance-adjusted propensity score with
interference. A matching estimator that matches treated to control units on a
data-driven composite of three normalised distances,
D = π₁·DPS + π₂·DSpatial + π₃·DInterference.
The weights are chosen by minimising a covariate/spatial/exposure balance
score, not tuned by hand. Setting π₃ = 0 recovers DAPS and π₂ = π₃ = 0
recovers naive PS.recoverUplus()) — a doubly robust estimator whose
propensity score and control outcome models are augmented with a partially
recovered spatial confounder U_R(s) (recovered from the residual Matérn
field by GLS) and a neighbourhood-exposure term, so that SC and SI are
adjusted for simultaneously.The naive PS (naive_ps()), DAPS (daps()) and recoverU (recoverU())
comparators, a data simulator (simulate_spatial_causal()) and an all-methods
wrapper (spatial_ate()) are also provided.
This package accompanies the report "A unified framework for estimating direct causal effect under spatial confounding and interference, with the R package spaci" (Ogunsola, I. and Johnson, O. 2026).
The released version is on CRAN:
install.packages("spaci")
The development version, which adds the spatial inference tools (vcov_hac(),
boot_spatial(), rand_test()) and bias_bound(), can be installed from GitHub:
# install.packages("devtools")
devtools::install_github("Ogunsolaia/spaci")
spaci depends only on base R and stats. The recoverU family recovers the
spatial confounder via a self-contained Matérn maximum-likelihood fit; to
reproduce the report exactly with geoR, install geoR and pass
matern_method = "geoR".
library(spaci)
## 1. Simulate data with BOTH spatial confounding and interference (true ATT = 2)
sim <- simulate_spatial_causal(n = 250, seed = 1)
## sim$Y outcome sim$X covariate matrix (X1, X2)
## sim$Z 0/1 treatment sim$coords facility coordinates
## 2. Run every estimator at once (seed fixes the randomised matching order)
res <- spatial_ate(sim$Y, sim$Z, sim$X, sim$coords, tau = 0.1, seed = 1)
res
#> Method ATT SE Lower Upper
#> 1 Naive PS ... <- ignores SC and SI, most biased
#> 2 DAPS ...
#> 3 iDAPS ... <- adjusts for both
#> 4 recoverU ...
#> 5 recoverU+ ... <- closest to the true ATT
## 3. Visualise the comparison (forest plot, Figure 2.5 style)
plot_ate(res, true_att = sim$true_att)
## 4. Inspect a single method
fit <- idaps(sim$Y, sim$Z, sim$X, sim$coords, tau = 0.1, seed = 1)
fit # ATT, CI and the selected (π₁, π₂, π₃) weights
fit$weights # data-driven composite-distance weights
The forest plot places each method's ATT and confidence interval against the "no effect" line (0) and the true effect:

Supply your outcome, binary treatment, covariates and coordinates directly. The two recommended estimators:
library(spaci)
# Y numeric outcome (length n)
# Z binary treatment 0/1 (length n)
# X covariates (n x p matrix or data frame)
# coords facility/site coordinates (n x 2 matrix: longitude, latitude)
# iDAPS — matching on the composite distance
idaps(Y, Z, X, coords, tau = 0.1, caliper = 0.25, seed = 1)
# recoverU+ — doubly robust with recovered confounder + interference term
recoverUplus(Y, Z, X, coords, tau = 0.1)
# all five methods side by side, then plot
res <- spatial_ate(Y, Z, X, coords, tau = 0.1, seed = s)
plot_ate(res)
tau sets the interference kernel bandwidth (smaller = more local); tune it to
the spatial scale of your interference.
All estimators share the same interface:
| Argument | Description |
|---|---|
Y |
numeric outcome vector |
Z |
binary treatment/exposure vector (0/1) |
X |
covariate matrix or data frame |
coords |
two-column matrix/data frame of spatial coordinates |
tau |
spatial decay (bandwidth) of the exposure kernel |
caliper |
maximum matching distance (matching estimators) |
methods <- c("Naive PS", "DAPS", "iDAPS", "recoverU", "recoverU+")
nsim <- 1000; true <- 2
store <- matrix(NA, nsim, length(methods), dimnames = list(NULL, methods))
for (s in seq_len(nsim)) {
sim <- simulate_spatial_causal(n = 250, delta_u = 2.0, tau_exp = 0.1)
store[s, ] <- spatial_ate(sim$Y, sim$Z, sim$X, sim$coords, seed = s)$ATT
}
data.frame(Method = methods,
Bias = colMeans(store - true, na.rm = TRUE),
MSE = colMeans((store - true)^2, na.rm = TRUE))
MIT © Isqeel Ogunsola, Olatunji Johnson, Thomas House