3 Random Matrix Theory (RMT)–based random network
When a numerical matrix is used as input, it is necessary to filter and screen the resulting correlation matrix. Empirical thresholds are commonly adopted, usually between 0.6 to 0.85; however, these thresholds are largely subjective.
Compared with empirical thresholding, random matrix theory (RMT) provides a data-driven and objective approach for determining correlation thresholds, effectively separating true biological signals from random noise and improving the robustness and comparability of ecological networks.
3.2 Example data
Example data
# Access built-in example datasets in ggNetView
# Relative abundance table of rarefied ASVs or OTUs
data("otu_rare_relative")
dim(otu_rare_relative)## [1] 2859 18
otu_rare_relative[1:5, 1:5]## KO1 KO2 KO3 KO4 KO5
## ASV_1 0.03306667 0.05453333 0.02013333 0.03613333 0.02686667
## ASV_2 0.05750000 0.03393333 0.06046667 0.05810000 0.07320000
## ASV_3 0.01733333 0.01296667 0.02290000 0.02336667 0.03106667
## ASV_4 0.04266667 0.01093333 0.01416667 0.01933333 0.03346667
## ASV_6 0.02646667 0.01856667 0.02110000 0.02353333 0.03806667
## [1] 2859 8
tax_tab[1:5, 1:5]## # A tibble: 5 × 5
## OTUID Kingdom Phylum Class Order
## <chr> <chr> <chr> <chr> <chr>
## 1 ASV_2 Archaea Thaumarchaeota Unassigned Nitrososphaerales
## 2 ASV_3 Bacteria Verrucomicrobia Spartobacteria Unassigned
## 3 ASV_31 Bacteria Actinobacteria Actinobacteria Actinomycetales
## 4 ASV_27 Archaea Thaumarchaeota Unassigned Nitrososphaerales
## 5 ASV_9 Bacteria Unassigned Unassigned Unassigned
3.3 Compute RMT threshold
# ---- RMT threshold scan #1: WGCNA correlation backend ----
# `ggNetView_RMT()` scans a series of candidate |r| thresholds, builds the
# thresholded correlation matrix at each, computes its eigenvalue spacing
# distribution (NNSD), and picks the threshold at which the NNSD transitions
# from Gaussian Orthogonal Ensemble (signal + noise) to Poisson (noise-free,
# modular network) — this is the RMT-recommended cutoff.
out <- ggNetView_RMT(
mat = otu_rare_relative, # input matrix (variables x samples)
transfrom.method = "none", # already relative-abundance, no extra transform
method = "WGCNA", # use WGCNA::corAndPvalue for correlation
cor.method = "pearson", # Pearson correlation
unfold.method = "gaussian", # Gaussian KDE unfolding of eigenvalues
bandwidth = "nrd0", # KDE bandwidth rule (stats::density default)
nr.fit.points = 20, # support points (only used if unfold.method = "spline")
discard.outliers = TRUE, # IQR-trim extreme eigenvalues before unfolding
discard.zeros = TRUE, # drop all-zero rows/cols after thresholding
min.mat.dim = 40, # early-stop if effective dim drops below 40
max.ev.spacing = 3, # truncate NNSD tail at spacing = 3
save_plots = FALSE, # don't write diagnostic PNGs to disk
out_dir = "RMT_plots", # (only used when save_plots = TRUE)
verbose = TRUE, # print scan progress to console
seed = 1115 # fix RNG for reproducibility
)## [Info] Matrix dimension: 2859 x 2859
## [Info] #non-zeros: 8173751 | Sparseness: 0.0000
## [Scan] 1/51 | threshold = 0
## Effective dimension: 2859
## [Scan] 2/51 | threshold = 0.02
## Effective dimension: 2859
## [Scan] 3/51 | threshold = 0.04
## Effective dimension: 2859
## [Scan] 4/51 | threshold = 0.06
## Effective dimension: 2859
## [Scan] 5/51 | threshold = 0.08
## Effective dimension: 2859
## [Scan] 6/51 | threshold = 0.1
## Effective dimension: 2859
## [Scan] 7/51 | threshold = 0.12
## Effective dimension: 2859
## [Scan] 8/51 | threshold = 0.14
## Effective dimension: 2859
## [Scan] 9/51 | threshold = 0.16
## Effective dimension: 2859
## [Scan] 10/51 | threshold = 0.18
## Effective dimension: 2859
## [Scan] 11/51 | threshold = 0.2
## Effective dimension: 2859
## [Scan] 12/51 | threshold = 0.22
## Effective dimension: 2859
## [Scan] 13/51 | threshold = 0.24
## Effective dimension: 2859
## [Scan] 14/51 | threshold = 0.26
## Effective dimension: 2859
## [Scan] 15/51 | threshold = 0.28
## Effective dimension: 2859
## [Scan] 16/51 | threshold = 0.3
## Effective dimension: 2859
## [Scan] 17/51 | threshold = 0.32
## Effective dimension: 2859
## [Scan] 18/51 | threshold = 0.34
## Effective dimension: 2859
## [Scan] 19/51 | threshold = 0.36
## Effective dimension: 2859
## [Scan] 20/51 | threshold = 0.38
## Effective dimension: 2859
## [Scan] 21/51 | threshold = 0.4
## Effective dimension: 2859
## [Scan] 22/51 | threshold = 0.42
## Effective dimension: 2859
## [Scan] 23/51 | threshold = 0.44
## Effective dimension: 2859
## [Scan] 24/51 | threshold = 0.46
## Effective dimension: 2859
## [Scan] 25/51 | threshold = 0.48
## Effective dimension: 2859
## [Scan] 26/51 | threshold = 0.5
## Effective dimension: 2859
## [Scan] 27/51 | threshold = 0.52
## Effective dimension: 2859
## [Scan] 28/51 | threshold = 0.54
## Effective dimension: 2859
## [Scan] 29/51 | threshold = 0.56
## Effective dimension: 2859
## [Scan] 30/51 | threshold = 0.58
## Effective dimension: 2859
## [Scan] 31/51 | threshold = 0.6
## Effective dimension: 2859
## [Scan] 32/51 | threshold = 0.62
## Effective dimension: 2859
## [Scan] 33/51 | threshold = 0.64
## Effective dimension: 2857
## [Scan] 34/51 | threshold = 0.66
## Effective dimension: 2855
## [Scan] 35/51 | threshold = 0.68
## Effective dimension: 2842
## [Scan] 36/51 | threshold = 0.7
## Effective dimension: 2800
## [Scan] 37/51 | threshold = 0.72
## Effective dimension: 2705
## [Scan] 38/51 | threshold = 0.74
## Effective dimension: 2546
## [Scan] 39/51 | threshold = 0.76
## Effective dimension: 2333
## [Scan] 40/51 | threshold = 0.78
## Effective dimension: 2049
## [Scan] 41/51 | threshold = 0.8
## Effective dimension: 1735
## [Scan] 42/51 | threshold = 0.82
## Effective dimension: 1396
## [Scan] 43/51 | threshold = 0.84
## Effective dimension: 1082
## [Scan] 44/51 | threshold = 0.86
## Effective dimension: 825
## [Scan] 45/51 | threshold = 0.88
## Effective dimension: 600
## [Scan] 46/51 | threshold = 0.9
## Effective dimension: 454
## [Scan] 47/51 | threshold = 0.92
## Effective dimension: 324
## [Scan] 48/51 | threshold = 0.94
## Effective dimension: 215
## [Scan] 49/51 | threshold = 0.96
## Effective dimension: 157
## [Scan] 50/51 | threshold = 0.98
## Effective dimension: 79
## [Scan] 51/51 | threshold = 1
## Effective dimension: 2
## Too small. Stop scanning.
# The chosen threshold to feed downstream into build_graph_from_mat(r.threshold = ...)
out$chosen_threshold## [1] 0.82
# ---- RMT threshold scan #2: plain `cor()` backend for comparison ----
# Same parameters as `out` above, but with `method = "cor"` (base stats::cor /
# psych::corr.test) instead of WGCNA. Useful as a sanity check — the chosen
# threshold should be very close across the two backends
out2 <- ggNetView_RMT(
mat = otu_rare_relative,
transfrom.method = "none",
method = "cor", # <-- only difference vs. `out`
cor.method = "pearson",
unfold.method = "gaussian",
bandwidth = "nrd0",
nr.fit.points = 20,
discard.outliers = TRUE,
discard.zeros = TRUE,
min.mat.dim = 40,
max.ev.spacing = 3,
save_plots = FALSE,
out_dir = "RMT_plots",
verbose = TRUE,
seed = 1115
)## [Info] Matrix dimension: 2859 x 2859
## [Info] #non-zeros: 8173761 | Sparseness: 0.0000
## [Scan] 1/51 | threshold = 0
## Effective dimension: 2859
## [Scan] 2/51 | threshold = 0.02
## Effective dimension: 2859
## [Scan] 3/51 | threshold = 0.04
## Effective dimension: 2859
## [Scan] 4/51 | threshold = 0.06
## Effective dimension: 2859
## [Scan] 5/51 | threshold = 0.08
## Effective dimension: 2859
## [Scan] 6/51 | threshold = 0.1
## Effective dimension: 2859
## [Scan] 7/51 | threshold = 0.12
## Effective dimension: 2859
## [Scan] 8/51 | threshold = 0.14
## Effective dimension: 2859
## [Scan] 9/51 | threshold = 0.16
## Effective dimension: 2859
## [Scan] 10/51 | threshold = 0.18
## Effective dimension: 2859
## [Scan] 11/51 | threshold = 0.2
## Effective dimension: 2859
## [Scan] 12/51 | threshold = 0.22
## Effective dimension: 2859
## [Scan] 13/51 | threshold = 0.24
## Effective dimension: 2859
## [Scan] 14/51 | threshold = 0.26
## Effective dimension: 2859
## [Scan] 15/51 | threshold = 0.28
## Effective dimension: 2859
## [Scan] 16/51 | threshold = 0.3
## Effective dimension: 2859
## [Scan] 17/51 | threshold = 0.32
## Effective dimension: 2859
## [Scan] 18/51 | threshold = 0.34
## Effective dimension: 2859
## [Scan] 19/51 | threshold = 0.36
## Effective dimension: 2859
## [Scan] 20/51 | threshold = 0.38
## Effective dimension: 2859
## [Scan] 21/51 | threshold = 0.4
## Effective dimension: 2859
## [Scan] 22/51 | threshold = 0.42
## Effective dimension: 2859
## [Scan] 23/51 | threshold = 0.44
## Effective dimension: 2859
## [Scan] 24/51 | threshold = 0.46
## Effective dimension: 2859
## [Scan] 25/51 | threshold = 0.48
## Effective dimension: 2859
## [Scan] 26/51 | threshold = 0.5
## Effective dimension: 2859
## [Scan] 27/51 | threshold = 0.52
## Effective dimension: 2859
## [Scan] 28/51 | threshold = 0.54
## Effective dimension: 2859
## [Scan] 29/51 | threshold = 0.56
## Effective dimension: 2859
## [Scan] 30/51 | threshold = 0.58
## Effective dimension: 2859
## [Scan] 31/51 | threshold = 0.6
## Effective dimension: 2859
## [Scan] 32/51 | threshold = 0.62
## Effective dimension: 2859
## [Scan] 33/51 | threshold = 0.64
## Effective dimension: 2857
## [Scan] 34/51 | threshold = 0.66
## Effective dimension: 2855
## [Scan] 35/51 | threshold = 0.68
## Effective dimension: 2842
## [Scan] 36/51 | threshold = 0.7
## Effective dimension: 2800
## [Scan] 37/51 | threshold = 0.72
## Effective dimension: 2705
## [Scan] 38/51 | threshold = 0.74
## Effective dimension: 2546
## [Scan] 39/51 | threshold = 0.76
## Effective dimension: 2333
## [Scan] 40/51 | threshold = 0.78
## Effective dimension: 2049
## [Scan] 41/51 | threshold = 0.8
## Effective dimension: 1735
## [Scan] 42/51 | threshold = 0.82
## Effective dimension: 1396
## [Scan] 43/51 | threshold = 0.84
## Effective dimension: 1082
## [Scan] 44/51 | threshold = 0.86
## Effective dimension: 825
## [Scan] 45/51 | threshold = 0.88
## Effective dimension: 600
## [Scan] 46/51 | threshold = 0.9
## Effective dimension: 454
## [Scan] 47/51 | threshold = 0.92
## Effective dimension: 324
## [Scan] 48/51 | threshold = 0.94
## Effective dimension: 215
## [Scan] 49/51 | threshold = 0.96
## Effective dimension: 157
## [Scan] 50/51 | threshold = 0.98
## Effective dimension: 79
## [Scan] 51/51 | threshold = 1
## Effective dimension: 10
## Too small. Stop scanning.
# Compare with out$chosen_threshold — they should be in the same neighborhood.
out2$chosen_threshold## [1] 0.82
3.4 Build graph from matrix based on RMT-threshold
Build graph object
# ---- Construct the tidygraph network using the RMT-selected r threshold ----
# Replaces the usual subjective r-cutoff (e.g. 0.6–0.85) with the data-driven
# value `out$chosen_threshold` returned above.
graph_obj <- build_graph_from_mat(
mat = otu_rare_relative, # variable x sample matrix
transfrom.method = "none", # match the transform used for RMT scan
r.threshold = out$chosen_threshold, # RMT-derived |r| cutoff (objective)
p.threshold = 0.05, # significance cutoff on adjusted p-values
method = "WGCNA", # correlation backend (must match RMT scan)
cor.method = "pearson", # correlation type
proc = "bonferroni", # multiple-testing correction for p-values
module.method = "Fast_greedy", # community detection algorithm
node_annotation = tax_tab, # taxonomy joined onto each node (by name)
top_modules = 15, # keep top-15 modules; smaller ones -> "Others"
seed = 1115 # reproducibility
)
# Print to inspect node/edge counts and the columns now available on nodes
# (modularity, modularity2, modularity3, Modularity, Degree, Strength + taxonomy)
graph_obj## # A tbl_graph: 213 nodes and 844 edges
## #
## # An undirected simple graph with 29 components
## #
## # Node Data: 213 × 14 (active)
## name modularity modularity2 modularity3 Modularity Degree Strength Kingdom
## <chr> <fct> <ord> <chr> <ord> <dbl> <dbl> <chr>
## 1 ASV_649 5 5 5 5 27 26.5 Bacter…
## 2 ASV_705 5 5 5 5 27 26.5 Bacter…
## 3 ASV_12… 5 5 5 5 27 26.5 Bacter…
## 4 ASV_13… 5 5 5 5 27 26.5 Bacter…
## 5 ASV_14… 5 5 5 5 27 26.5 Bacter…
## 6 ASV_14… 5 5 5 5 27 26.5 Bacter…
## 7 ASV_24… 5 5 5 5 27 26.5 Bacter…
## 8 ASV_25… 5 5 5 5 27 26.4 Bacter…
## 9 ASV_28… 5 5 5 5 27 26.5 Bacter…
## 10 ASV_28… 5 5 5 5 27 26.5 Bacter…
## # ℹ 203 more rows
## # ℹ 6 more variables: Phylum <chr>, Class <chr>, Order <chr>, Family <chr>,
## # Genus <chr>, Species <chr>
## #
## # Edge Data: 844 × 5
## from to weight correlation corr_direction
## <int> <int> <dbl> <dbl> <chr>
## 1 194 195 0.959 0.959 Positive
## 2 185 208 0.954 0.954 Positive
## 3 185 213 0.957 0.957 Positive
## # ℹ 841 more rows