Cell-Type Annotation in scRNA-seq: Markers vs CellTypist, Azimuth, and SingleR
A decision tree for annotating single-cell clusters using marker scoring, SingleR, Azimuth, and CellTypist — and when to trust each call.
Every single-cell project reaches the same crossroads: you have thirty Leiden clusters, a gorgeous UMAP, and no idea what any of the dots actually are. Naming clusters is where the biology finally enters, and it is also where most published atlases quietly go wrong. The temptation is to run one automated tool, accept its labels, and move on. The better path is to understand what each annotation method is measuring, because a correlation-based call, an anchor-based projection, and a logistic-regression classifier can disagree about the same cluster for reasons that are entirely predictable once you know how they work.
The Annotation Decision Tree
There are three broad strategies, and they are not interchangeable. Manual marker scoring asks whether a cluster expresses genes you already know define a cell type. Reference-based mapping asks which cells in a labeled atlas your cells most resemble — this splits into correlation methods (SingleR) and anchor-based projection (Azimuth). Classifier-based prediction trains a model on a reference and applies it to your data (CellTypist, scANVI). Marker scoring is transparent but slow and subjective; reference methods are fast but only as good as the atlas you point them at. Most real pipelines use two or three in combination and trust the consensus, not any single call.
Manual Marker-Gene Scoring
The oldest approach is still the ground truth. You take canonical markers — CD3D and CD3E for T cells, MS4A1 and CD79A for B cells, LYZ and CD14 for monocytes, EPCAM for epithelium — and check where they light up. In Scanpy, sc.tl.rank_genes_groups with the wilcoxon method surfaces the top differentially expressed genes per cluster, and sc.tl.score_genes collapses a marker panel into a single per-cell score you can plot on the UMAP.
The strength of manual scoring is that it forces you to look. You will catch doublet clusters co-expressing CD3E and LYZ, and you will notice a cluster that expresses no lineage marker at all — often a low-quality or ambient-RNA artifact. The weakness is that it does not scale, it depends on marker panels that vary between labs, and it collapses under fine-grained subtypes. Distinguishing CD4 naive from central-memory T cells by eye is a losing game. That is where automated tools earn their place. If you are still building intuition for the upstream clustering, start with single-cell RNA-seq for beginners.
A marker being "positive" is relative, not absolute. Dropout means a true T cell may show zero CD3D counts in a given droplet. Always judge a marker by its enrichment across the cluster, not by whether individual cells hit a fixed count threshold like counts > 0.
SingleR: Correlation to a Reference
SingleR (R/Bioconductor) works by correlation. For every cell — or every cluster, if you aggregate first — it computes a Spearman correlation against each reference label's expression profile, restricting to variable genes, then assigns the label with the highest score. It refines this with a fine-tuning step that iteratively drops the weakest candidate labels and re-correlates on the genes that best separate the survivors.
The elegant part is that SingleR is reference-agnostic and needs no shared feature space beyond overlapping gene names. Built-in references from celldex — such as the Human Primary Cell Atlas or Monaco immune data — make it a one-liner for immune work. The catch is that correlation rewards the closest available label even when the truth is absent from the reference. Feed it a tumor sample against a healthy-blood reference and it will confidently call malignant cells "monocytes." Always inspect the pruned.labels output and the per-cell score heatmap; low or uniform scores are the tool telling you it is guessing.
Azimuth: Anchor-Based Reference Mapping
Azimuth, built on Seurat's transfer-anchor framework, is a fundamentally different operation. Instead of correlating profiles, it finds mutual nearest-neighbor "anchors" between your query and a curated, multimodal reference (the human PBMC, lung, or bone-marrow atlases), then projects your cells into the reference's own PCA and UMAP space and transfers labels along those anchors. Because the references are annotated at several resolutions, you get nested calls — celltype.l1, celltype.l2, celltype.l3 — from coarse lineage down to fine subtype.
Anchor mapping shines when your data and the reference come from similar technology and tissue. It also returns a mapping.score and prediction.score per cell, which flag cells that do not sit cleanly in the reference manifold. The limitation is rigidity: you are locked to the references CZI and the Satija lab have published. A cell state absent from the reference gets forced onto the nearest anchor, and rare or novel populations can vanish into a dominant neighbor. Azimuth is best treated as a fast, well-calibrated first pass for well-characterized tissues, not a discovery tool.
CellTypist: Logistic-Regression Classifiers
CellTypist takes the machine-learning route. Its models are L2-regularized logistic-regression classifiers trained on large, harmonized atlases — the Immune_All_Low.pkl and Immune_All_High.pkl models cover peripheral and tissue immune cells at two resolutions. Prediction is a single celltypist.annotate call on log-normalized data scaled to 10000 counts per cell, and it returns both a raw per-cell label and, crucially, a majority-voting label that harmonizes predictions over the clustering neighborhood to suppress single-cell noise.
CellTypist's advantages are speed, probability outputs, and the --majority-voting option that stabilizes calls. Because the models ship pre-trained, you can annotate a fresh PBMC dataset in seconds with no reference download beyond the model file. The failure mode is the shared one: a classifier only knows its training classes. Run a neuronal dataset through an immune model and every cell gets an immune label with spurious confidence. Match the model to your tissue, and use celltypist.train to build your own when no stock model fits.
scANVI and the Deep-Learning Option
When you need to annotate one dataset using a partially labeled reference and correct batch effects at the same time, scANVI (from scvi-tools) is the semi-supervised deep generative approach. It extends the scVI latent-variable model with a cell-type classifier head, learning a batch-corrected latent space where labels propagate from annotated to unannotated cells. This is the method of choice for large reference-mapping projects and for atlas building, where integration and annotation must happen jointly rather than as separate steps. The cost is real: scANVI needs a GPU for reasonable training times, careful epoch tuning, and far more setup than a CellTypist one-liner. Reserve it for atlas-scale work, not a routine ten-sample experiment.
Cell Ontology: Making Labels Comparable
A subtle problem sinks many multi-dataset projects: everyone spells cell types differently. "CD8 T", "CD8+ T cell", and "Cytotoxic T lymphocyte" are the same population under three strings, and no computer knows that. The Cell Ontology (CL) solves this with stable, structured identifiers — CL:0000625 is exactly one concept, "CD8-positive, alpha-beta T cell", positioned in a hierarchy of is_a relationships. Mapping every tool's output to CL terms lets you compare annotations across SingleR, Azimuth, and CellTypist, roll fine labels up to coarse ones programmatically, and submit to atlases like CELLxGENE that require CL terms. Treat CL as the common language your annotations must eventually speak.
Choosing a Method, and When to Distrust It
| Method | Mechanism | Best for | Watch out for |
|---|---|---|---|
| Manual markers | Known-gene scoring | Sanity checks, novel states | Subjective, does not scale |
SingleR | Spearman correlation | Fast immune calls in R | Forces nearest label if truth absent |
Azimuth | Transfer anchors | Well-mapped tissues (PBMC, lung) | Locked to published references |
CellTypist | Logistic regression | Immune data, probability output | Only knows trained classes |
scANVI | Semi-supervised deep model | Atlas building, joint integration | GPU, heavy tuning |
The table tells you which tool to reach for; judgment tells you when to overrule it. Automated annotation is a hypothesis generator, not an oracle. Distrust any call where the confidence score is low or flat across labels, where the reference plausibly lacks your true cell type (tumors, organoids, perturbed states, non-model tissues), or where two methods with different mechanisms disagree — a SingleR-versus-CellTypist conflict almost always marks a genuinely ambiguous or transitional population worth manual review. And never trust a label that contradicts its own markers: if a cluster is called "B cell" but shows no MS4A1 and strong CD3E, the tool is wrong, not the biology.
The single most reliable annotation workflow is consensus. Run one classifier and one correlation method, map both to Cell Ontology terms, and manually resolve every cluster where they disagree. The agreements are cheap confidence; the disagreements are where the science is.
Practical Takeaways
- Start with manual markers for a coarse sanity map, then layer automated tools on top — never the reverse.
- Use
SingleRorCellTypistfor fast immune calls,Azimuthfor well-characterized tissues, andscANVIonly for atlas-scale integration. - Always read the confidence outputs —
pruned.labels,mapping.score, majority-voting labels — and treat low scores as "unknown," not as truth. - Match every reference or model to your tissue and platform; a mismatched reference produces confident nonsense.
- Map all labels to Cell Ontology (CL) terms so results are comparable across tools and submittable to public atlases.
- Resolve disagreements between methods manually — those clusters carry the most biological signal.
Solid annotation rests on a clean upstream pipeline, so get the counting and clustering right first — install the core toolkit with our Scanpy install guide and manage everything in isolated conda environments with Bioconda channels. Once your labels are trustworthy, the same clusters feed directly into downstream enrichment analysis with clusterProfiler.