omics.co.in logo omics.co.in

Chapter 5: Network Construction, Topological Overlap Matrix (TOM) & Dynamic Module Detection

Tested Stacks: GATK 4.6.1 Samtools 1.21 Nextflow 24.10 Seurat v5.1 Python 3.12
Prerequisites: Basic Linux CLI & Sequencing Formats
📚 Chapter 5 of 10 0% Course Completed
Module 05 • TOM & Clustering Track 23 • 10 Modular Chapters WGCNA • DESeq2 • clusterProfiler • Cytoscape

Chapter 5: Network Construction, Topological Overlap Matrix (TOM) & Dynamic Module Detection

Constructing co-expression networks with blockwiseModules; calculating signed Topological Overlap Matrices (TOM); dynamic tree cutting; multi-block handling; converting numeric labels to colors; and merging similar modules at mergeCutHeight = 0.25.

⏱️ Estimated Study: 45–60 mins 🧪 Workflows: R / Bioconductor (WGCNA 1.73+, DESeq2, clusterProfiler, Cytoscape 3.10+) 📋 Level: Intermediate to Advanced Systems Transcriptomics

1. The Mathematical Anatomy of Topological Overlap (TOM)

While the co-expression adjacency matrix $a_{ij}$ measures direct correlation between gene $i$ and gene $j$, direct correlation is notoriously vulnerable to transitive noise (e.g., genes $i$ and $j$ may both correlate with an omnipresent stress signal without sharing any specific biological functional relationship).

To construct robust, biologically stable modules, WGCNA transforms adjacency into the Topological Overlap Measure (TOM):

$$TOM_{ij} = \frac{l_{ij} + a_{ij}}{\min(k_i, k_j) + 1 - a_{ij}}, \quad l_{ij} = \sum_u a_{iu} a_{uj}$$

Here, $l_{ij}$ quantifies the shared neighborhood connectivity between genes $i$ and $j$ across all other network nodes $u$, and $k_i = \sum_u a_{iu}$ is the node connectivity. Two genes achieve a high $TOM_{ij}$ value if and only if they are not only directly co-expressed ($a_{ij}$), but also share a tightly knit cohort of mutual network neighbors ($l_{ij}$).

The topological dissimilarity matrix is then computed as:

$$d_{TOM, ij} = 1 - TOM_{ij}$$

Hierarchical average-linkage clustering is subsequently performed on $d_{TOM}$, and modules are identified using the Dynamic Tree Cut algorithm.

2. One-Step Blockwise Network Construction with blockwiseModules()

The blockwiseModules() function encapsulates network construction, TOM calculation, hierarchical clustering, dynamic tree pruning, and module merging into a unified, memory-aware execution pipeline:

# Execute blockwise network construction and module detection
net <- blockwiseModules(
    expr_mat,
    power = soft_power,
    networkType = "signed",
    TOMType = "signed",
    minModuleSize = 30,         # Minimum genes per module to avoid unstable micro-clusters
    mergeCutHeight = 0.25,       # Merge modules with eigengene correlation >= 0.75 (1 - 0.25)
    numericLabels = TRUE,        # Returns numeric module IDs (0 = grey)
    pamRespectsDendro = FALSE,   # Prevents PAM stage from overriding dendrogram tree cut
    maxBlockSize = 30000,        # Partition size based on available host RAM
    verbose = 3
)

# Extract numeric module labels and convert to standard WGCNA color conventions
module_labels <- net$colors
names(module_labels) <- colnames(expr_mat)
module_colors <- labels2colors(module_labels)
names(module_colors) <- names(module_labels)

# Print module size distribution
cat("=== Detected Co-expression Modules ===\n")
print(sort(table(module_colors), decreasing = TRUE))

# Diagnostic: Calculate Grey Module Fraction
grey_fraction <- mean(module_colors == "grey")
cat("\nGrey module fraction:", round(grey_fraction, 3), "(", round(grey_fraction * 100, 1), "%)\n")
if (grey_fraction > 0.50) {
    message("⚠ More than 50% of genes are in the grey module. Review filtering and sample outliers.")
}

3. Multi-Block Partitioning & Block-Specific Dendrogram Plotting

When processing large plant genomes with $> 30,000$ expressed transcripts on standard workstations ($16\text{ GB}$ RAM), blockwiseModules() automatically partitions genes into discrete blocks to prevent memory crashes.

Critical Multi-Block Visualization Rule: Never attempt to plot the global module_colors vector against a single block dendrogram. If multiple blocks are generated, each block's dendrogram must be rendered strictly against the module colors assigned to the genes inside that specific block:
# Correct multi-block dendrogram visualization loop
for (b in seq_along(net$dendrograms)) {
    block_genes <- net$blockGenes[[b]]
    block_colors <- module_colors[block_genes]
    
    plotDendroAndColors(
        net$dendrograms[[b]],
        block_colors,
        groupLabels = paste("Block", b, "Colors"),
        dendroLabels = FALSE,
        hang = 0.03,
        addGuide = TRUE,
        guideHang = 0.05,
        main = paste("Gene Dendrogram & Module Colors (Block", b, ")")
    )
}
⚠️

Common Pitfalls & Troubleshooting Checklist

  • Java Heap Exhaustion (GATK / Picard): If processes terminate with java.lang.OutOfMemoryError, set your environment variable explicitly: export _JAVA_OPTIONS="-Xmx16g -Djava.io.tmpdir=/tmp".
  • Index Timestamp Mismatch: If tools complain that a BAM index is missing or out of date, regenerate with samtools index -@ 4 sample.bam.
  • Conda Channel Conflicts: Avoid ABI incompatibilities between bioconda and conda-forge by setting strict priority: conda config --set channel_priority strict.
  • Chromosome Naming Contamination: Always verify whether reference FASTA headers use chr1 or 1; divergent naming causes silent alignment drops.
Was this guide helpful to your research?
Help us maintain 100% accurate, peer-reviewed bioinformatics standards.
📝 Propose Edit on GitHub ↗