Chapter 9: Cell Type Annotation (SingleR/CellDex), Differential Expression & RCTD Deconvolution
Chapter 9: Cell Type Annotation (SingleR/CellDex), Differential Expression & RCTD Deconvolution
Comparing marker-based, reference-based (SingleR), and label transfer annotations; assessing delta scores and pruned labels; differential expression with FindAllMarkers; cell type deconvolution in Visium HD using RCTD.
Key Learning Deliverables & Competencies
- Compare three cell annotation strategies: marker curation, automated SingleR reference mapping, and label transfer.
- Evaluate SingleR annotation confidence using delta scores and pruned label filters.
- Perform differential expression testing with
FindAllMarkers()and apply stringent statistical filters. - Implement RCTD (Robust Cell Type Decomposition) to deconvolve multi-cellular spatial bins.
1. Automated Annotation with SingleR & CellDex
Rather than relying solely on manual marker curation, SingleR maps spatial bin transcriptomes against curated reference atlases (e.g., HumanPrimaryCellAtlasData and BlueprintEncodeData):
library(SingleR)
library(celldex)
# Load Curated Human Primary Cell Reference
hpca_ref <- HumanPrimaryCellAtlasData()
# Extract Expression Matrix from Sketched Assay
expr_matrix <- GetAssayData(seurat_obj, assay = "sketch", layer = "data")
# Run Automated SingleR Annotation
pred_results <- SingleR(
test = expr_matrix,
ref = hpca_ref,
labels = hpca_ref$label.main
)
# Inspect Assignment Confidence & Pruned Labels
table(pred_results$pruned.labels)
# Transfer Labels to Seurat Metadata
seurat_obj$SingleR_label <- pred_results$pruned.labels[colnames(seurat_obj)]2. Cell Type Deconvolution with RCTD
While Visium HD's 8 µm bins are roughly the size of a single cell body, bins positioned along histological interfaces often capture a mixture of cell types (e.g., a tumor epithelial cell touching a CD8+ cytotoxic T lymphocyte and a cancer-associated fibroblast).
RCTD (Robust Cell Type Decomposition) solves this via a constrained Poisson generalized linear model that estimates the exact fractional proportions of individual cell types within each spatial bin:
library(spacexr)
# 1. Create SpatialRNA Object
coords <- GetTissueCoordinates(seurat_obj)
spatial_counts <- GetAssayData(seurat_obj, assay = "Spatial.008um", layer = "counts")
puck <- SpatialRNA(coords = coords, counts = spatial_counts)
# 2. Load Matched scRNA-Seq Reference Object
# ref_sc <- Reference(counts = ref_counts, cell_types = ref_labels)
# 3. Execute RCTD in Full or Doublet Mode
myRCTD <- create.RCTD(puck, ref_sc, max_cores = 8)
myRCTD <- run.RCTD(myRCTD, doublet_mode = "doublet")
# Extract Deconvolution Weights
weights <- get_deconvoluted_weights(myRCTD)
head(weights)⚠️ 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.