Chapter 5: Computational Quality Control & Spatial Outlier Filtering in R
Chapter 5: Computational Quality Control & Spatial Outlier Filtering in R
Ingesting spatial data into R; evaluating library size (nCount_Spatial), gene diversity (nFeature_Spatial), and mitochondrial percentage; spatial QC feature plots, identifying tissue tears and bubbles, and establishing defensible QC cutoffs.
Key Learning Deliverables & Competencies
- Load Space Ranger outputs into R utilizing Seurat v5 and
hdf5r. - Compute key spatial bin quality control metrics:
nCount_Spatial,nFeature_Spatial, andpercent.mt. - Identify technical spatial artifacts: tissue folds, tears, bubbles, edge drying, and detachment zones.
- Establish defensible, data-driven filtering thresholds accompanied by written mathematical justifications.
1. Loading Data & Calculating QC Metrics in R
In spatial analysis, quality control must be evaluated in both statistical distribution space (violin/scatter plots) and 2D spatial tissue space (spatial feature plots):
library(Seurat)
library(ggplot2)
library(patchwork)
library(viridis)
# 1. Load 8 µm Binned Visium HD Data
data_dir <- "/data/Visium_HD_Colorectal_Cancer_TME/outs"
seurat_obj <- Load10X_Spatial(
data.dir = data_dir,
filename = "binned_outputs/square_008um/filtered_feature_bc_matrix.h5",
assay = "Spatial.008um",
slice = "colorectal_section1"
)
# 2. Calculate Mitochondrial & Ribosomal Percentages
seurat_obj[["percent.mt"]] <- PercentageFeatureSet(seurat_obj, pattern = "^MT-")
seurat_obj[["percent.ribo"]] <- PercentageFeatureSet(seurat_obj, pattern = "^RP[SL]")
# 3. Inspect Distribution Summaries
summary(seurat_obj$nCount_Spatial.008um)
summary(seurat_obj$nFeature_Spatial.008um)
summary(seurat_obj$percent.mt)2. Visualizing Spatial Outliers in Tissue Space
Standard scRNA-seq thresholds cannot be blindly applied to spatial bins. For instance, acellular necrotic regions or dense collagenous stroma naturally have lower transcript counts without being technical failures:
# Spatial Feature Plots for Quality Control
p1 <- SpatialFeaturePlot(seurat_obj, features = "nCount_Spatial.008um") +
scale_fill_viridis(option = "magma") +
ggtitle("Total UMIs per 8 µm Bin")
p2 <- SpatialFeaturePlot(seurat_obj, features = "nFeature_Spatial.008um") +
scale_fill_viridis(option = "viridis") +
ggtitle("Detected Genes per 8 µm Bin")
p3 <- SpatialFeaturePlot(seurat_obj, features = "percent.mt") +
scale_fill_gradient2(low = "blue", mid = "yellow", high = "red", midpoint = 15) +
ggtitle("Mitochondrial % (Cell Lysis / Artifacts)")
(p1 | p2) / p3✍️ Checkpoint: Defensible Threshold Selection
Participants must justify QC cutoff parameters before proceeding to normalization:
• Minimum UMI Count: Filter bins with $nCount < 50$ (eliminates empty background bins and low-permeabilized tissue fragments).
• Minimum Detected Genes: Filter bins with $nFeature < 30$ to ensure sufficient gene diversity for multivariate clustering.
• Maximum Mitochondrial Ratio: Filter bins with $percent.mt > 25\%$ (signals mitochondrial membrane disruption and severe cytoplasmic transcript loss).
⚠️ Common Pitfalls & Troubleshooting in Spatial Genomics
- Out-Of-Memory (OOM) Crashes on 2 µm/8 µm Grids: Never run default
FindVariableFeatures()or dense Euclidean distance matrices across raw unsketched 2 µm arrays. Always leverage Seurat v5'sSketchData()with on-disk BPCells streaming. - Tissue Detachment & Optical Misalignment: Inspect fiducial edge coordinates in Loupe Browser before downstream R processing. If tissue folds or lifts during CytAssist transfer, manually flag and filter affected spatial barcodes.
- Over-Filtering Low-Cellularity Stromal Bins: Dense desmoplastic collagen or mucinous lakes naturally possess low transcript abundance. Setting an overly aggressive $nFeature$ threshold risks completely discarding critical acellular microenvironments.
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.