Skip to content

scdecon.genes

genes

Generic, dataset-agnostic gene-identifier harmonisation utilities.

Real bulk and single-cell datasets frequently label genes in different namespaces -- for example version-suffixed Ensembl IDs (ENSG00000141510.16) in one and HGNC symbols (TP53) in the other. Deconvolution requires a single, consistent gene index, so callers harmonise the namespaces before aligning a signature and bulk matrix (:func:scdecon.deconvolution.align_signature_and_bulk).

This module provides the reusable building blocks for that step. It is deliberately generic: it operates on plain strings and gene-indexed DataFrame objects plus an already-parsed {old_id -> new_id} mapping, and it knows nothing about any particular dataset, file format, or study. Producing the mapping table (e.g. by parsing an annotation GTF) is a dataset-ingestion concern that lives outside the package.

GeneAggregation

Bases: StrEnum

How to combine rows whose identifiers collapse onto the same target label.

When several source identifiers map to one target (a many-to-one mapping, e.g. two Ensembl IDs sharing a symbol), the corresponding rows must be combined.

Attributes:

Name Type Description
SUM

Add the rows -- appropriate for additive, count-like expression values.

MEAN

Average the rows -- appropriate for already-normalised expression values.

GeneMappingCoverage dataclass

GeneMappingCoverage(n_total: int, n_mapped: int)

Coverage of a gene-identifier mapping over a set of identifiers.

A lightweight, first-class QC metric for gene-ID harmonisation: it records how many identifiers a mapping covers so low overlap (a classic cause of unreliable deconvolution) cannot pass unnoticed. Mirrors the summary-object style of :class:~scdecon.preprocessing.QCSummary.

Attributes:

Name Type Description
n_total int

Number of identifiers considered.

n_mapped int

Number of those identifiers present in the mapping.

n_unmapped property

n_unmapped: int

Number of identifiers absent from the mapping.

fraction_mapped property

fraction_mapped: float

Fraction of identifiers covered by the mapping (0.0 if none).

percent_mapped property

percent_mapped: float

Percentage of identifiers covered by the mapping.

__post_init__

__post_init__() -> None

Validate the counts, failing loudly on impossible values.

to_dict

to_dict() -> dict[str, int | float]

Return a serialisable summary of the coverage.

render

render() -> str

Return a human-readable one-line summary.

strip_ensembl_version

strip_ensembl_version(gene_id: str) -> str

Remove a trailing .<version> suffix from an Ensembl gene identifier.

Ensembl / GENCODE identifiers are often version-suffixed (e.g. ENSG00000141510.16); the unversioned stem (ENSG00000141510) is what maps stably across annotation releases. Only a trailing dot followed by an all-digit suffix is stripped; any other identifier (including bare symbols such as HLA-DRB1) is returned unchanged.

Parameters:

Name Type Description Default
gene_id str

A gene identifier.

required

Returns:

Type Description
str

gene_id without its trailing .<digits> version, if present; otherwise gene_id unchanged.

Notes

Identifiers whose trailing segment is not purely numeric are returned unchanged by design. This includes GENCODE pseudoautosomal identifiers such as ENSG00000182162.10_PAR_Y: the _PAR_Y suffix makes the trailing segment non-numeric, so the version is not stripped and the identifier is returned verbatim. This is harmless as long as both sides of a harmonisation apply this same function: such identifiers then remain byte-identical on each side and still match (the chrX and chrY pseudoautosomal copies collapse onto one symbol via :func:relabel_gene_index). Only a mapping built with a different normalisation (e.g. fully base identifiers) would fail to match them.

compute_mapping_coverage

compute_mapping_coverage(identifiers: Iterable[str], mapping: Mapping[str, str]) -> GeneMappingCoverage

Report how many gene identifiers are covered by a mapping.

Parameters:

Name Type Description Default
identifiers Iterable[str]

Gene identifiers to check (e.g. a bulk matrix's gene index).

required
mapping Mapping[str, str]

Mapping keyed by gene identifier.

required

Returns:

Type Description
GeneMappingCoverage

Counts of total vs mapped identifiers, with derived fractions.

relabel_gene_index

relabel_gene_index(frame: DataFrame, mapping: Mapping[str, str], *, aggregate: GeneAggregation = GeneAggregation.SUM, drop_unmapped: bool = True, min_coverage: float = DEFAULT_MIN_COVERAGE) -> pd.DataFrame

Relabel a gene-indexed matrix from one identifier namespace to another.

Each row of frame (indexed by gene identifier) is relabelled through mapping ({current_id -> target_id}). Identifiers absent from mapping are unmapped. Because a mapping may be many-to-one (several current identifiers sharing a target), rows that collapse onto the same target are combined per aggregate. Target labels appear in the order their first contributing row appears in frame (deterministic).

Parameters:

Name Type Description Default
frame DataFrame

Gene-indexed matrix (genes x columns), e.g. a bulk expression matrix.

required
mapping Mapping[str, str]

Mapping from a current gene identifier to its target identifier.

required
aggregate GeneAggregation

How to combine rows that collapse onto the same target label (:class:GeneAggregation); SUM by default.

SUM
drop_unmapped bool

If True (default), silently discard rows whose identifier is not in mapping. If False, raise when any identifier is unmapped.

True
min_coverage float

Minimum fraction of frame's identifiers that must be found in mapping before a low-coverage warning is logged. In [0, 1]. The coverage is always computable via :func:compute_mapping_coverage.

DEFAULT_MIN_COVERAGE

Returns:

Type Description
DataFrame

A new matrix indexed by target identifier (columns unchanged). The index name is preserved from frame.

Raises:

Type Description
ValueError

If min_coverage is outside [0, 1], frame is empty, its index has duplicate identifiers, any column is non-numeric, drop_unmapped is False and some identifier is unmapped, or no identifier could be mapped at all.