Skip to content

Add tximport-style transcript-length normalization and pytximport support#451

Open
gitbenlewis wants to merge 7 commits into
scverse:mainfrom
gitbenlewis:main
Open

Add tximport-style transcript-length normalization and pytximport support#451
gitbenlewis wants to merge 7 commits into
scverse:mainfrom
gitbenlewis:main

Conversation

@gitbenlewis

Copy link
Copy Markdown

Reference Issue or PRs

Closes #305.
Closes #359.
Related to #412.

What does your PR implement? Be specific.

This PR adds DESeq2-style transcript-length
normalization and direct compatibility
with AnnData objects produced by
[pytximport](https://github.com/complextissue/
pytximport).

The generic API remains available:

dds = DeseqDataSet(
    counts=estimated_counts,
    metadata=metadata,
    transcript_lengths=average_transcript_lengths,
    design="~condition",
)

Compatible pytximport objects can also be passed
directly:

dds = DeseqDataSet(adata=txi, design="~condition")

The implementation:

  • Rounds unscaled estimated counts, matching
    DESeq2.
  • Stores average transcript lengths in
    layers["avg_tx_length"].
  • Constructs sample-by-gene normalization factors
    containing both transcript-length
    and relative library-size components.
  • Stores the final factors in
    layers["normalization_factors"].
  • Uses the factor matrix throughout dispersion
    fitting, LFC fitting, Wald tests,
    shrinkage, Cook's outlier replacement/refitting,
    and VST.
  • Supports the ratio and poscounts size-factor
    methods.
  • Preserves sparse CSR and CSC counts through
    inference boundaries.
  • Does not add pytximport as a runtime
    dependency.

Length-source precedence is deterministic:

  1. Explicit transcript_lengths
  2. adata.layers["avg_tx_length"]
  3. Compatible pytximport fields

For pytximport input, only unscaled estimated
counts with
adata.uns["counts_from_abundance"] is None are
accepted. Abundance-scaled modes
are rejected to prevent applying transcript-length
correction twice.

Validation

Local verification completed successfully:

  • Full test suite: 104 passed
  • Dense-versus-CSR/CSC parity through dispersion
    fitting, LFCs, Wald tests,
    shrinkage, VST, Cook's distances, replacement,
    and refitting
  • AnnData 0.11.4 compatibility checks passed
  • Ruff, formatting, mypy, pre-commit, and Sphinx
    warnings-as-errors passed

Current limitations

  • Direct pytximport normalization requires an in-
    memory AnnData object.
  • Pytximport's current obsm["length"] matrix has
    no gene-axis labels, so it must
    remain synchronized when genes are subset or
    reordered.
  • Iterative size-factor fitting is not supported
    with transcript-length offsets.
  • VST transformation of an external count matrix
    with separate transcript lengths
    is not currently supported.

Related work and acknowledgements

This implementation began independently with the
generic transcript_lengths
API and was extended with direct pytximport
compatibility following discussion in
#412.

Thank you to @maltekuehl for the earlier draft
implementation and design work in
#412, and to @Zethson for encouraging this work to
be taken forward. The original
tximport, DESeq2, and pytximport work is credited
in the documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plans to add DESeqDataSetFromTximport? Add support for sample-/gene-dependent normalization factors (e.g., length offsets from pytximport)

1 participant