res <- identify_module(
res = res,
method = "louvain",
min_size = 3,
max_size = 100000,
n_perm = 100,
seed = 123,
workers = 4,
node_freq = 0.05,
louvain_resolution = 1
)5 Module Identification
This chapter describes Step 4 of the FeatureMAP pipeline: identifying functional modules from the PCSF-derived subnetwork using identify_module().
Prerequisites: You need a featuremap_result object from Subnetwork Extraction, with stage "subnetwork_extracted".
5.1 What identify_module() Does
identify_module() performs community detection on the extracted subnetwork and then evaluates module significance using permutation testing.
At a high level, this step performs the following operations:
Construct a module detection graph: A metabolite-only subgraph is derived from the PCSF subnetwork. Metabolite nodes with low occurrence frequency across repeated PCSF runs are filtered out using
node_freq. Nodes directly linked to observed features are labelledannotated; the remaining ones are labelledhidden.Adjust edge weights: Edge weights are taken from the PCSF edge frequency and divided by the square root of the product of the two endpoint degrees in the full MRN, so that highly connected hub metabolites do not dominate the partition.
Detect network communities: One of four community detection algorithms partitions the network into candidate modules, which are then relabelled as compact IDs
"M1","M2", …Prune module boundaries: Within each module, non-annotated terminal nodes are iteratively removed. Nodes dropped this way are kept in the table but flagged with
Use_for_annotation = FALSE, so they no longer influence Step 5.Compute module activity scores: Module scores summarize the statistical signal from features mapped to metabolites within the module.
Assess statistical significance: A permutation procedure with a gamma null model estimates empirical significance and controls the false discovery rate.
This step converts the extracted metabolic network into functional modules supported by both network topology and feature-level statistical evidence.
5.2 Key Parameters
| Parameter | Default | Description |
|---|---|---|
res |
required | featuremap_result returned by extract_subnetwork() |
method |
"louvain" |
Community detection algorithm |
min_size |
3 |
Minimum module size retained |
max_size |
100000 |
Maximum module size retained |
n_perm |
100 |
Number of permutations for significance testing |
seed |
123 |
Random seed for reproducibility |
workers |
4 |
Parallel workers for permutation testing |
node_freq |
0.05 |
Frequency threshold for retaining metabolite nodes |
louvain_resolution |
1 |
Resolution parameter for Louvain clustering |
walktrap_steps |
4 |
Number of steps used in walktrap clustering |
5.3 Basic Usage
res <- identify_module(
res = res,
method = "leading_eigen",
min_size = 3,
max_size = 100000,
n_perm = 100,
seed = 123,
workers = 4,
node_freq = 0.05
)res <- identify_module(
res = res,
method = "fast_greedy",
min_size = 3,
max_size = 100000,
n_perm = 100,
seed = 123,
workers = 4,
node_freq = 0.05
)res <- identify_module(
res = res,
method = "walktrap",
min_size = 3,
max_size = 100000,
n_perm = 100,
seed = 123,
workers = 4,
node_freq = 0.05,
walktrap_steps = 4
)5.4 Clustering Methods
louvain
A fast modularity optimization algorithm that iteratively merges nodes and communities to maximize network modularity. It is widely used for large biological networks.leading_eigen
A spectral method that partitions the network based on the leading eigenvectors of the modularity matrix. It identifies communities by separating nodes according to their contribution to modularity.fast_greedy
An agglomerative hierarchical clustering method that starts with each node as its own community and progressively merges communities to increase modularity.walktrap
A random-walk-based community detection algorithm that simulates short random walks on the network. The intuition is that random walks tend to remain within densely connected regions.
Different algorithms may produce slightly different module boundaries. Comparing multiple methods can help evaluate module robustness.
5.5 Result Interpretation
After successful module identification:
res$stage
# [1] "module_identified"5.5.1 Module membership table
module_table records the assignment of metabolite nodes to modules, together with node-level metadata:
head(res$module_table[, c(
"Module", "Compound_name", "HMDB_ID", "KEGG_ID",
"Node_freq", "Type", "Importance", "Use_for_annotation"
)])
# Module Compound_name HMDB_ID KEGG_ID Node_freq Type Importance
# 1 M1 Tungstic acid HMDB0259326 C20679 1.00 annotated 0.2267914
# 2 M1 Adenosine HMDB0000050 C00212 0.19 annotated 0.1321317
# 3 M1 Salsolinol HMDB0042012 C09642 0.07 annotated 0.1183787
# 4 M1 ADP HMDB0001341 C00008 0.94 hidden NA
# 5 M1 Adenosine triphosphate HMDB0000538 C00002 1.00 hidden NA
# 6 M1 Guanosine 3',5'-bis(diphosphate) HMDB0059638 C01228 0.75 annotated 0.2105361
# Use_for_annotation
# 1 TRUE
# 2 TRUE
# 3 TRUE
# 4 TRUE
# 5 TRUE
# 6 TRUEEach row represents a metabolite node belonging to an identified module:
| Column | Description |
|---|---|
Module |
Module identifier ("M1", "M2", …) |
INCHI_ID |
InChI of the metabolite node, used as the primary key of the network |
HMDB_ID, KEGG_ID, Compound_name |
Metabolite identity carried over from the MRN |
Node_freq |
Frequency of the node across repeated PCSF runs (robustness) |
Type |
"annotated" if linked to an observed feature, "hidden" otherwise |
Importance |
Feature-derived importance, averaged with weighted_anno_score as weights |
Use_for_annotation |
FALSE for non-annotated terminal nodes pruned from the module boundary; those nodes are excluded from Step 5 |
Nodes with higher Node_freq and Importance values tend to represent more stable and feature-supported metabolites within the network.
Here, annotated metabolites are nodes directly linked to observed LC–MS features through the feature annotation step, whereas hidden metabolites are Steiner nodes introduced during PCSF optimization to connect annotated metabolites within the metabolic reaction network. Hidden metabolites are not directly supported by measured features, but may provide important network context.
5.5.2 Module significance table
module_score summarizes statistics for each detected module:
head(res$module_score)
# Module Module_size Annotated_nodes Module_score p fdr
# 1 M4 73 60 0.3278375 0.01174675 0.4463765
# 2 M12 51 44 0.2184320 0.08448563 0.6701799
# 3 M11 17 16 0.2090107 0.09908058 0.6701799
# 4 M3 123 113 0.1998797 0.11538420 0.6701799
# 5 M13 25 24 0.1797426 0.16015150 0.6701799
# 6 M17 32 31 0.1753071 0.17186291 0.6701799
sum(res$module_score$p < 0.05, na.rm = TRUE)
sum(res$module_score$fdr < 0.05, na.rm = TRUE)
# [1] 1
# [1] 0Important columns include:
Module– module identifier, matchingmodule_tableModule_size– number of metabolite nodes in the moduleAnnotated_nodes– number of nodes supported by feature annotationsModule_score– aggregated activity score of the modulep– permutation-based p-valuefdr– multiple-testing corrected p-value
Modules with lower p or fdr values indicate stronger statistical support and are typically prioritized for downstream biological interpretation.
What this looks like on the demo data. With the default settings, the demo dataset returns one module with p < 0.05 and no module with fdr < 0.05. This is expected for a small demonstration dataset: the permutation p-values are corrected across all detected modules, so the FDR becomes conservative when only one module carries a strong signal.
Because of this, the demo examples in Function Annotation filter modules on the raw permutation p-value (p = "p") instead of the default "fdr". For a real study, keep "fdr" whenever enough modules pass it, and treat p-value-only modules as exploratory findings that need independent support.
If too many tiny modules appear, increase min_size or tighten node_freq. If modules are too coarse, try alternative methods, or lower louvain_resolution for broader and raise it for finer communities.
5.6 Next Step
Once modules are identified, continue to Function Annotation to interpret significant modules in biological terms.