Skip to main content

Bayesian normalizations for RNA-seq

Project description

rafinat

Dirichlet-Multinomial posterior normalizations for single-cell RNA-seq count data.

Three count normalizations derived from a Dirichlet-Multinomial (Liouville) posterior view of the latent expression rates.

Each is a scikit-learn-style transformer: you instantiate it with its operating point, then .fit / .transform / .fit_transform a count matrix — so it drops into a preprocessing pipeline.

transformer what it is default operating point
compost the DM-posterior generalisation of CLR p=0 digamma log-corner, top-2/3-trimmed per-cell centering
hellnorm the Hellinger / Fisher-Rao spherical (sqrt-CLR) normalization top-2/3-trimmed per-cell sqrt reference
rafinat the complete Fisher-Rao Liouville embedding composition arc length (sharp r_snr) + radial cell-size coordinate, beta="total"

All take a dense (genes, cells) array of raw counts (genes = features/rows, cells = samples/columns) and return (genes, cells) (rafinat returns (genes + 1, cells) — the extra row is the cell-size scale coordinate). compost and hellnorm are pure numpy/scipy (CPU). rafinat uses the optional liudist package (Fisher-Rao geometry of Liouville laws; pulls in JAX, and benefits from a GPU JAX build) — installed via the [rafinat] extra.

import numpy as np, rafinat
X = np.random.poisson(0.5, size=(2000, 500)).astype(float)   # genes x cells

Z = rafinat.compost().fit_transform(X)      # CLR-dominating default (compost p=0, trimmed)
H = rafinat.hellnorm().fit_transform(X)     # Hellnorm, trimmed reference
R = rafinat.rafinat().fit_transform(X)      # rafinat (complete); (genes + 1) x cells

fit / transform

fit estimates and freezes the two data-driven pieces — the concentration r and the per-cell reference level beta — and transform re-applies that frozen normalization. Because beta is per-cell, transform expects a matrix of the same (genes, cells) shape it was fitted on (re-fit for a different gene/cell set). fit_transform(X) is the one-shot form.

tr = rafinat.compost(p=0.5).fit(X)     # estimate & freeze tr.r_ and tr.beta_
Z  = tr.transform(X)                    # apply; == tr.fit_transform(X)

from sklearn.pipeline import Pipeline   # optional — also works without scikit-learn installed
pipe = Pipeline([("normalize", rafinat.compost())])
Z = pipe.fit_transform(X)

scikit-learn is an optional extra: if installed, the transformers inherit BaseEstimator / TransformerMixin (full Pipeline / clone / get_params support); otherwise a light built-in shim provides the same fit / transform / fit_transform / get_params API.

Choosing the operating point

The benchmark-winning defaults are baked in, but every knob is a constructor argument:

rafinat.compost(p=0.0)               # default: digamma log-corner, trimmed centering
rafinat.compost(p=0.5)               # posterior sqrt mean (order-1/2 power-mean cell size)
rafinat.compost(trim=0.0)            # ordinary (non-trimmed) per-cell centering
rafinat.compost(r="mle")             # pooled DM-MLE concentration instead of the CLR-matched r
rafinat.compost(zscore=True)         # + per-gene z-score (the optional '->Z' standardization)

rafinat.hellnorm(reference="uniform")     # classic log-map references: uniform / extrinsic / frechet
rafinat.hellnorm(trim=0.5)                # lighter top-trim

rafinat.rafinat(beta="atop10")       # simulation-leaning cell-size estimator
rafinat.rafinat(r_comp="mle")        # smoother directional concentration
rafinat.rafinat(zcomp=True)          # + per-gene z-score of the composition rows ('-> coordZ')

Each of these returns a transformer; call .fit_transform(X) (or .fit(X) then .transform(X)) on it. After fitting, the estimated values are exposed as fitted attributes (trailing underscore): compost.r_ / compost.beta_, rafinat.r_comp_ / rafinat.ref_ / rafinat.C_ / rafinat.beta_.

The optional per-gene standardization (compost(zscore=True), rafinat(zcomp=True)) is disabled by default, matching the benchmark's shipped defaults.

Install

pip install rafinat                # compost / hellnorm (numpy + scipy only)
pip install "rafinat[rafinat]"     # + the rafinat() method — adds the liudist backend (pulls in JAX)

compost and hellnorm need only numpy/scipy; the heavy liudist + JAX stack is pulled in only by the [rafinat] extra, i.e. only if you use the rafinat() method.

Method provenance

  • compostdigamma(r + x) with r = 1/(4·alpha) + 1/2 (so digamma(r+x) ≈ log(x + 1/(4·alpha))), minus a per-cell location estimated on the low-expression bulk (the top high-expression genes — the biologically variable ones — are dropped from the cell-size estimate).
  • hellnormsqrt(x / sum x) minus a top-trimmed per-cell mean (the sqrt-geometry analog of compost's trimmed centering). Dominates the classic uniform/extrinsic/Frechet references.
  • rafinat — the Fisher-Rao Liouville distance factors as d² = d_composition² + C·(d ln β)²; rafinat realises it as the stack [ composition arc length (depth-normalized, sharp r_snr) ; sqrt(C)·ln(β_c) ], with the radial weight C = genes · r_mle decoupled from the (sharp) directional concentration so the cell-size axis stays alive at a parameter-free weight.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rafinat-0.2.0.tar.gz (28.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rafinat-0.2.0-py3-none-any.whl (27.6 kB view details)

Uploaded Python 3

File details

Details for the file rafinat-0.2.0.tar.gz.

File metadata

  • Download URL: rafinat-0.2.0.tar.gz
  • Upload date:
  • Size: 28.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for rafinat-0.2.0.tar.gz
Algorithm Hash digest
SHA256 26e84149078933ee484aa04c3b9a4c17bea0aaa9ec859d89338c43d38d8d1b1b
MD5 642b2b9dba9109eb35df5c31146f6078
BLAKE2b-256 c98cbe43e4a2cd7099721d14142adcb070fb0463b7625c246ca0eb2419ddec9e

See more details on using hashes here.

File details

Details for the file rafinat-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: rafinat-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 27.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for rafinat-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ff7f7e30cd54ec53596dd8ed48a76c36ea9f119854924ee9099b771cadaad394
MD5 fbffc314129f0952025b3f55051bbe0e
BLAKE2b-256 f3dcecd4b10052519d7a2d5529f565da1d6928cb751165dab4066df93d7855b2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page