
Accurate cell type annotation is essential but difficult in single-cell RNA sequencing (scRNA-seq).
scTypeEval provides a ground-truth-agnostic framework to systematically assess annotation quality using internal validation metrics.
For comprehensive tutorials, detailed metric explanations, and real-world case studies, please visit our documentation website:
scTypeEval Tutorials and Case Studies
Install scTypeEval from Bioconductor (recommended) or GitHub.
# Option 1 (recommended): Bioconductor/devel install (R >= 4.6.0)
if (!requireNamespace("BiocManager", quietly = TRUE)) {
install.packages("BiocManager")
}
BiocManager::install("scTypeEval")
# Option 2: Install GitHub releases
if (!requireNamespace("remotes", quietly = TRUE)) {
install.packages("remotes")
}
# Installing without @<tag> pulls the current GitHub branch (R >= 4.6.0)
# remotes::install_github("carmonalab/scTypeEval")
install.packages("scTypeEval", repos = c('https://carmonalab.r-universe.dev', 'https://cloud.r-project.org'))
# R-4.3.0_prerelease installs the pre-release compatible with R >= 4.3.0
remotes::install_github("carmonalab/scTypeEval@R-4.3.0_prerelease")
This package is supported on macOS, Linux, and Windows through continuous integration and automated builds.
DESCRIPTION).DESCRIPTION under Imports:
Matrix (>= 1.6-5)BiocParallel (>= 1.34.2)dplyr (>= 1.1.4)tidyr (>= 1.3.1)scran (>= 1.30.2)bluster (>= 1.12.0)ggplot2 (>= 3.5.1)ggrepel (>= 0.9.6)cluster (>= 2.1.4)SingleR (>= 2.4.1)irlba (>= 2.3.5.1)transport (>= 0.15-4)grDevices, methods, stats, utils
To run scTypeEval on your own data, follow the workflow below.
For complete runnable examples, see the repository vignettes:
For larger datasets, runtime increases with dataset size (especially number of samples), feature space, and selected dissimilarity method:
scTypeEval objects accept either a count matrix (rows as genes and columns as cells) and its corresponding metadata, a Seurat object, or a SingleCellExperiment (SCE) object. Metadata is expected to contain annotation labels and sample identifiers.
library(scTypeEval)
# From count matrix and metadata dataframe
sceval <- create_scTypeEval(matrix = count_matrix,
metadata = metadata)
# From Seurat object
sceval <- create_scTypeEval(seurat_obj)
# From SCE object
sceval <- create_scTypeEval(sce)
Process and normalize data stored in an scTypeEval object.
This step aggregates, filters, and normalizes the count matrix, storing results as data_assay objects for single-cell and pseudobulk data.
# Run data processing on an scTypeEval object
sceval <- run_processing_data(
scTypeEval = sceval,
ident = "celltype", # column in metadata defining identities (e.g. cell type)
sample = "patient_id", # column in metadata defining sample IDs
min_samples = 5, # minimum samples required to retain a cell type
min_cells = 10, # minimum cells required per sample-celltype
)
Extract relevant features such as highly variable genes (HVGs) and cell type marker genes, or add custom gene lists. Dissimilarity and subsequently consistency will be evaluated using these features.
# Identify highly variable genes (HVGs)
sceval <- run_hvg(
scTypeEval = sceval,
var_method = "scran", # method to compute HVGs
ngenes = 2000, # number of HVGs to retain
sample = TRUE # whether to perform sample-level blocking
)
# Identify marker genes per cell type
sceval <- run_gene_markers(
scTypeEval = sceval,
method = "scran.findMarkers", # supported: scran.findMarkers
ngenes_celltype = 50 # max markers per cell type
)
Custom gene list may be also added using add_gene_list().
Consistency metrics can be measured directly on relevant features selected earlier.
However, for most methods, their computation in a low-dimensional space (e.g., PCA) speeds up the process while yielding very similar results.
sceval <- run_pca(
scTypeEval = sceval,
ndim = 30 # number of PCs
)
Alternatively, you can insert pre-computed embeddings (e.g., PCA, UMAP, t-SNE) using add_dim_reduction().
The function run_dissimilarity() computes pairwise dissimilarities between cell types across samples stored in a scTypeEval object.
You can choose among several strategies depending on whether you want to compare cell type pseudobulk profiles, cell type single-cell distributions, or classification-based matches.
Available methods include:
Pseudobulk:<distance> – computes distances between pseudobulk gene expression profiles.WasserStein – computes Wasserstein distances between distributions of cells.recip_classif:<method> – matches cells across groups using a classifier and computes dissimilarities.By default, if reduction = TRUE, dissimilarity is computed on dimensional reduction embeddings (e.g. PCA).
Set reduction = FALSE to instead compute dissimilarities on processed expression data.
# Euclidean distance based on pseudobulk aggregation
sceval <- run_dissimilarity(sceval,
method = "Pseudobulk:Euclidean",
reduction = FALSE) # whether to compute dissimilarities in low dimensional space
# Wasserstein distance on embeddings
sceval <- run_dissimilarity(sceval,
method = "WasserStein",
reduction = TRUE)
# Reciprocal Classification Match using SingleR classifier
sceval <- run_dissimilarity(sceval,
method = "recip_classif:Match",
reciprocal_classifier = "SingleR")
The function plot_heatmap() visualizes dissimilarity matrices stored in a scTypeEval object as annotated heatmaps.
This produces a ggplot2 heatmap with cell types grouped and optionally ordered by similarity or consistency.
plot_heatmap(sceval,
dissimilarity_slot = "recip_classif:Match",
sort_consistency = "silhouette",
sort_similarity = "Pseudobulk:Euclidean")

