| Title: | Fast Ligand-Receptor Interaction Screening for Single-Cell Data Using Rust |
|---|---|
| Description: | Fast identification of over-expressed genes and ligand-receptor interactions in single-cell RNA-seq data, the pre-processing steps of cell-cell communication analysis tools such as CellChat. Works directly on SummarizedExperiment and SingleCellExperiment objects and accepts any ligand-receptor table (for example the CellChatDB interaction table). The per-gene, per-cell-group Wilcoxon rank-sum tests are implemented in Rust via extendr and run in parallel with Rayon. |
| Authors: | Mayank Gandhi [aut, cre] (ORCID: <https://orcid.org/0009-0000-3448-9308>) |
| Maintainer: | Mayank Gandhi <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.99.1 |
| Built: | 2026-10-01 06:17:07 UTC |
| Source: | https://github.com/BiocStaging/cellchatr |
Runs a one-sided ("greater") Wilcoxon rank-sum test for every gene and every cell group, comparing cells of that group against all other cells. A gene is reported if it is significantly higher in at least one group. Tests are run in parallel in Rust (Rayon), using the normal approximation with tie correction.
findOverexpressedGenes(x, ...) ## S4 method for signature 'ANY' findOverexpressedGenes(x, labels, lrdb = NULL, pval_threshold = 0.05) ## S4 method for signature 'SummarizedExperiment' findOverexpressedGenes(x, labels, assay.type = "logcounts", ...)findOverexpressedGenes(x, ...) ## S4 method for signature 'ANY' findOverexpressedGenes(x, labels, lrdb = NULL, pval_threshold = 0.05) ## S4 method for signature 'SummarizedExperiment' findOverexpressedGenes(x, labels, assay.type = "logcounts", ...)
x |
A numeric gene-by-cell matrix (dense |
... |
Further arguments passed to the matrix method. |
labels |
Cell group labels. Either a vector with one entry per cell
(column of |
lrdb |
Optional ligand-receptor table (see |
pval_threshold |
P-value cutoff for significance. |
assay.type |
For |
A character vector of over-expressed gene names.
counts <- matrix(c(rep(10, 50), rep(0, 50), rep(1, 100)), nrow = 2, byrow = TRUE, dimnames = list(c("TGFB1", "GAPDH"), NULL)) labels <- c(rep("A", 50), rep("B", 50)) findOverexpressedGenes(counts, labels) # SummarizedExperiment / SingleCellExperiment input se <- SummarizedExperiment::SummarizedExperiment( assays = list(logcounts = counts), colData = S4Vectors::DataFrame(cell_type = labels)) findOverexpressedGenes(se, labels = "cell_type")counts <- matrix(c(rep(10, 50), rep(0, 50), rep(1, 100)), nrow = 2, byrow = TRUE, dimnames = list(c("TGFB1", "GAPDH"), NULL)) labels <- c(rep("A", 50), rep("B", 50)) findOverexpressedGenes(counts, labels) # SummarizedExperiment / SingleCellExperiment input se <- SummarizedExperiment::SummarizedExperiment( assays = list(logcounts = counts), colData = S4Vectors::DataFrame(cell_type = labels)) findOverexpressedGenes(se, labels = "cell_type")
Convenience wrapper running the full workflow: restrict x to genes in
the ligand-receptor table, find over-expressed genes per cell group with
findOverexpressedGenes(), and return the interactions they take part
in with matchLRPairs().
identifyInteractions(x, lrdb, ...)identifyInteractions(x, lrdb, ...)
x |
A numeric gene-by-cell matrix (dense |
lrdb |
Ligand-receptor table, see |
... |
Further arguments passed to |
A S4Vectors::DataFrame of matching interactions, with
the over-expressed genes stored in metadata(result)$overexpressed.
library(SingleCellExperiment) set.seed(1) genes <- c("TGFB1", "TGFBR1", "VEGFA", "FLT1", paste0("G", 1:20)) mat <- matrix(rnorm(length(genes) * 60, mean = 1), nrow = length(genes), dimnames = list(genes, NULL)) cell_type <- rep(c("Fibroblast", "Endothelial"), each = 30) mat["TGFB1", cell_type == "Fibroblast"] <- 6 sce <- SingleCellExperiment(assays = list(logcounts = mat), colData = DataFrame(cell_type = cell_type)) lrdb <- data.frame( interaction_name = c("TGFB1_TGFBR1", "VEGFA_FLT1"), ligand = c("TGFB1", "VEGFA"), receptor = c("TGFBR1", "FLT1")) identifyInteractions(sce, lrdb, labels = "cell_type")library(SingleCellExperiment) set.seed(1) genes <- c("TGFB1", "TGFBR1", "VEGFA", "FLT1", paste0("G", 1:20)) mat <- matrix(rnorm(length(genes) * 60, mean = 1), nrow = length(genes), dimnames = list(genes, NULL)) cell_type <- rep(c("Fibroblast", "Endothelial"), each = 30) mat["TGFB1", cell_type == "Fibroblast"] <- 6 sce <- SingleCellExperiment(assays = list(logcounts = mat), colData = DataFrame(cell_type = cell_type)) lrdb <- data.frame( interaction_name = c("TGFB1_TGFBR1", "VEGFA_FLT1"), ligand = c("TGFB1", "VEGFA"), receptor = c("TGFBR1", "FLT1")) identifyInteractions(sce, lrdb, labels = "cell_type")
Keeps the interactions in a ligand-receptor table whose ligand OR receptor is among the supplied genes. The lookup is done with a Rust hash set.
matchLRPairs(genes, lrdb)matchLRPairs(genes, lrdb)
genes |
Character vector of gene names (typically the output of
|
lrdb |
A |
A S4Vectors::DataFrame holding the matching rows of
lrdb, with all of its columns.
lrdb <- data.frame( interaction_name = c("TGFB1_TGFBR1", "MYC_MYCBP", "VEGFA_FLT1"), ligand = c("TGFB1", "MYC", "VEGFA"), receptor = c("TGFBR1", "MYCBP", "FLT1")) matchLRPairs(c("TGFB1", "FLT1"), lrdb)lrdb <- data.frame( interaction_name = c("TGFB1_TGFBR1", "MYC_MYCBP", "VEGFA_FLT1"), ligand = c("TGFB1", "MYC", "VEGFA"), receptor = c("TGFBR1", "MYCBP", "FLT1")) matchLRPairs(c("TGFB1", "FLT1"), lrdb)
Match overexpressed genes against CellChatDB ligand-receptor pairs.
rust_match_lr_pairs(overexpressed, lr_ligands, lr_receptors, lr_names)rust_match_lr_pairs(overexpressed, lr_ligands, lr_receptors, lr_names)
overexpressed |
Character vector of overexpressed gene names. |
lr_ligands |
Character vector of ligands from CellChatDB. |
lr_receptors |
Character vector of receptors from CellChatDB. |
lr_names |
Character vector of interaction names from CellChatDB. |
Character vector of matched interaction names.
Time: O(m + r) where m = overexpressed genes, r = LR pairs
Space: O(m) for HashSet
R equivalent (%%in%% twice): O(m x r)
rust_match_lr_pairs( c("TGFB1", "VEGFA"), c("TGFB1", "MYC", "VEGFA"), c("TGFBR1", "MYCBP", "FLT1"), c("TGFB1_TGFBR1", "MYC_MYCBP", "VEGFA_FLT1") )rust_match_lr_pairs( c("TGFB1", "VEGFA"), c("TGFB1", "MYC", "VEGFA"), c("TGFBR1", "MYCBP", "FLT1"), c("TGFB1_TGFBR1", "MYC_MYCBP", "VEGFA_FLT1") )
Filter expression matrix genes to only those present in CellChatDB.
rust_subset_genes(expr_genes, db_genes)rust_subset_genes(expr_genes, db_genes)
expr_genes |
Character vector of gene names from expression matrix. |
db_genes |
Character vector of gene names from CellChatDB. |
Character vector of genes present in both inputs.
Time: O(n + m) where n = db_genes, m = expr_genes
Space: O(n) for HashSet
R equivalent (%%in%%): O(n x m)
rust_subset_genes( c("TGFB1", "VEGFA", "GAPDH"), c("TGFB1", "VEGFA", "MYC") )rust_subset_genes( c("TGFB1", "VEGFA", "GAPDH"), c("TGFB1", "VEGFA", "MYC") )
For each gene and each cell type, a one-sided ("greater") Wilcoxon rank-sum test compares the cells of that type against all other cells, using the normal approximation with tie correction. A gene is returned if it is significantly higher in at least one cell type.
rust_wilcoxon_filter(counts, labels, gene_names, pval_threshold)rust_wilcoxon_filter(counts, labels, gene_names, pval_threshold)
counts |
NumericMatrix of gene expression (genes x cells). |
labels |
Character vector of cell type labels, one per cell. |
gene_names |
Character vector of gene names. |
pval_threshold |
P-value cutoff for significance. |
Character vector of over-expressed gene names.
Time: O((g/p) x k x c log c) where g = genes, p = CPU cores, k = cell types, c = cells
Space: O(c) per gene
R equivalent: O(g x k x c log c) — serial, no parallelism
counts <- matrix(c(rep(10, 50), rep(0, 50), rep(1, 100)), nrow = 2, byrow = TRUE) labels <- c(rep("A", 50), rep("B", 50)) rust_wilcoxon_filter(counts, labels, c("TGFB1", "GAPDH"), 0.05)counts <- matrix(c(rep(10, 50), rep(0, 50), rep(1, 100)), nrow = 2, byrow = TRUE) labels <- c(rep("A", 50), rep("B", 50)) rust_wilcoxon_filter(counts, labels, c("TGFB1", "GAPDH"), 0.05)