Chapter 5: Expression Count Normalization, Library Sizing & Log Transformation
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 18
0% Course Completed
1. Why Raw Read Counts Cannot Be Directly Compared
Raw integer read counts output by quantification tools (HTSeq, featureCounts) represent the number of sequencing reads that aligned to each gene. However, raw counts are confounded by two massive technical biases:
- Sequencing Depth Bias (Library Size): If Sample A is sequenced to 60 million reads and Sample B is sequenced to 20 million reads, every gene in Sample A will exhibit approximately three-fold higher raw counts purely due to sequencing effort.
- Gene Length Bias: Under uniform expression, an 8,000 bp transcript (such as TTN) will generate 8 times more fragmented cDNA reads than a 1,000 bp transcript, even when both transcripts exist at identical molar concentrations in the cell.
2. Comprehensive Comparison of Normalization Units
| Metric | Formula | Adjusts for Depth? | Adjusts for Gene Length? | Valid for Between-Sample Comparison? | Primary Research Purpose |
|---|---|---|---|---|---|
| Raw Counts | \(X_i\) | No | No | No | Input for statistical GLMs (DESeq2, edgeR) |
| CPM (Counts Per Million) | \(\frac{X_i}{\sum X} \times 10^6\) | Yes | No | Partial (sensitive to high-expression outliers) | Pre-filtering low-expression genes (e.g. CPM > 1) |
| RPKM / FPKM | \(\frac{X_i}{(L_i/10^3) \cdot (\sum X / 10^6)}\) | Yes | Yes | No (Sum of FPKMs varies per sample) | Obsolete legacy metric; historical papers |
| TPM (Transcripts Per Million) | \(\frac{X_i / L_i}{\sum (X_j / L_j)} \times 10^6\) | Yes | Yes | Yes (Invariant relative molar proportion) | Comparing expression within sample & across samples |
| DESeq2 Median of Ratios | \(s_j = \text{median}_i \left( \frac{K_{ij}}{\left(\prod_{j=1}^m K_{ij}\right)^{1/m}} \right)\) | Yes (via scaling factor \(s_j\)) | No | Yes (Gold Standard for differential testing) | Robust to extreme outlier genes (e.g. globin/albumin) |
| VST (Variance Stabilizing) | Transformation satisfying \(\text{Var}(f(Y)) \approx \text{const}\) | Yes | No | Yes (Homoskedastic log-like values) | PCA, Euclidean clustering, Machine Learning input |
3. Step-by-Step R Normalization Implementation
library(DESeq2)
library(vsn) # for meanSdPlot
# Construct DESeqDataSet
dds <- DESeqDataSetFromMatrix(
countData = mock_counts,
colData = clinical_clean,
design = ~ Condition
)
# 1. Filter lowly expressed genes (require at least 10 reads across at least 3 samples)
keep <- rowSums(counts(dds) >= 10) >= 3
dds <- dds[keep, ]
cat("Genes retained after filtering:", sum(keep), "\n")
# 2. Compute DESeq2 Median of Ratios Size Factors
dds <- estimateSizeFactors(dds)
size_factors <- sizeFactors(dds)
cat("Sample Size Factors:\n")
print(round(size_factors, 3))
# Extract normalized counts matrix
normalized_counts <- counts(dds, normalized = TRUE)
# 3. Apply Variance Stabilizing Transformation (VST)
# VST stabilizes the variance across the entire dynamic range,
# preventing high-count genes from dominating distance metrics.
vsd <- vst(dds, blind = FALSE)
vst_matrix <- assay(vsd)
# 4. Diagnostic Mean-Variance Homoskedasticity Plot
# If properly stabilized, the red running trend line should be approximately flat.
meanSdPlot(vst_matrix)
⚠️
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
chr1or1; divergent naming causes silent alignment drops.
Was this guide helpful to your research?
Help us maintain 100% accurate, peer-reviewed bioinformatics standards.