Evaluate inter-sample label consistency.
scTypeEval supports a range of internal validation metrics to evaluate cell type annotation quality without external ground truth:
Higher values indicate stronger internal consistency. Metrics can be computed per dissimilarity assay for downstream comparison across cell types, metrics, and methods.
consis <- get_consistency(scTypeEval,
dissimilarity_slot = "Pseudobulk:Euclidean", # indicate in which dissimilarity space compute metrics
consistency_metric = "silhouette" # choose consistency metric
)
Example of results table:
| celltype | measure | consistency_metric | dissimilarity_method | ident |
|---|---|---|---|---|
| CD4.Tn | -0.03104529 | silhouette | Pseudobulk:Euclidean | celltype |
| CD4.Tstr | -0.01739486 | silhouette | Pseudobulk:Euclidean | celltype |
| CD4.Tfh | 0.01741703 | silhouette | Pseudobulk:Euclidean | celltype |
| CD4.Tcm | 0.04147180 | silhouette | Pseudobulk:Euclidean | celltype |
| CD8.t.Teff | 0.04487724 | silhouette | Pseudobulk:Euclidean | celltype |
| CD4.Tctl | 0.10953912 | silhouette | Pseudobulk:Euclidean | celltype |
| CD8.Teff | 0.09775360 | silhouette | Pseudobulk:Euclidean | celltype |
| CD8.Tcm | 0.10451983 | silhouette | Pseudobulk:Euclidean | celltype |
| CD8.Tn | 0.13723062 | silhouette | Pseudobulk:Euclidean | celltype |
| CD8.Tstr | 0.15740846 | silhouette | Pseudobulk:Euclidean | celltype |
| CD4.Th17 | 0.24654894 | silhouette | Pseudobulk:Euclidean | celltype |
| CD8.Tex | 0.27483708 | silhouette | Pseudobulk:Euclidean | celltype |
| CD4.Treg | 0.26537602 | silhouette | Pseudobulk:Euclidean | celltype |
Based on our benchmark across 31 real scRNA-seq datasets, we recommend the following ISC metric choices:
By default (and in the benchmark), these metrics are computed on highly variable genes (HVGs) identified with scran via run_hvg(var_method = "scran").
In our benchmark we observed a trade-off: some metrics are more sensitive to global, gradual signal degradation, while others are more sensitive to local structural inconsistencies/over-partitioning, so we recommend using one from each class.
silhouette in the reciprocal-classification match space (recip_classif:Match) — also the top-ranked overall choice in the benchmark.2label_silhouette in pseudobulk cosine space (Pseudobulk:Cosine).

library(dplyr)
library(scTypeEval)
## Assumes you already created `sceval` and ran processing (see Usage above)
# Default feature space used in the benchmark: scran HVGs, sample-blocked,
# on single-cell data
# Also, we recommend removing from selected genes within HVG the black listed
# genes with high inter-sample variability
data(black_list)
bl <- list(black_list$TCR,
black_list$Immunoglobulins,
black_list$Ygenes) |>
unlist()
sceval <- run_hvg(
scTypeEval = sceval,
var_method = "scran",
ngenes = 2000,
sample = TRUE,
aggregation = "single-cell",
black_list = bl
)
# Not affecting recip_classif:Match but for Pseudobulk-based metrics,
# computation in PCA space is faster with similar results
# (See Usage above to add dimensional reduction).
# (1) Overall best and local structure: silhouette in
# reciprocal-classification match space
sceval <- run_dissimilarity(
sceval,
method = "recip_classif:Match",
reduction = FALSE
)
# (2) Complementary for global structure: 2-label silhouette
# in pseudobulk cosine space
sceval <- run_dissimilarity(
sceval,
method = "Pseudobulk:Cosine",
reduction = TRUE
)
# (3) Obtain consistency scores for computed dissimilarities and
# for silhouette and 2label_silhouette
isc <- get_consistency(
sceval,
dissimilarity_slot = c("recip_classif:Match", "Pseudobulk:Cosine"),
consistency_metric = c("silhouette", "2label_silhouette")
)
# (4) Filter to obtain best combinations for each cell type
sel.methods <- c("local" = "silhouette | recip_classif:Match",
"global" = "2label_silhouette | Pseudobulk:Cosine")
isc |>
mutate(method.type = paste(consistency_metric,
dissimilarity_method,
sep = " | ")
) |>
filter(method.type %in% sel.methods)
To reproduce the manuscript benchmarking analyses and figures (task simulations + scoring helpers), use:
This external repository contains the dedicated code and instructions for full quantitative reproduction.
If you use scTypeEval in your work, please cite the associated preprint:
Garnica J, Andreatta M, Carmona SJ (2026). Evaluating cell type annotations in single-cell omics in the absence of ground truth. bioRxiv. https://doi.org/10.64898/2026.06.10.731285
This work is currently available as a preprint; a peer-reviewed publication will be added when available.