sc_tools.tl — Analysis Tools#
Analysis tools for spatial omics data following the scanpy API pattern.
import sc_tools.tl as tl
hallmark = tl.load_hallmark()
combined = tl.merge_gene_signatures(project_sigs, hallmark)
tl.score_signature(adata, combined)
Gene Signature Scoring#
Scores are stored in adata.obsm['signature_score'] (raw) and
adata.obsm['signature_score_z'] (z-scored). Column names are full paths,
e.g. Hallmark/HYPOXIA, Myeloid/Macrophage_Core.
- sc_tools.tl.score_signature(adata, signatures_nested, method='scanpy', use_raw=True, ctrl_size=50, n_bins=25, min_genes=3, copy=False, save_path=None)[source]#
Score gene signatures and store in obsm (single flat key for traceability).
Supports three scoring backends selected by
method: -"scanpy"(default): scanpy score_genes (AddModuleScore-style control subtraction). -"ucell": rank-based AUC (requirespyucell; robust to normalization and sparsity). -"ssgsea": single-sample GSEA (requiresgseapy; slower; publication-grade).Writes raw scores to
adata.obsm["signature_score"]and z-scored (across obs) toadata.obsm["signature_score_z"]. Column names are full path (e.g.Myeloid/Macrophage_Core). Report is stored inadata.uns["signature_score_report"]. The method used is recorded inadata.uns["scoring_method"].- Parameters:
adata (AnnData) – Annotated data. Must have
adata.rawifuse_raw=Trueandmethod="scanpy".signatures_nested (dict, str, or Path) – Two-level nested dict
{category: {subprocess: [genes]}}or path to JSON. Keys like_metaare skipped.method (str) – Scoring backend:
"scanpy"|"ucell"|"ssgsea". Default"scanpy".use_raw (bool) – Use
adata.rawfor expression (default True). Only applies tomethod="scanpy".ctrl_size (int) – Control genes per signature gene (default 50). Only applies to
method="scanpy".n_bins (int) – Expression bins for control matching (default 25). Only applies to
method="scanpy".min_genes (int) – Minimum present genes to score (default 3); sets below this threshold receive NaN.
copy (bool) – If True, copy adata before modifying (default False).
save_path (str, Path, or None) – If set, write adata to this path after scoring (default None).
- Returns:
AnnData with
obsm["signature_score"],obsm["signature_score_z"],uns["signature_score_report"], anduns["scoring_method"]. Same object as input ifcopy=False.- Return type:
AnnData
Gene Set Loaders and Curation#
- sc_tools.tl.load_hallmark(organism='human')[source]#
Load bundled MSigDB Hallmark gene sets.
Returns the 50 Hallmark gene sets as a two-level dict:
{"Hallmark": {"TNFA_SIGNALING_VIA_NFKB": ["ABCA1", ...], ...}}
The
HALLMARK_prefix is stripped from set names so that column names produced byscore_signatureare concise (e.g.Hallmark/HYPOXIAinstead ofHallmark/HALLMARK_HYPOXIA).
- sc_tools.tl.load_msigdb_json(path, category_name=None)[source]#
Load an MSigDB-format JSON file.
MSigDB JSON format: a flat dict where each key is a set name and the value is either a plain list of gene symbols or an object with a
geneSymbolskey (the richer export format).- Parameters:
- Returns:
Two-level nested dict
{category_name: {set_name: [genes]}}.- Return type:
- sc_tools.tl.load_gmt(path, category_name=None)[source]#
Load a GMT (Gene Matrix Transposed) file.
GMT format: tab-separated; each line is:
SET_NAME\tDESCRIPTION\tGENE1\tGENE2\t...
- sc_tools.tl.validate_gene_signatures(signatures, var_names=None, min_genes=3)[source]#
Validate a nested gene signature dict or JSON file.
Checks every leaf gene list and reports coverage, duplicates, and empty sets. Optionally checks presence against a provided gene universe (e.g.
adata.var_names).- Parameters:
signatures (dict, str, or Path) – Two-level nested dict or path to a JSON file containing signatures.
var_names (list[str] or None) – Gene universe (e.g.
list(adata.var_names)). If provided, reportsn_presentandpct_coverage.min_genes (int) – Minimum number of genes required per set (flags sets below threshold).
- Returns:
One row per signature with columns:
signature,n_genes,n_unique,n_duplicates,n_present(if var_names given),n_missing(if var_names given),pct_coverage(if var_names given),status.- Return type:
pd.DataFrame
- sc_tools.tl.merge_gene_signatures(*dicts)[source]#
Combine multiple two-level signature dicts.
Later dicts overwrite earlier ones on key collision at both the category and set-name level. The special
_metakey is skipped.- Parameters:
*dicts (dict) – Two-level nested dicts to merge.
- Returns:
Merged two-level nested dict.
- Return type:
Examples
>>> project = {"Myeloid": {"Macrophage": ["CD68", "CSF1R"]}} >>> hallmark = load_hallmark() >>> combined = merge_gene_signatures(project, hallmark)
- sc_tools.tl.update_gene_symbols(signatures, alias_map)[source]#
Replace deprecated or alias gene symbols in a signature dict.
Does NOT fetch from the internet; the caller provides the alias map (e.g. from an HGNC download or a per-project correction list).
- Parameters:
- Returns:
New nested dict with symbols updated. Original dict is not mutated.
- Return type:
Enrichment Testing#
Group-level enrichment. Optional dependency: pip install sc-tools[geneset].
- sc_tools.tl.run_ora(adata, groupby, gene_set_dict, background=None, min_genes=3, fdr_method='fdr_bh')[source]#
Over-representation analysis (ORA) using Fisher exact test.
For each (group, gene set) pair, tests whether the overlap between the gene set and the genes expressed in that group is greater than expected by chance, relative to the background gene universe.
Multiple testing correction is Benjamini-Hochberg (BH) applied across all tests within each group.
- Parameters:
adata (AnnData) – Annotated data matrix. Must have
adata.obs[groupby].groupby (str) – Column in
adata.obsdefining groups (e.g."leiden").gene_set_dict (dict) – Gene sets as
{category: {name: [genes]}}or{name: [genes]}.background (list[str] or None) – Background gene universe. Defaults to
adata.var_names.min_genes (int) – Minimum overlap required to include a test (default 3).
fdr_method (str) – Multiple testing correction method passed to
statsmodels.stats.multitest.multipletests(default"fdr_bh").
- Returns:
Long-format results with columns:
group,category,gene_set,n_overlap,n_set,n_background,n_group_genes,odds_ratio,p_val,p_adj. Sorted byp_adjascending within each group.- Return type:
pd.DataFrame
- sc_tools.tl.run_gsea_pseudobulk(adata, groupby, gene_set_dict, layer=None, method='prerank', ranking_stat='logfc', min_genes=10, n_permutations=1000)[source]#
Pseudobulk GSEA: aggregate expression per group, rank genes, run fgsea/prerank.
Requires
gseapy(optional dependency). Install with:pip install gseapy
Steps:
Aggregate expression: mean per group → pseudobulk matrix (genes x groups).
Compute ranking statistic (log2FC, z-score, or t-stat) relative to the mean expression across all groups.
Pass ranked gene list to
gseapy.prerankfor each group.Collect results; apply Benjamini-Hochberg FDR across all gene sets per group.
- Parameters:
adata (AnnData) – Annotated data. Must have
adata.obs[groupby].groupby (str) – Column in
adata.obsdefining groups.gene_set_dict (dict) – Gene sets as
{category: {name: [genes]}}or{name: [genes]}.layer (str or None) – Layer to use for expression. Defaults to
adata.X.method (str) – GSEA method:
"prerank"(default) or"fgsea"(also via gseapy).ranking_stat (str) – Statistic used to rank genes:
"logfc"(default),"zscore","tstat".min_genes (int) – Minimum genes in a set after intersecting with ranked genes (default 10).
n_permutations (int) – Number of permutations for enrichment scoring (default 1000).
- Returns:
Long-format results with columns:
group,category,gene_set,NES,p_val,p_adj,lead_edge_genes. Sorted byp_adjascending within each group.- Return type:
pd.DataFrame
Cell-type Deconvolution#
Backend registry: cell2location, tangram, destvi.
Optional dependency: pip install sc-tools[deconvolution].
- sc_tools.tl.deconvolution(spatial_adata, sc_adata=None, *, method='cell2location', celltype_key='celltype', spatial_batch_key='library_id', sc_batch_key=None, reference_profiles=None, n_signature_genes=2000, use_gpu=None, qc_labels=None, method_kwargs=None, cache_dir=None, output_dir=None, output_file=None, logger_instance=None)[source]#
Run cell-type deconvolution on spatial transcriptomics data.
Processes each library (
spatial_batch_key) independently using backed AnnData loading to keep peak memory low.- Parameters:
spatial_adata (
AnnData|str|Path) – Spatial AnnData or path to h5ad file.sc_adata (
AnnData|str|Path|None) – Single-cell reference AnnData or path. Required unless reference_profiles is provided (Cell2location only).method (
str) – Backend name:"cell2location"(default),"tangram","destvi".celltype_key (
str) – Column insc_adata.obswith cell-type labels.spatial_batch_key (
str) – Column in spatialobsfor per-library batching.sc_batch_key (
str|None) – Batch key insc_adata.obs(used for HVG selection).reference_profiles (
DataFrame|Path|None) – Pre-computed reference profiles (DataFrame or path to pickle). Memory optimisation for Cell2location – skips regression training.n_signature_genes (
int) – Number of signature genes for deconvolution.qc_labels (
list[str] |None) – Cell-type labels to exclude from the reference.method_kwargs (
dict|None) – Extra keyword arguments forwarded to the backendrun()method.cache_dir (
str|Path|None) – Directory for caching signature genes and reference profiles.output_dir (
str|Path|None) – Directory for per-library intermediate results.output_file (
str|Path|None) – Path to save the final AnnData with proportions.
- Returns:
The spatial AnnData with
obsm['cell_type_proportions'](n_spots x n_celltypes) andobs['{method}_argmax'](dominant cell type per spot).- Return type:
AnnData
- sc_tools.tl.extract_reference_profiles(sc_adata, celltype_key, genes=None, qc_labels=None, cache_path=None, logger_instance=None)[source]#
Compute mean expression per cell type from scRNA-seq reference.
The resulting DataFrame (genes x cell_types) is ~100x smaller than the full reference and can be passed directly to Cell2location via its
cell_state_dfparameter, skipping regression model training.- Parameters:
sc_adata (
AnnData) – Single-cell reference AnnData.celltype_key (
str) – Column insc_adata.obswith cell-type labels.genes (
list[str] |None) – Subset of genes to include. None keeps all.qc_labels (
list[str] |None) – Cell-type labels to exclude (e.g.["Doublets", "QC_Filtered"]).cache_path (
str|Path|None) – If given, cache the result as a pickle file.
- Returns:
Genes (rows) x cell types (columns) mean expression matrix.
- Return type:
- sc_tools.tl.select_signature_genes(sc_adata, celltype_key, sc_batch_key, n_genes_max, skip_hvg=True, cache_dir=None, force_recompute=False, sc_data_file=None, logger_instance=None)[source]#
Select signature genes for deconvolution.
This function selects genes for deconvolution by combining: 1. Highly variable genes (HVGs) - optional 2. Top marker genes per cell type (always computed)
Results can be cached to avoid recomputation.
- Parameters:
sc_adata (AnnData) – Single-cell reference data
celltype_key (str) – Column name in sc_adata.obs containing cell type annotations
sc_batch_key (str) – Column name in sc_adata.obs containing batch information
n_genes_max (int) – Maximum number of genes to return
skip_hvg (bool) – If True, skip HVG computation and use only marker genes (faster, less memory)
cache_dir (str, optional) – Directory to cache signature genes. If None, caching is disabled.
force_recompute (bool) – If True, force recomputation even if cache exists.
sc_data_file (str, optional) – Path to single-cell data file (for cache key generation).
logger_instance (Logger, optional) – Custom logger instance. If None, uses module logger.
- Returns:
List of signature gene names
- Return type:
Colocalization#
- sc_tools.tl.colocalization.truncated_similarity(score_a, score_b)[source]#
Truncated similarity: score_a * score_b where both > 0, else 0.
- Parameters:
score_a (array-like) – First score vector (e.g. proliferation).
score_b (array-like) – Second score vector (e.g. macrophage).
- Returns:
Same shape as inputs; product where both > 0, else 0.
- Return type:
np.ndarray
- sc_tools.tl.colocalization.pearson_correlation(adata, sig_columns, min_valid_ratio=0.5)[source]#
Compute Pearson correlation matrix between signatures across spots.
- sc_tools.tl.colocalization.morans_i(adata, sig_column, coord_key='spatial', n_perms=1000, n_jobs=1)[source]#
Compute Moran’s I spatial autocorrelation for a signature using squidpy.
Note: squidpy’s spatial_autocorr expects genes in var_names, but signature scores are typically in obs columns. This function temporarily adds the signature as a “pseudo-gene” to compute Moran’s I.
- Parameters:
adata (AnnData) – Annotated data object
sig_column (str) – Signature column name in adata.obs
coord_key (str) – Key in adata.obsm for spatial coordinates (default: ‘spatial’)
n_perms (int) – Number of permutations for p-value calculation (default: 1000)
n_jobs (int) – Number of jobs for parallel processing (default: 1)
- Returns:
Dictionary with ‘I’ (Moran’s I) and ‘pval’ (p-value)
- Return type:
- sc_tools.tl.colocalization.morans_i_batch(adata, sig_columns, n_perms=1000, coord_key='spatial', n_jobs=1)[source]#
Compute Moran’s I for multiple signatures using squidpy.
This function efficiently computes Moran’s I for multiple signatures by temporarily adding them all as “pseudo-genes” and computing in one batch.
- Parameters:
- Returns:
DataFrame with columns ‘Morans_I’ and ‘p_value’
- Return type:
DataFrame
- sc_tools.tl.colocalization.neighborhood_enrichment(adata, sig_column, threshold_low=-1.0, threshold_high=1.0, n_perms=1000)[source]#
Compute thresholded neighborhood enrichment for a signature.
- Parameters:
- Returns:
Neighborhood enrichment score
- Return type: