Cell Annotation: Reference Mapping

· 6 min read · Analysis Notes

Cell Annotation: Reference Mapping

TL;DR: Annotate cells by transferring labels from well-characterized reference atlases. Use SingleR for simple projection, Azimuth for UMAP-guided mapping, or Symphony for fast multi-reference integration. Best for standard tissues where high-quality references exist.


What is Reference Mapping?

Instead of manually identifying cell types, reference mapping transfers annotations from a curated atlas to your query data.

The Concept

Your data (unannotated) + Reference atlas (annotated) → Your data (annotated)

Reference Mapping Concept

Advantages

Reference Mapping Manual Annotation
Automated Manual marker checking
Consistent Subjective
Fast Time-consuming
Comprehensive May miss rare types

Limitations

  • ⚠️ Reference must include your cell types (won't find novel populations)
  • ⚠️ Batch effects can cause mis-mapping
  • ⚠️ Quality depends on reference quality

When to Use Reference Mapping

✅ Ideal for:

  • Standard tissues (PBMC, lung, brain, tumor microenvironment)
  • Validation against published atlases
  • High-throughput annotation of multiple samples
  • Cross-study integration

❌ Avoid when:

  • Novel cell types expected
  • Reference is from different species/technology
  • Small sample sizes (reference may overwhelm signal)

Major Reference Databases

Human Cell Atlas (HCA)

Link: data.humancellatlas.org
Content: ~100M cells across 40+ tissues
Best for: Cross-tissue annotation, developmental biology

CellxGene (Chan Zuckerberg Initiative)

Link: cellxgene.cziscience.com
Content: Curated single-cell datasets with interactive browser
Best for: Finding tissue-specific references, exploratory analysis

Azimuth References (Seurat)

Link: azimuth.hubmapconsortium.org
Content: Optimized references for PBMC, lung, kidney, motor cortex, etc.
Best for: Immediate use with Seurat/Azimuth workflow

Additional Resources

Database Focus URL
HuBMAP Human tissue atlases hubmapconsortium.org
Tabula Sapiens Human organs tabula-sapiens-portal.ds.czbiohub.org
Tabula Muris Mouse organs tabula-muris.ds.czbiohub.org
PanglaoDB Aggregated databases panglaodb.se

Method 1: SingleR (Simple & Fast)

Best for: Quick annotation, Bioconductor workflows

Installation

BiocManager::install("SingleR")
BiocManager::install("celldex")  # Reference datasets

Basic Usage

library(SingleR)
library(celldex)

# Load reference (built-in)
ref <- HumanPrimaryCellAtlasData()

# Annotate your Seurat/SingleCellExperiment object
predictions <- SingleR(
  test = as.SingleCellExperiment(pbmc),
  ref = ref,
  labels = ref$label.main
)

# Add to Seurat object
pbmc$SingleR_labels <- predictions$labels
pbmc$SingleR_scores <- predictions$scores

# Visualize
DimPlot(pbmc, group.by = "SingleR_labels", label = TRUE)

Available References

# List available references
celldex::listReferences()

# Common references
ref_immune <- ImmGenData()              # Mouse immune cells
ref_encode <- BlueprintEncodeData()     # Human immune/bulk
ref_monaco <- MonacoImmuneData()        # Human PBMC
ref_hpca <- HumanPrimaryCellAtlasData() # Pan-tissue human

Fine vs Coarse Labels

# Hierarchical annotation
predictions_fine <- SingleR(
  test = sce,
  ref = ref,
  labels = ref$label.fine  # Detailed labels
)

predictions_coarse <- SingleR(
  test = sce,
  ref = ref,
  labels = ref$label.main  # Broad categories
)

Method 2: Azimuth (UMAP-based)

Best for: User-friendly web interface, Seurat integration

Web Interface (No Coding)

  1. Go to azimuth.hubmapconsortium.org
  2. Select reference (PBMC, lung, kidney, etc.)
  3. Upload Seurat object (.rds) or counts matrix
  4. Download annotated results

R Workflow

library(Seurat)
library(Azimuth)

# Install reference
InstallData("pbmcref")

# Run Azimuth
pbmc <- RunAzimuth(pbmc, reference = "pbmcref")

# View transferred labels
DimPlot(pbmc, group.by = "predicted.celltype.l2", label = TRUE)

# Check confidence scores
FeaturePlot(pbmc, features = "predicted.celltype.l2.score")

Available Azimuth References

Reference Tissue Resolution
pbmcref PBMC 3 levels (L1, L2, L3)
lungref Lung Cell states + subtypes
kidneyref Kidney Developmental + adult
motorcortexref Brain Neuronal subtypes
pancreas Pancreas Endocrine + exocrine

Method 3: Symphony (Fast Multi-Reference)

Best for: Large datasets, batch integration, multiple references

Installation

devtools::install_github("immunogenomics/symphony")

Build Reference

library(symphony)

# Build from Seurat object
ref_obj <- buildReferenceFromSeurat(
  seurat_obj,
  verbose = TRUE,
  do_normalize = TRUE,
  do_umap = TRUE
)

# Save for reuse
saveRDS(ref_obj, "my_reference.rds")

Map Query

# Load reference
ref <- readRDS("my_reference.rds")

# Map query data
query_mapped <- mapQuery(
  exp_query = query_seurat,
  ref_obj = ref,
  vars = c("batch", "donor"),  # Correct for batch
  verbose = TRUE
)

# Transfer labels
query_seurat$predicted_celltype <- query_mapped$meta_data$cell_type

Method 4: Seurat v5 Reference Mapping

Transfer Labels

library(Seurat)

# Find anchors between query and reference
anchors <- FindTransferAnchors(
  reference = reference_seurat,
  query = query_seurat,
  dims = 1:30,
  reference.reduction = "pca"
)

# Transfer cell type labels
predictions <- TransferData(
  anchorset = anchors,
  refdata = reference_seurat$celltype,
  dims = 1:30
)

query_seurat$predicted_celltype <- predictions$predicted.id
query_seurat$prediction_score <- predictions$prediction.score.max

# Visualize
DimPlot(query_seurat, group.by = "predicted_celltype", label = TRUE)
VlnPlot(query_seurat, features = "prediction_score", group.by = "predicted_celltype")

Transfer UMAP Embedding

# Project query onto reference UMAP
query_seurat <- MapQuery(
  anchorset = anchors,
  reference = reference_seurat,
  query = query_seurat,
  refdata = list(celltype = "celltype"),
  reference.reduction = "pca",
  reduction.model = "umap"
)

# Plot on reference UMAP
DimPlot(query_seurat, reduction = "ref.umap", group.by = "predicted.celltype")

Quality Control for Predictions

Check Confidence Scores

# SingleR
hist(predictions$scores, main = "Prediction Scores")

# Azimuth/Seurat
VlnPlot(pbmc, features = "predicted.celltype.score", pt.size = 0)

# Flag low-confidence predictions
pbmc$confident <- pbmc$predicted.celltype.score > 0.5
table(pbmc$confident)

Compare Methods

# Run multiple methods
pbmc$singleR <- singleR_predictions$labels
pbmc$azimuth <- azimuth_predictions
pbmc$seurat <- seurat_predictions

# Confusion matrix
table(SingleR = pbmc$singleR, Azimuth = pbmc$azimuth)

# Consensus annotation (all methods agree)
pbmc$consensus <- ifelse(
  pbmc$singleR == pbmc$azimuth & pbmc$azimuth == pbmc$seurat,
  pbmc$singleR,
  "Ambiguous"
)

Validate with Markers

# Check if predicted T cells express CD3D
VlnPlot(pbmc, features = "CD3D", group.by = "predicted.celltype")

# Canonical markers by predicted type
markers_to_check <- c("CD3D", "CD14", "MS4A1", "NKG7")
DotPlot(pbmc, features = markers_to_check, group.by = "predicted.celltype")

Advanced: Cross-Modality Mapping

ATAC-seq to RNA-seq

library(Signac)

# Find transfer anchors (ATAC → RNA)
anchors <- FindTransferAnchors(
  reference = rna_seurat,
  query = atac_seurat,
  reduction = "cca",
  features = VariableFeatures(rna_seurat)
)

# Transfer labels
predictions <- TransferData(
  anchorset = anchors,
  refdata = rna_seurat$celltype,
  weight.reduction = atac_seurat[["lsi"]]
)

atac_seurat$predicted_celltype <- predictions$predicted.id

Multi-omic Integration (CITE-seq)

# Transfer protein expression from reference
protein_predictions <- TransferData(
  anchorset = anchors,
  refdata = GetAssayData(reference, assay = "ADT"),
  weight.reduction = "pca"
)

query[["ADT_predicted"]] <- CreateAssayObject(data = protein_predictions)

Troubleshooting

Low prediction scores

Causes:

  • Reference doesn't contain your cell types
  • Batch effects between query and reference
  • Poor data quality

Fix:

# Try different reference
# Or use more lenient thresholds
predictions_filtered <- predictions[predictions$scores > 0.3, ]

Mis-annotation of rare types

Cause: Reference lacks rare populations
Fix: Combine reference mapping with manual curation:

# Auto-annotate common types, manually check rare clusters
pbmc$final_celltype <- pbmc$predicted.celltype
pbmc$final_celltype[pbmc$seurat_clusters == 12] <- "Manual: Rare Type"

Reference is too different (species, technology)

Fix: Use homology-based tools:

# Convert mouse to human genes
library(orthogene)
converted <- convert_orthologs(gene_df, gene_input = "rownames", 
                               input_species = "mouse", 
                               output_species = "human")

Comparison of Methods

Method Speed Accuracy Integration Best Use
SingleR Fast High Bioconductor Quick annotation
Azimuth Medium Very high Seurat User-friendly, high-quality refs
Symphony Very fast High Harmony-based Large cohorts, multiple batches
Seurat Transfer Medium High Seurat Custom references

Resources


See also:


Originally posted on WeChat Official Account: bioinfo_studyroom

Cell Annotation