
fastsae is a high-performance R package for Small Area Estimation (SAE). It re-engineers classic and modern SAE models using C++ (via Rcpp and RcppArmadillo) and OpenMP multi-threading, providing massive speedups (up to 10,000x+) and radical memory reductions (up to 20,000x) compared to existing packages like sae and emdi.
Most importantly, fastsae produces exact numerical equivalence with the gold-standard implementations in the sae package (Molina & Rao), ensuring that your statistical conclusions remain 100% faithful to the published literature while executing in a fraction of a second.
Documentation : https://ridsonap.github.io/fastsae/
eblup_sfh) automatically handles domains with
missing responses (y = NA) via full-spatial synthetic
(kriging) prediction.summary(), coef(),
fitted(), residuals(), and
autoplot().| Model | Function | Random Effect Structure | MSE Estimation Methods |
|---|---|---|---|
| Fay-Herriot (Area-level) | eblup_fh() |
Independent area effects (\(u_d \sim N(0, \sigma_u^2)\)) | Analytical (Prasad-Rao) |
| Spatial Fay-Herriot | eblup_sfh() |
Simultaneous Autoregressive (SAR(1)) | Analytical, Parametric Bootstrap
(pbmse), Non-Parametric Bootstrap
(npbmse) |
| Spatio-Temporal Fay-Herriot | eblup_stfh() |
Spatial SAR(1) + Temporal AR(1) | Parametric Bootstrap
(pbmse) |
| Battese-Harter-Fuller (Unit-level) | eblup_bhf() |
Random intercept nested in domains | Parametric Bootstrap
(pbmse) |
saeAlthough fastsae runs orders of magnitude faster, its statistical estimates are identical to machine precision with the benchmark sae package:
library(fastsae)
library(sae)
# 1. Standard Fay-Herriot Model
mys_df <- as.data.frame(na.omit(mys))
fit_fast <- eblup_fh(y ~ x1 + x2 + x3, vardir = ~vardir, data = mys_df, print_result = FALSE)
fit_sae <- sae::eblupFH(y ~ x1 + x2 + x3, vardir = vardir, data = mys_df)
all.equal(fit_fast$df_eblup$eblup, as.vector(fit_sae$eblup))
# TRUE
all.equal(as.vector(coef(fit_fast)), as.vector(fit_sae$fit$estcoef$beta))
# TRUE
all.equal(fit_fast$random_effect_var, fit_sae$fit$refvar)
# TRUE
# 2. Spatial Fay-Herriot Model (SAR)
W_clean <- mys_proxmat[!is.na(mys$y), !is.na(mys$y)]
sfh_fast <- eblup_sfh(y ~ x1 + x2 + x3, vardir = ~vardir, W = W_clean, data = mys_df, print_result = FALSE)
sfh_sae <- sae::eblupSFH(y ~ x1 + x2 + x3, vardir = vardir, proxmat = W_clean, data = mys_df)
all.equal(sfh_fast$df_eblup$eblup, as.vector(sfh_sae$eblup))
# TRUE
all.equal(sfh_fast$rho, sfh_sae$fit$spatialcorr)
# TRUE
all.equal(sfh_fast$random_effect_var, sfh_sae$fit$refvar)
# TRUE| Feature | fastsae |
sae (Molina & Rao) |
emdi (Kreutzmann et
al.) |
|---|---|---|---|
| Core Computation | C++ (RcppArmadillo) | Pure R | R / lme4 / nlme |
| Multi-Threading | Native OpenMP
(n_threads) |
Single-threaded | Optional foreach/parallel |
| Numerical Consistency | Reference baseline | Baseline | Approximations |
| Unsampled Area Support | Automatic (Spatial Kriging) | Manual subsetting required | Limited |
| Bootstrap Speed | Ultra-Fast (Parallel C++) | Slow (R loops) | Moderate |
| RAM Consumption | Minimal (< 10 MB) | Moderate (~100 MB) | High (~800+ MB) |
| S3 Methods Support | print,
summary, coef, fitted,
residuals, autoplot |
Custom lists | Standard S3 |
Benchmark performed across area sizes ranging from \(n = 30\) to \(n = 1,000\) (with 5 covariates):
| Metric | fastsae |
sae |
emdi |
|---|---|---|---|
| Mean Time (EBLUP FH) | 0.0015 s | 0.291 s | 9.64 s |
| Mean Time (Spatial FH) | 0.165 s | 12.60 s | 8.69 s |
| Mean Time (Spatio Temporal FH) | 12.5 s | 353.0 s | - |
| Peak Memory (EBLUP FH) | 0.055 MB | 16.3 MB | 824 MB |
| Peak Memory (Spatial FH) | 5.15 MB | 408 MB | 824 MB |
| Peak Memory (Spatio Temporal FH) | 0.289 MB | 7822 MB | - |
| Speedup at n = 1,000 | Baseline | ~364x slower | ~12,300x slower |


