mantispy.pp.regress_out

Contents

mantispy.pp.regress_out#

mantispy.pp.regress_out(adata, keys=('Metadata_CellCount',), by='Metadata_Plate', reference=None, key_added=None, copy=False)[source]#

Regress confounders out of every feature, within groups.

Parameters:
  • adata (AnnData) – Object to correct.

  • keys (Sequence[str] (default: ('Metadata_CellCount',))) – obs columns to regress out. Numeric columns enter directly; categorical ones are one-hot encoded with the first level dropped.

  • by (str | None (default: 'Metadata_Plate')) – Fit separately within each group of this column, usually the plate, which scanpy.pp.regress_out() cannot do. None fits one model globally.

  • reference (str | None (default: None)) – Rows to fit on: None for all, "negcon" for Metadata_Control, or the name of a boolean obs column. With a reference, each feature is re-expressed at the reference rows’ mean covariate, and a covariate beyond the range the reference rows span is clipped to it, so no row is corrected by extrapolating the fit. Numeric covariates only.

  • key_added (str | None (default: None)) – Write to layers[key_added] instead of overwriting X.

  • copy (bool (default: False)) – Return a modified copy instead of mutating in place.

Return type:

AnnData | None

Returns:

None, or the modified copy. Writes X or layers[key_added], where each feature is replaced by its residual plus the fitted value at an anchor: the mean over the whole object for a numeric covariate, and the group’s own mean for a categorical one, which keeps the units of the data. With reference, the anchor is the reference rows’ mean within each group.

Raises:
  • KeyError – If any of keys is not an obs column.

  • ValueError – If a categorical covariate has missing values, or reference selects no rows or is given with a categorical covariate.

Notes

Missing and infinite values stay as they are, and a feature holding one is fitted on its finite rows. A group with no more rows than design columns is left uncorrected and logged.

Without a reference, every group is re-expressed at the pooled mean covariate, which removes a density difference between plates when every plate spans that value. A plate that does not span it is corrected by extrapolating its own fit, and a warning names it.

With reference="negcon" the slope is estimated where density varies for technical reasons only. Fitted on every well of a screen whose treatments change density, the slope also carries the treatments’ own effects, so the correction erodes their phenotypes and moves the controls away from the centre a control-referenced normalization put them at. With few control wells per plate, pool them with by=None and a covariate that is comparable between plates, such as the log of each well’s cell count over its plate’s control median.

Whether the cell count is a confounder at all depends on the screen. In the ORF and CRISPR arms of JUMP, whose plate layouts were not randomized, it is largely technical, and the recipe regresses it out [Chandrasekaran et al., 2023]. In a compound screen it is partly a treatment effect (a compound that kills cells is supposed to lower it), so regressing it out removes part of the phenotype along with the nuisance, and the recipe does not. Measure both ways before adopting either; evaluate_correction() takes a covariates argument for exactly this, and cytotoxicity() asks the question directly.

A numeric covariate with a missing or infinite value is dropped from that group’s design and nothing is regressed out for it there, with a warning. A categorical covariate with a missing label is refused instead: the all-zero encoding of a missing category is also the encoding of the level drop_first removed, so those rows would be corrected as the reference level and take every other row with them.