Python API¶
The package is installed from PyPI with pip installm3-sc and imported as import m3.
It has a one-to-one counterpart in the R API — both drive the same
engine and produce identical results.
The public API has two pieces:
- an input facade (
m3.read_h5,m3.read_h5ad,m3.from_anndata,m3.read_matrix,m3.concat, andm3.datasets.*) that turns common single-cell formats into one canonicalDataset; and - the
m3.M3model object, whose methods cover representation learning, donor-level disease prediction, integrated-gradients attribution, and generative augmentation.
A typical run is three lines:
import m3
data = m3.datasets.liu_demo() # 1. load a Dataset
model = m3.M3(data, condition_keys=["cond_group"], # 2. declare column roles
celltype_key="mergedcelltype",
donor_key="sample_id").train() # 3. train
emb = model.embedding(part="bio") # read out
Capability gating¶
m3 is staged, and which read-outs are available depends on how the model was
built and trained. Every trained model unlocks the integration-level
read-outs (embedding, reconstruct,
generate, augment). The donor-level read-outs
(predict_donors, donor_embedding,
attribute) additionally require the donor predictor, which
trains automatically when both donor_key and celltype_key were supplied at
construction (and is skipped when you pass donor_prediction=False). Calling a
read-out before its capability is unlocked raises
m3.M3CapabilityError. Inspect model.capabilities at any time:
Input facade¶
All readers return a Dataset holding raw counts.
m3 normalises internally, so do not pre–log-normalise the input.
m3.read_h5¶
m3.read_h5(*, rna: str | None = None, adt: str | None = None,
atac: str | None = None, metadata: str,
batch: str = "batch0") -> Dataset
Read one batch from paper-format HDF5 count matrices plus a metadata CSV. Pass
the path to each modality you have (at least one of rna / adt / atac) and
the per-cell metadata CSV. Matrix orientation (cells×features vs.
features×cells) is resolved automatically against the feature count.
m3.read_h5ad¶
Read one batch from an AnnData .h5ad. If adata.var has a feature_types
column (Gene Expression / Antibody Capture / Peaks), the matrix is split
into modalities; otherwise the whole matrix is taken as modality.
m3.from_anndata¶
Build one batch from an in-memory AnnData. Splitting by feature_types
behaves as in read_h5ad.
m3.read_matrix¶
Build one batch from in-memory per-modality count arrays.
counts—{modality -> array | sparse}of raw counts, shape[n_cells, n_features]obs— per-cellDataFrame,n_cellsrowsvar—{modality -> list/Index}of feature names, aligned to each matrix's columns
m3.concat¶
Stack single-batch Datasets into one multi-batch Dataset. Features per
modality are harmonised by name intersection (the first batch's order is
kept); a batch missing a whole modality is zero-filled and flagged in
present (mosaic design). Batch labels must be unique across inputs.
m3.datasets.liu_demo¶
Load the built-in Liu et al. COVID-19 demo — a ~30 k-cell (30,534) stratified subsample of the full ~135 k-cell dataset, shipped inside the wheel so every tutorial runs with no downloads.
>>> data = m3.datasets.liu_demo()
>>> print(data)
Dataset(n_cells=30534, batches=['B1', 'B2', 'B3'], modalities=[rna:1000, adt:192])
obs carries the columns batch (B1 / B2 / B3), sample_id, Donor,
cond_group (HC / Severe), Age_interval, and mergedcelltype.
The Dataset container¶
The readers return an immutable, multimodal, multi-batch raw-count container. You normally obtain one from a reader rather than constructing it by hand. Useful read-only members:
| Member | Meaning |
|---|---|
data.modalities |
{name -> scipy.sparse.csr_matrix} of raw counts |
data.obs |
per-cell DataFrame (always contains a batch column) |
data.var |
{name -> pd.Index} of feature names per modality |
data.present |
{name -> bool ndarray} per-cell modality presence (mosaic) |
data.n_cells |
number of cells |
data.modality_names |
list of modality names, e.g. ['rna', 'adt'] |
data.batches |
list of batch labels |
m3.M3¶
m3.M3(
dataset,
*,
condition_keys, # str | list[str] (required)
target_condition: str | None = None,
donor_key: str | None = None,
celltype_key: str | None = None,
batch_key: str = "batch",
held_out: list | None = None,
held_out_samples: list | None = None,
hvg: dict | None = None,
embedding_dim: int = 30,
)
Construct a model and freeze the column-role contract against dataset.obs.
Nothing is trained yet — call train.
| Argument | Role |
|---|---|
condition_keys |
one or more obs columns the model is made aware of; each gets its own latent factor. m3 is condition-aware, so this is required. |
target_condition |
the single condition used for prediction / attribution (defaults to the first of condition_keys). |
donor_key |
obs column identifying a donor/sample. Required for donor-level prediction, attribution, and augment. |
celltype_key |
obs column with cell-type labels. Required for train. |
batch_key |
obs column with the batch label (default "batch", written by every reader). It is the integration / held-out unit, the site the donor adversary removes, and the stratifier for augment(batch=...). |
held_out |
batch label(s) to hold out as an unlabelled query set — their target_condition labels are masked during training and recovered by predict_donors. |
held_out_samples |
donor/sample ID(s) to hold out as the query set instead of a whole batch — may span batches; requires donor_key and is mutually exclusive with held_out. Their target_condition labels are masked during training and recovered by predict_donors. |
hvg |
{modality -> n_top} highly-variable-feature selection, e.g. {"rna": 1000}. Omit to use all features as given. |
embedding_dim |
size of the shared biological latent (default 30). |
After construction, model.contract records the frozen roles, including
model.contract["reference_vocab"] (the condition values observed in the
reference batches).
M3.train¶
M3.train(
*,
max_epochs: int = 300,
lr: float = 1e-5,
batch_size: int = 256,
early_stop_patience: int = 300,
min_delta: float = 0.0,
val_percentage: float = 0.1,
weight_batch_ae: float = 1.0,
balance_batches: bool = True,
donor_prediction: bool | None = None,
donor_predictor: dict | None = None,
) -> M3
Train the integration VAE and, when applicable, the donor predictor on top.
Returns the model (so you can chain .train()). Uses CUDA automatically when
available; otherwise CPU.
donor_prediction—None(default) auto-enables the donor predictor whendonor_keyandcelltype_keyare set. PassFalsefor pure representation learning,Trueto require it.balance_batches— whenTrue(default), the VAE trains on a batch-balanced subset; with no held-out query, downstream steps are automatically rebuilt on the full reference so sparse cell types are not biased out.donor_predictor— a dict tuning the Stage-2 adversarial corrector. Recognised keys (defaults):enc_lr(1e-5),glr(5e-3),n_epochs(30),patient_w(3.0),adv_max(1.0),adv_warmup(10),n_disc(1),hidden(64),min_cells(5).
Read-outs¶
m3.embedding¶
Cell-level latent, row-aligned to cell_metadata.
part selects which factor(s):
"bio"— the intrinsic biological latent plus every condition factor (the integration embedding for clustering / UMAP)"intrinsic"— the intrinsic biological latent only"batch"— the 2-d batch factor (should carry batch signal, not biology)- any name in
condition_keys— that condition's 2-d factor
m3.reconstruct¶
Posterior-mean decode of every cell, returned as {modality -> ndarray}
([n_cells, n_features]). With remove_batch=True (default) the batch factor is
zeroed before decoding, yielding a batch-corrected matrix suitable for
cross-batch differential expression.
m3.predict_donors¶
Donor-level prediction of target_condition. Requires the donor predictor.
Returns one row per donor with columns donor, is_reference,
predicted_label, and a prob_<label> column per class. By default only the
held-out query donors are returned; pass include_reference=True to include
the reference donors too.
m3.donor_embedding¶
The corrector-corrected per-donor vector used for prediction — a DataFrame
indexed by donor, with an is_reference column followed by embedding dimensions
(m3_0, m3_1, …). Join with predict_donors(include_reference=True) for
labels, or feed straight into UMAP for a patient-level map. Requires the donor
predictor.
m3.generate¶
Posterior-resampled synthetic cells (one per reference cell, 1:1), returned as
{modality -> ndarray}. tau scales the sampling temperature. Handy for
noise-augmenting a training set.
m3.augment¶
M3.augment(*, conditions: list, n_donors: list, tau: float = 0.8,
batch: str | None = None, seed: int = 42) -> dict
Synthesise new donors per condition by posterior-resampling real donor
templates. conditions lists the target_condition values to generate and
n_donors how many synthetic donors for each (same length). Returns
{"expression": {modality -> ndarray}, "obs": DataFrame}. When batch is given
(a value of batch_key), templates are drawn only from that batch — useful for
reproducing batch-stratified figures. Requires donor_key + celltype_key.
m3.cell_metadata¶
Row-aligned metadata for embedding /
reconstruct (reference cells first, then query cells).
Attribution¶
m3.attribute¶
M3.attribute(*, reference_labels: list, target_class: int | None = None,
n_steps: int = 50) -> Attribution
End-to-end integrated-gradients attribution of target_condition, returning an
Attribution. Requires the donor predictor.
reference_labels— the healthy/baseline label(s) oftarget_condition(e.g.["HC"]). These set the IG baseline at two levels: the mean expression of reference cells (cell-level IG) and the mean patient vector over reference donors (donor-level IG). Supplying them is what keeps housekeeping / pan–cell-type genes from dominating the ranking.target_class— which non-reference class to attribute toward; defaults to the first class not inreference_labels.n_steps— IG interpolation steps (default50; lower is faster but biased).
The Attribution object¶
Returned by attribute. Holds several views of importance:
| Member | Type | Meaning |
|---|---|---|
attr.genes |
DataFrame(feature, importance) |
raw mean(\|IG\|) over all cells — quick, unfiltered |
attr.celltypes |
DataFrame(celltype, importance) |
per–cell-type importance |
attr.donors |
DataFrame(donor, attribution) or None |
per-donor attribution |
attr.cells |
ndarray |
signed per-cell importance |
attr.attribution |
ndarray [n_cells, n_features] |
full cell × feature IG matrix |
attr.gene_celltype_matrix |
ndarray [n_celltypes, n_features] |
signed per–(cell-type, feature) attribution |
attr.target_label |
str |
the attributed (non-reference) condition |
Attribution.top_genes¶
Attribution.top_genes(n: int = 100, *, min_cells_per_condition: int = 200,
exclude_regex: str | None = <housekeeping>,
modality: str | None = None) -> pd.DataFrame
The publication ranking recipe. Drops cell types where either the reference or
the target condition has fewer than min_cells_per_condition cells, scores each
feature as mean(|gene_celltype_matrix|) across the kept cell types, removes
housekeeping / ribosomal genes by name (exclude_regex, default matches
^MT-, ^RPL, ^RPS, …; pass None to skip), optionally restricts to one
modality ("rna" / "adt" / "atac"), and returns the top-n ranked
DataFrame with columns feature, modality, score, n_celltypes_used.
Attribution.top_celltypes¶
Cell-type importance ranking, filtered to types with at least
min_cells_per_condition cells in both the reference and the target
condition (default 200; set 0 for the raw celltypes table).
Exceptions¶
m3.M3CapabilityError— a read-out was requested before its capability was unlocked (subclassesRuntimeError). The message names what is missing and how to obtain it.
Versioning¶
The installed version is available as m3.__version__.
R ⇄ Python correspondence¶
Every verb has a 1:1 counterpart in the R API. The R package wraps the same engine, so a script translated line-for-line produces identical numbers.
| Python | R |
|---|---|
m3.datasets.liu_demo() |
m3_demo() |
m3.read_h5() / m3.read_h5ad() |
m3_read_h5() / m3_read_h5ad() |
m3.from_anndata() / m3.read_matrix() |
m3_dataset() |
m3.concat() |
m3_concat() |
m3.M3(...).train() |
m3_train(...) |
M3.capabilities |
m3_capabilities() |
M3.embedding() |
m3_embedding() |
M3.cell_metadata |
m3_cell_metadata() |
M3.reconstruct() |
m3_reconstruct() |
M3.predict_donors() |
m3_predict_donors() |
M3.donor_embedding() |
m3_donor_embedding() |
M3.generate() |
m3_generate() |
M3.augment() |
m3_augment() |
M3.attribute() |
m3_attribute() |
Attribution.top_genes() / .top_celltypes() |
m3_top_genes() / m3_top_celltypes() |
| — | m3_shutdown() (R only — releases the engine session) |
See also¶
- R API — the same surface for R (
library(m3)). - Tutorials — Representation learning, Patient prediction, Feature attribution, Data augmentation.