Chapter 6: Exploratory Data Analysis & Dimensionality Reduction: PCA, MDS & t-SNE
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 6 of 18
0% Course Completed
1. High-Dimensional Transcriptomic Landscapes
An RNA-Seq experiment typically measures 20,000+ protein-coding genes simultaneously across a cohort of samples. Visualizing this high-dimensional space requires dimensionality reduction techniques that project maximum biological variance onto low-dimensional orthogonal coordinates (PC1, PC2). Exploratory Data Analysis (EDA) answers three critical diagnostic questions:
- Do biological replicate samples cluster tightly together by condition?
- Are there extreme sample outliers caused by degradation, sequencing failure, or mislabeling?
- Is the primary axis of variance driven by the biological variable of interest, or by an uncorrected technical batch effect?
2. Dimensionality Reduction Comparison: PCA vs. MDS vs. t-SNE
| Algorithm | Mathematical Approach | Linear vs Non-Linear | Preservation Focus | Primary Use Case in RNA-Seq |
|---|---|---|---|---|
| PCA (Principal Component Analysis) | Eigenvalue decomposition of the gene covariance matrix | Linear | Global variance maximization across all samples | First-line quality control, identifying batch effects and sample outliers |
| MDS (Multi-Dimensional Scaling) | Preserving pairwise Euclidean or Poisson sample distances | Linear / Metric | Pairwise distance preservation | Visualizing sample-to-sample similarity matrices (cmdscale) |
| t-SNE (t-Distributed SNE) | Minimizing Kullback-Leibler divergence between probability distributions | Non-linear | Local neighborhood structure preservation | Resolving distinct cellular subpopulations and non-linear clusters |
3. Production PCA & Scree Plot Script
library(DESeq2)
library(ggplot2)
# Extract top 500 most variable genes from VST matrix
ntop <- 500
rv <- rowVars(assay(vsd))
select <- order(rv, decreasing = TRUE)[seq_len(min(ntop, length(rv)))]
pca <- prcomp(t(assay(vsd)[select, ]))
# Calculate percentage variance explained by each PC
percent_var <- round((pca$sdev^2 / sum(pca$sdev^2)) * 100, 1)
# Construct PCA Dataframe
pca_df <- data.frame(
PC1 = pca$x[, 1],
PC2 = pca$x[, 2],
PC3 = pca$x[, 3],
Sample = colnames(vsd),
Condition = colData(vsd)$Condition,
Batch = colData(vsd)$Extraction_Batch
)
# 1. Publication-Grade PCA Biplot
ggplot(pca_df, aes(x = PC1, y = PC2, color = Condition, shape = Batch)) +
geom_point(size = 4, alpha = 0.9) +
stat_ellipse(aes(group = Condition, fill = Condition), geom = "polygon", alpha = 0.1, linetype = "dashed") +
scale_color_manual(values = c("Control" = "#06b6d4", "Treated" = "#f43f5e")) +
scale_fill_manual(values = c("Control" = "#06b6d4", "Treated" = "#f43f5e")) +
labs(
title = "Principal Component Analysis (PCA) of Transcriptomic Cohort",
subtitle = "Clear biological separation along PC1; Batch variance checked along PC2",
x = paste0("PC1: ", percent_var[1], "% variance explained"),
y = paste0("PC2: ", percent_var[2], "% variance explained")
) +
theme_bw(base_size = 14) +
theme(panel.grid.minor = element_blank())
# 2. Scree Plot of Eigenvalue Variances
scree_df <- data.frame(
PC = paste0("PC", 1:10),
Variance = percent_var[1:10]
)
scree_df$PC <- factor(scree_df$PC, levels = scree_df$PC)
ggplot(scree_df, aes(x = PC, y = Variance)) +
geom_col(fill = "#3b82f6", width = 0.6) +
geom_line(aes(group = 1), color = "#f59e0b", linewidth = 1) +
geom_point(color = "#f59e0b", size = 3) +
labs(
title = "PCA Scree Plot (Percentage Variance Explained)",
x = "Principal Component",
y = "% Variance Explained"
) +
theme_minimal(base_size = 14)
⚠️
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.