| Title: | Explainable Confidence Scoring for Single-Cell Annotations |
|---|---|
| Description: | scCertify provides an explainable framework for assessing the confidence of cell-type annotations in single-cell RNA sequencing data. Confidence scores are computed using marker enrichment, neighborhood agreement, entropy-based uncertainty estimation, and ontology-aware label matching to support transparent interpretation of annotation quality. |
| Authors: | Jaya Surya Doddetipalli [aut, cre] (ORCID: <https://orcid.org/0009-0002-8975-4425>) |
| Maintainer: | Jaya Surya Doddetipalli <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.99.7 |
| Built: | 2026-10-11 10:40:24 UTC |
| Source: | https://github.com/bioc/scCertify |
scCertify provides an explainable framework for evaluating the confidence
of single-cell RNA sequencing (scRNA-seq) cell-type annotations.
The package combines multiple complementary sources of evidence, including marker gene enrichment, neighborhood agreement, entropy-based uncertainty, and doublet information, to produce calibrated confidence scores and interpretable confidence classes.
The package is designed to complement existing cell-type annotation methods rather than replace them. Predicted cell labels are supplied by the user and scCertify evaluates the confidence of those assignments.
Both SingleCellExperiment and Seurat objects are supported.
The main workflow consists of:
obtaining predicted cell-type labels from an annotation method;
supplying marker genes for the predicted cell types;
evaluating marker consistency;
evaluating neighborhood agreement;
incorporating entropy-based uncertainty and doublet information;
combining these components into a calibrated confidence score; and
interpreting the resulting confidence using explanation functions.
cell_certify computes integrated confidence scores.
marker_score evaluates marker consistency.
neighbor_score evaluates neighborhood agreement.
entropy_score estimates entropy-based uncertainty.
calibrate_confidence calibrates confidence scores.
classify_confidence assigns confidence classes.
explain_cell summarizes confidence for an individual cell.
explain_confidence identifies major confidence contributors.
match_labels assists with label matching.
scCertify supports both SingleCellExperiment and Seurat objects.
SingleCellExperiment is the primary object class used in the package
documentation and vignette, consistent with Bioconductor conventions.
Seurat compatibility is provided for interoperability with existing
single-cell analysis workflows.
Confidence scores combine four sources of evidence:
marker enrichment;
neighborhood agreement;
entropy-based uncertainty; and
doublet information.
The resulting confidence score is classified into Low, Moderate, or High confidence according to user-defined thresholds.
Maintainer: Jaya Surya Doddetipalli [email protected] (ORCID)
Authors:
Jaya Surya Doddetipalli [email protected] (ORCID)
Useful links:
Report bugs at https://github.com/Jaya-Surya-dev/scCertify/issues
Converts raw confidence scores into calibrated percentile scores.
calibrate_confidence(scores)calibrate_confidence(scores)
scores |
Numeric vector |
Numeric vector
scores <- c( 0.2, 0.5, 0.8 ) calibrate_confidence( scores )scores <- c( 0.2, 0.5, 0.8 ) calibrate_confidence( scores )
Computes confidence scores for cell-type annotations using marker enrichment, neighborhood agreement, entropy-based uncertainty, and doublet information.
cell_certify( object, markers, label_column = "predicted_label", moderate_threshold = 0.5, high_threshold = 0.8 )cell_certify( object, markers, label_column = "predicted_label", moderate_threshold = 0.5, high_threshold = 0.8 )
object |
A Seurat or SingleCellExperiment object. |
markers |
Named list of marker genes. |
label_column |
Metadata column containing predicted labels. |
moderate_threshold |
Lower threshold for the "Moderate" confidence class. |
high_threshold |
Lower threshold for the "High" confidence class. |
The input object with confidence scores and classifications added to the metadata.
set.seed(123) counts <- matrix( rpois(1000, lambda = 5), nrow = 50, ncol = 20, dimnames = list( paste0("Gene", seq_len(50)), paste0("Cell", seq_len(20)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 10) markers <- list( "T cell" = c("Gene1", "Gene2"), "B cell" = c("Gene3", "Gene4") ) SingleCellExperiment::reducedDim(sce, "PCA") <- prcomp( t(counts), rank. = 5 )$x sce <- cell_certify( sce, markers ) head( SummarizedExperiment::colData(sce)$confidence_score )set.seed(123) counts <- matrix( rpois(1000, lambda = 5), nrow = 50, ncol = 20, dimnames = list( paste0("Gene", seq_len(50)), paste0("Cell", seq_len(20)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 10) markers <- list( "T cell" = c("Gene1", "Gene2"), "B cell" = c("Gene3", "Gene4") ) SingleCellExperiment::reducedDim(sce, "PCA") <- prcomp( t(counts), rank. = 5 )$x sce <- cell_certify( sce, markers ) head( SummarizedExperiment::colData(sce)$confidence_score )
Converts confidence scores into confidence classes.
classify_confidence(scores, moderate_threshold = 0.5, high_threshold = 0.8)classify_confidence(scores, moderate_threshold = 0.5, high_threshold = 0.8)
scores |
Numeric vector of confidence scores. |
moderate_threshold |
Lower threshold for the "Moderate" confidence class. |
high_threshold |
Lower threshold for the "High" confidence class. |
Character vector of confidence classes.
scores <- c( 0.2, 0.7, 0.9 ) classify_confidence( scores ) classify_confidence( scores, moderate_threshold = 0.4, high_threshold = 0.75 )scores <- c( 0.2, 0.7, 0.9 ) classify_confidence( scores ) classify_confidence( scores, moderate_threshold = 0.4, high_threshold = 0.75 )
Calculates entropy from prediction score matrices.
entropy_score(score_matrix)entropy_score(score_matrix)
score_matrix |
Numeric matrix |
Numeric vector
mat <- matrix( runif(20), nrow = 5 ) entropy_score( mat )mat <- matrix( runif(20), nrow = 5 ) entropy_score( mat )
Provides an interpretable explanation for the confidence score assigned to an individual cell.
explain_cell(object, cell_id)explain_cell(object, cell_id)
object |
A Seurat or SingleCellExperiment object. |
cell_id |
Cell barcode or column name. |
A character vector containing the interpretation.
set.seed(123) counts <- matrix( rpois(200, lambda = 5), nrow = 20, ncol = 10, dimnames = list( paste0("Gene", seq_len(20)), paste0("Cell", seq_len(10)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 5) SummarizedExperiment::colData(sce)$marker_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$neighbor_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$entropy_norm <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$confidence_score <- runif(ncol(sce)) explain_cell( sce, cell_id = "Cell1" )set.seed(123) counts <- matrix( rpois(200, lambda = 5), nrow = 20, ncol = 10, dimnames = list( paste0("Gene", seq_len(20)), paste0("Cell", seq_len(10)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 5) SummarizedExperiment::colData(sce)$marker_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$neighbor_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$entropy_norm <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$confidence_score <- runif(ncol(sce)) explain_cell( sce, cell_id = "Cell1" )
Explains why a cell received its confidence score.
explain_confidence(object, cell_id)explain_confidence(object, cell_id)
object |
A Seurat or SingleCellExperiment object. |
cell_id |
Cell barcode. |
Character vector describing the factors contributing to the confidence score.
set.seed(123) counts <- matrix( rpois(200, lambda = 5), nrow = 20, ncol = 10, dimnames = list( paste0("Gene", seq_len(20)), paste0("Cell", seq_len(10)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 5) SummarizedExperiment::colData(sce)$marker_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$neighbor_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$entropy_norm <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$doublet_score <- runif(ncol(sce)) explain_confidence( sce, cell_id = "Cell1" )set.seed(123) counts <- matrix( rpois(200, lambda = 5), nrow = 20, ncol = 10, dimnames = list( paste0("Gene", seq_len(20)), paste0("Cell", seq_len(10)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 5) SummarizedExperiment::colData(sce)$marker_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$neighbor_score <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$entropy_norm <- runif(ncol(sce)) SummarizedExperiment::colData(sce)$doublet_score <- runif(ncol(sce)) explain_confidence( sce, cell_id = "Cell1" )
Uses UCell enrichment scoring for marker evaluation.
marker_score(object, markers, label_column = "predicted_label")marker_score(object, markers, label_column = "predicted_label")
object |
A Seurat or SingleCellExperiment object. |
markers |
Named list of marker genes. |
label_column |
Metadata column containing predicted labels. |
Numeric vector of marker consistency scores.
set.seed(123) counts <- matrix( rpois(200, lambda = 5), nrow = 20, ncol = 10, dimnames = list( paste0("Gene", seq_len(20)), paste0("Cell", seq_len(10)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 5) markers <- list( "T cell" = c("Gene1", "Gene2"), "B cell" = c("Gene3", "Gene4") ) scores <- marker_score( sce, markers, label_column = "predicted_label" ) head(scores)set.seed(123) counts <- matrix( rpois(200, lambda = 5), nrow = 20, ncol = 10, dimnames = list( paste0("Gene", seq_len(20)), paste0("Cell", seq_len(10)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 5) markers <- list( "T cell" = c("Gene1", "Gene2"), "B cell" = c("Gene3", "Gene4") ) scores <- marker_score( sce, markers, label_column = "predicted_label" ) head(scores)
Matches predicted labels to marker database labels.
match_labels(label, marker_names)match_labels(label, marker_names)
label |
Character label |
marker_names |
Character vector |
Character vector
match_labels( "T_cells", c( "T_cell", "B_cell" ) )match_labels( "T_cells", c( "T_cell", "B_cell" ) )
Computes local neighborhood agreement scores.
neighbor_score( object, reduction = "pca", dims = NULL, k = 10, label_column = "predicted_label" )neighbor_score( object, reduction = "pca", dims = NULL, k = 10, label_column = "predicted_label" )
object |
A Seurat or SingleCellExperiment object. |
reduction |
Dimensionality reduction to use. |
dims |
Dimensions to include. |
k |
Number of nearest neighbors. |
label_column |
Metadata column containing predicted labels. |
Numeric vector of neighborhood agreement scores.
set.seed(123) counts <- matrix( rpois(1000, lambda = 5), nrow = 50, ncol = 20, dimnames = list( paste0("Gene", seq_len(50)), paste0("Cell", seq_len(20)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 10) SingleCellExperiment::reducedDim(sce, "PCA") <- prcomp( t(counts), rank. = 5 )$x scores <- neighbor_score(sce) head(scores)set.seed(123) counts <- matrix( rpois(1000, lambda = 5), nrow = 50, ncol = 20, dimnames = list( paste0("Gene", seq_len(50)), paste0("Cell", seq_len(20)) ) ) sce <- SingleCellExperiment::SingleCellExperiment( assays = list( logcounts = log1p(counts) ) ) SummarizedExperiment::colData(sce)$predicted_label <- rep(c("T cell", "B cell"), each = 10) SingleCellExperiment::reducedDim(sce, "PCA") <- prcomp( t(counts), rank. = 5 )$x scores <- neighbor_score(sce) head(scores)