fastrda is a high-performance implementation of Redundancy Analysis (RDA) written in C++ with Armadillo and OpenMP. It is designed for large-scale ecological, genomic, and other multivariate datasets where computational efficiency is critical.
Key Features:
anova_fastrda() and the S3 anova() method, supporting both overall and axis-wise tests.print(), summary(), predict(), scores(), plot(), and biplot())."minimal", "compact", "full", or "none" to balance memory and functionality.biplotrda() or S3 generics plot() / biplot().# Install from CRAN (when available)
install.packages("fastrda")
# Or install development version from GitHub
# devtools::install_github("zcebeci/fastrda")System Requirements:
To demonstrate fastrda, we will use the classic mite (oribatid mite) ecological dataset from the vegan package.
Load the package:
library(fastrda)Prepare the data:
# Load real-world ecological data: Mite dataset
if (!requireNamespace("vegan", quietly = TRUE)) {
stop("The 'vegan' package is required to run this example.")
}
data(mite, package = "vegan")
data(mite.env, package = "vegan")
# Apply Hellinger standardization to species abundance data
Y <- vegan::decostand(mite, "hellinger")
# Select environmental variables (e.g., Substrate Density and Water Content)
# The intercept is removed because RDA expects only explanatory variables.
X <- model.matrix(~ SubsDens + WatrCont, mite.env)[, -1]Fit a standard RDA model using fastrda:
fit <- fastrda(
genotype = Y,
environment = X,
axes = 2,
scaling = 2,
keep_workspace = "minimal",
threads = 1, # Increase to use multiple CPU cores
verbose = TRUE
)Inspect the results using S3 methods:
# Inspect the components of model fit
names(fit)
#> [1] "raw_site_scores" "site_scores" "species_scores"
#> [4] "scaling_constants" "loadings" "coefficients"
#> [7] "lc_coefficients_raw" "eigenvalues" "prop_total"
#> [10] "prop_constrained" "total_inertia" "conditioned_inertia"
#> [13] "constrained_inertia" "unconstrained_inertia" "R2"
#> [16] "adj_R2" "rank" "n_axes"
#> [19] "n_samples" "df_model" "pseudo_F"
#> [22] "df_resid" "df_cond" "X_rcond"
#> [25] "X_rank_deficient" "Z_rcond" "Z_rank_deficient"
#> [28] "fitted" "scaling" "X_center"
#> [31] "X_scale" "Y_center" "Y_scale"
#> [34] "Z_center" "Z_scale" "center"
#> [37] "scale_x" "scale_y" "workspace"
#> [40] "call" "env_names" "species_names"
#> [43] "site_names"
# Print concise model output
print(fit)
#>
#> Call:
#> fastrda(genotype = Y, environment = X, axes = 2, scaling = 2,
#> keep_workspace = "minimal", threads = 1, verbose = TRUE)
#>
#> Inertia Proportion Rank Df
#> Total 35.000 1.0000 NA 69
#> Constrained 8.477 0.2422 2 2
#> Unconstrained 26.523 0.7578 NA 67
#>
#> Eigenvalues for constrained axes:
#> [,1]
#> [1,] 7.3817
#> [2,] 1.0953
#> attr(,"names")
#> [1] "RDA1" "RDA2"
#>
#> Pseudo-F : 10.7070
#> Adjusted R2 : 0.2196
# Summary of ordination model
summary(fit)
#>
#> Call:
#> fastrda(genotype = Y, environment = X, axes = 2, scaling = 2,
#> keep_workspace = "minimal", threads = 1, verbose = TRUE)
#>
#> Inertia Proportion Rank Df
#> Total 35.000 1.0000 NA 69
#> Constrained 8.477 0.2422 2 2
#> Unconstrained 26.523 0.7578 NA 67
#>
#> Eigenvalues for constrained axes:
#> [,1]
#> [1,] 7.3817
#> [2,] 1.0953
#> attr(,"names")
#> [1] "RDA1" "RDA2"
#>
#> Pseudo-F : 10.7070
#> Adjusted R2 : 0.2196
# Eigenvalues
fit$eigenvalues
#> [,1]
#> [1,] 7.381739
#> [2,] 1.095284
#> attr(,"names")
#> [1] "RDA1" "RDA2"
# R-squared values
fit$R2
#> [1] 0.2422007
fit$adj_R2
#> [1] 0.2195798
# Pre-computed site scores (samples)
head(fit$site_scores)
#> RDA1 RDA2
#> 1 0.3758482 0.06010833
#> 2 0.4178976 -1.06204778
#> 3 0.4903470 -0.41542980
#> 4 0.6376417 -0.54602140
#> 5 0.7254019 1.22438141
#> 6 1.2759345 -1.10787286
# Pre-computed species scores (response variables)
head(fit$species_scores)
#> RDA1 RDA2
#> Brachy 0.1950925 0.191384927
#> PHTH 0.7445179 0.008595452
#> HPAV -0.1803905 0.307530506
#> RARD 0.6724093 0.092191148
#> SSTR 0.4388376 -0.369165090
#> Protopl 0.4654368 0.000886920
# Canonical loadings (correlations)
head(fit$loadings)
#> RDA1 RDA2
#> Brachy 0.06059908 0.1543295346
#> PHTH 0.23126000 0.0069312260
#> HPAV -0.05603237 0.2479873453
#> RARD 0.20886184 0.0743413663
#> SSTR 0.13631046 -0.2976884206
#> Protopl 0.14457262 0.0007151972fastrda offers four workspace modes to balance memory usage and functionality:
| Mode | Stores | Recommended use |
|---|---|---|
"minimal" |
Q and QtY | Permutation testing (recommended default) |
"compact" |
QtY and R | Prediction for new data |
"full" |
X, Q, QtY, Y_res, R | Prediction and permutation testing |
"none" |
Nothing | Lowest memory usage (no permutation testing) |
# Minimal workspace (default) - supports permutation tests
fit_min <- fastrda(Y, X, keep_workspace = "minimal", verbose = FALSE)
# Compact workspace - supports prediction with newdata
fit_compact <- fastrda(Y, X, keep_workspace = "compact", verbose = FALSE)
# Full workspace - supports everything
fit_full <- fastrda(Y, X, keep_workspace = "full", verbose = FALSE)
# No workspace - fastest, no permutation tests
fit_none <- fastrda(Y, X, keep_workspace = "none", verbose = FALSE)Often you want to remove the effect of known covariates (e.g., spatial structure, microhabitat type like Shrub) before testing environmental variables.
# Define conditioning matrix Z (e.g., Shrub presence/absence or type)
Z <- model.matrix(~ Shrub, mite.env)[, -1]
# Partial RDA: Y ~ SubsDens + WatrCont | Shrub
fit_partial <- fastrda(
genotype = Y,
environment = X,
covariates = Z,
axes = 2,
scaling = 2,
keep_workspace = "minimal",
threads = 1,
verbose = FALSE
)
print(fit_partial)
#>
#> Call:
#> fastrda(genotype = Y, environment = X, covariates = Z, axes = 2,
#> scaling = 2, keep_workspace = "minimal", threads = 1, verbose = FALSE)
#>
#> Inertia Proportion Rank Df
#> Total 35.0000 1.0000 NA 69
#> Conditioned 5.5976 0.1599 2 2
#> Constrained 4.4394 0.1268 2 2
#> Unconstrained 24.9629 0.7132 NA 65
#>
#> Eigenvalues for constrained axes:
#> [,1]
#> [1,] 3.3923
#> [2,] 1.0472
#> attr(,"names")
#> [1] "RDA1" "RDA2"
#>
#> Pseudo-F : 5.7798
#> Adjusted R2 : 0.1080The conditioned inertia represents the variation explained exclusively by the conditioning variables before fitting the constrained model.
Assess the statistical significance of the constrained model using anova_fastrda() or the S3 generic anova().
# 999 permutations correspond to the minimum attainable p-value of 0.001.
# Larger numbers of permutations provide finer p-value resolution at the expense of longer computation time.
res_overall <- anova(fit, permutations = 999, threads = 1)
print(res_overall)
#>
#> Permutation Test for Redundancy Analysis (fastrda v1.4.4)
#> Model: Constrained Inertia = 8.4770 (R2 = 0.2422)
#> Test Statistic (Pseudo-F): 10.7070
#> Degrees of Freedom: Model = 2, Residuals = 67
#> Permutations: 999
#> Pr(>F) / p-value: 0.0010 ***# Test each constrained axis individually
res_axis <- anova_fastrda(
fit,
by = "axis",
permutations = 999,
threads = 1
)
print(res_axis)
#>
#> Marginal Permutation Test for Redundancy Analysis Axes (fastrda v1.4.4)
#> Permutations: 999
#>
#> Axis Eigenvalue Variance (%) Marginal p Sig
#> RDA1 7.3817 87.08% 0.0010 ***
#> RDA2 1.0953 12.92% 0.0010 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Disclaimer:
#> Axis-wise permutation p-values quantify the extremeness of each constrained eigenvalue relative to its permutation distribution.Axis-wise tests are marginal, not sequential. They should be interpreted descriptively rather than as formal significance tests for axis inclusion. Each canonical axis is evaluated against its own permutation distribution.
The scaling parameter controls how site and species scores are scaled:
| Scaling | Site Scores | Species Scores | Description |
|---|---|---|---|
| 0 | Unscaled | Unscaled | Raw scores |
| 1 | s * const |
/ const |
Sites scaled by sqrt(eigenvalue) |
| 2 | * const |
s / const |
Species scaled by sqrt(eigenvalue) (default) |
| 3 | sqrt(s) * const |
sqrt(s) / const |
Symmetric scaling |
Where s = sqrt(eigenvalue) and const = ((n - 1) * total_inertia)^(0.25).
# Compare different scaling options
# Scaling = 2 is the default because it emphasizes species relationships and matches the most
# commonly used scaling in ecological RDA applications.
fit0 <- fastrda(Y, X, scaling = 0, axes = 2, keep_workspace = "none", verbose = FALSE)
fit2 <- fastrda(Y, X, scaling = 2, axes = 2, keep_workspace = "none", verbose = FALSE)
head(fit2$site_scores)
#> RDA1 RDA2
#> 1 0.3758482 0.06010833
#> 2 0.4178976 -1.06204778
#> 3 0.4903470 -0.41542980
#> 4 0.6376417 -0.54602140
#> 5 0.7254019 1.22438141
#> 6 1.2759345 -1.10787286
head(fit2$species_scores)
#> RDA1 RDA2
#> Brachy 0.1950925 0.191384927
#> PHTH 0.7445179 0.008595452
#> HPAV -0.1803905 0.307530506
#> RARD 0.6724093 0.092191148
#> SSTR 0.4388376 -0.369165090
#> Protopl 0.4654368 0.000886920Extract site, species, or environmental scores using the standard S3 scores() method, get_site_scores(), or biplot_scores():
# Extract both site and species scores via S3 generic scores()
sc_both <- scores(fit, display = "both", choices = 1:2)
head(sc_both$sites)
#> RDA1 RDA2
#> 1 0.3758482 0.06010833
#> 2 0.4178976 -1.06204778
#> 3 0.4903470 -0.41542980
#> 4 0.6376417 -0.54602140
#> 5 0.7254019 1.22438141
#> 6 1.2759345 -1.10787286
head(sc_both$species)
#> RDA1 RDA2
#> Brachy 0.1950925 0.191384927
#> PHTH 0.7445179 0.008595452
#> HPAV -0.1803905 0.307530506
#> RARD 0.6724093 0.092191148
#> SSTR 0.4388376 -0.369165090
#> Protopl 0.4654368 0.000886920
# Get site scores with dynamic re-scaling (e.g., scaling = 1)
site_sc1 <- get_site_scores(fit, scaling = 1)
head(site_sc1)
#> RDA1 RDA2
#> 1 1.021155 0.06290687
#> 2 1.135401 -1.11149491
#> 3 1.332241 -0.43477150
#> 4 1.732431 -0.57144322
#> 5 1.970870 1.28138651
#> 6 3.466632 -1.15945353
# Get vegan-compatible species and environmental biplot scores
bp_scores <- biplot_scores(fit, type = "both", scaling = 2)
head(bp_scores$species)
#> RDA1 RDA2
#> Brachy 0.1950925 0.191384927
#> PHTH 0.7445179 0.008595452
#> HPAV -0.1803905 0.307530506
#> RARD 0.6724093 0.092191148
#> SSTR 0.4388376 -0.369165090
#> Protopl 0.4654368 0.000886920
head(bp_scores$environment)
#> RDA1 RDA2
#> SubsDens 0.1989174 -0.14000060
#> WatrCont -0.4103032 -0.02218149Use predict.fastrda() to get linear combination (LC) scores or fitted response values. The columns of newdata must have the same variables, order, and encoding as the environmental matrix used to fit the model.
# In-sample LC scores
lc_scores <- predict(fit, type = "lc")
head(lc_scores)
#> RDA1 RDA2
#> 1 0.3758482 0.06010833
#> 2 0.4178976 -1.06204778
#> 3 0.4903470 -0.41542980
#> 4 0.6376417 -0.54602140
#> 5 0.7254019 1.22438141
#> 6 1.2759345 -1.10787286
# Predict for new environmental data
new_X <- head(X, 10)
new_lc <- predict(fit, newdata = new_X, type = "lc")
head(new_lc)
#> RDA1 RDA2
#> 1 8.482354 0.5225437
#> 2 9.431348 -9.2327702
#> 3 11.066428 -3.6114834
#> 4 14.390656 -4.7467639
#> 5 16.371279 10.6439958
#> 6 28.796007 -9.6311444
# Reconstruct response matrix (type = "response")
pred_resp <- predict(fit, type = "response", rank = 2)
head(pred_resp[, 1:5])
#> Brachy PHTH HPAV RARD SSTR
#> 1 0.2333160 0.06887523 0.2321238 0.06351154 0.019324967
#> 2 0.2048422 0.07041546 0.1887400 0.05790424 0.035560321
#> 3 0.2238497 0.07464611 0.2115936 0.06598406 0.027796892
#> 4 0.2243656 0.08236401 0.2033849 0.07247331 0.032030942
#> 5 0.2734324 0.08809205 0.2684112 0.08904840 0.008954043
#> 6 0.2267085 0.11581156 0.1679666 0.10062199 0.050322724fastrda provides publication-ready ggplot2-based biplots through direct calls to biplotrda() or S3 methods plot() and biplot(). Environmental arrows indicate the direction of increasing values for each explanatory variable, whereas site and species positions summarize their relationships in canonical space.
# Create biplot with all components using S3 plot generic
plot(fit, axes = 1:2, scaling = 2, title = "Mite RDA Biplot (S3 method)")# Create biplot directly
biplotrda(fit, axes = 1:2, scaling = 2, title = "Mite RDA Biplot (Direct Call)")# Sites and environment only
biplotrda(fit,
type = "sites_environment",
site_col = "darkblue",
env_col = "darkred",
title = "Mite RDA - Sites and Environment")# Biplot with labels enabled
biplotrda(fit,
show_ids = TRUE,
max_labels = 20,
title = "Labeled Mite RDA Biplot")Control the number of parallel threads used by C++ routines in fastrda:
# Use all cores (default)
fit <- fastrda(Y, X, threads = parallel::detectCores())
# Use 4 threads
fit <- fastrda(Y, X, threads = 4)Internal benchmarks on synthetic datasets containing up to 10,000 response variables demonstrated median speedups of approximately 100×.
For detailed documentation, argument descriptions, and additional examples for specific functions, you can access the built-in help pages directly in R:
help(package = "fastrda")
?fastrda
?anova.fastrda
?predict.fastrda
?scores.fastrda
?biplotrdafastrda provides a fast, memory-efficient implementation of redundancy analysis for large ecological, genomic, and other multivariate datasets. The package supports partial RDA, permutation testing, prediction, multiple scaling options, and flexible visualization through a consistent S3 interface.
```