You can install the development version from GitHub:
# install.packages("remotes")
remotes::install_github("ridsonap/fastsae")
# or cran version
install.packages('fastsae')eblup_fh)library(fastsae)
# Fit area-level Fay-Herriot with REML
fit_fh <- eblup_fh(
y ~ x1 + x2 + x3,
vardir = ~vardir,
data = na.omit(mys),
method = "REML"
)
# View estimates and regression coefficients
summary(fit_fh)
coef(fit_fh)
head(fitted(fit_fh))eblup_sfh)When domains have geographic proximity, eblup_sfh
incorporates a spatial weight matrix \(W\) and supports multi-threaded Parametric
Bootstrap MSE:
# Fit Spatial Fay-Herriot with 4 OpenMP threads and Parametric Bootstrap MSE
fit_sfh <- eblup_sfh(
y ~ x1 + x2 + x3,
vardir = ~vardir,
data = mys,
W = mys_proxmat,
mse_method = "pbmse",
B = 200,
n_threads = 4,
seed = 123
)
# Unsampled domains (y = NA) are automatically predicted via spatial kriging!
head(fit_sfh$df_eblup)eblup_stfh)For panel data observed over multiple time periods,
eblup_stfh models simultaneous spatial correlation (SAR)
and temporal autoregression (AR(1)):
# Prepare panel data
panel_data <- mys_panel[!is.na(mys_panel$y) & mys_panel$year >= 2024, ]
W_sub <- mys_proxmat[-c(21, 25), -c(21, 25)]
fit_stfh <- eblup_stfh(
y ~ x1 + x2 + x3,
data = panel_data,
vardir = ~vardir,
domain = ~area,
time = ~year,
W = W_sub,
model = "ST",
compute_mse = TRUE,
B = 100,
seed = 42
)
head(fit_stfh$df_eblup)eblup_bhf)For survey datasets containing individual/unit observations:
# Prepare population auxiliary means
df_pop <- cornsoybeanmeans
names(df_pop)[names(df_pop) == "MeanCornPixPerSeg"] <- "CornPix"
names(df_pop)[names(df_pop) == "MeanSoyBeansPixPerSeg"] <- "SoyBeansPix"
names(df_pop)[names(df_pop) == "CountyIndex"] <- "County"
fit_bhf <- eblup_bhf(
CornHec ~ CornPix + SoyBeansPix,
unit_data = cornsoybean,
Xpop = df_pop,
domain_var = "County",
popsize_var = "PopnSegments",
compute_mse = TRUE,
B = 50,
seed = 123
)
summary(fit_bhf)
head(fit_bhf$df_eblup)autoplot)fastsae provides convenient ggplot2-based diagnostic and
comparison visualizations via autoplot():
# 1. EBLUP estimates vs direct survey estimates with 45Β° reference line
autoplot(fit_fh, type = "estimates")
# 2. Mean Squared Error (MSE) comparison across domains
autoplot(fit_fh, type = "mse")
# 3. Multi-model comparison across domains (e.g., FH vs Spatial FH)
autoplot(list("FH" = fit_fh, "Spatial FH" = fit_sfh), type = "comparison")