mantispy.tl.cytotoxicity

Contents

mantispy.tl.cytotoxicity#

mantispy.tl.cytotoxicity(adata, groupby='Metadata_Perturbation', reference='negcon', count_key='Metadata_CellCount', site_key='Metadata_SiteCount', distance_key='hits_row_distance', min_viability=0.7, key_added='cytotoxicity', copy=False)#

Flag perturbations that both lost cells and moved away from the controls.

Parameters:
  • adata (AnnData) – Profiles carrying a per-well cell count and a per-row distance from the controls.

  • groupby (str (default: 'Metadata_Perturbation')) – The column defining a perturbation.

  • reference (str | None (default: 'negcon')) – Rows whose median cell count defines a viability of 1.0.

  • count_key (str (default: 'Metadata_CellCount')) – obs column holding the cell count.

  • site_key (str | None (default: 'Metadata_SiteCount')) – obs column holding the number of fields of view that count covers. Where present, viability compares cells per field, so a well missing a field does not read as cell loss. None compares the counts as they are.

  • distance_key (str (default: 'hits_row_distance')) – obs column holding the per-row distance from the controls, as written by hit_calling(). Its group-level sibling hits_distance is one number repeated over each group’s rows, so the median below would return the value it was handed.

  • min_viability (float (default: 0.7)) – Fraction of the control cell count below which a group counts as having lost cells.

  • key_added (str (default: 'cytotoxicity')) – Name for the outputs.

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

Return type:

AnnData | None

Returns:

None, or the modified copy. Writes uns["mantispy"][key_added] with group, viability, distance, n_obs and suspect, and broadcasts obs[key_added + "_suspect"].

Raises:
  • KeyErrorobs has no count_key or no distance_key.

  • ValueError – The reference rows have no usable cell count to normalize viability against.

Notes

A group is suspect when its viability is below min_viability and its median distance is above that of the controls. Cell loss alone is a phenotype, and a large distance alone is a hit. Together they are suspect because a well with a fifth of its cells has a noisier median and drifts from the controls regardless of the biology. On a synthetic plate with one purely cytotoxic perturbation and its morphology effect removed, that perturbation’s distance was 21.1 against 7.0 for the controls.

Run it whichever feature block a hit was read off. Cell loss moves a profile away from the controls however it is measured, so a screen’s most distant perturbations are partly a cytotoxicity ranking on CellProfiler features and on learned embeddings alike.

Where the two differ is the geometry rather than the ranking. An embedding of the whole field encodes how full the well is, and on every trained model of jump_lite() the cell count lands on the first component, while averaging per-cell measurements over a well leaves it as one signal among many. That costs distances, neighbourhoods and batch correction rather than this flag, and 12. Learned embeddings measures both.

The flag is a diagnostic and does not correct the distances. How much cytotoxicity confounds a screen varies. Over the pki dose series, the rank correlation between phenotype distance and cell loss is +0.79 (p < 1e-8) and the four strongest hits have viabilities of 0.27 to 0.68. Over rohban2017’s ORF overexpression the same correlation is +0.00 (p = 0.95). Measure it on your own screen.

The cell count is a baseline in its own right. Across three bioactivity benchmarks, a model given only the cell count often matched one given the whole Cell Painting profile, because many assays’ actives simply lower it [Seal et al., 2025]. Predicting two cytotoxicity readouts in hepatocytes, the profiles did no better than cell count, plate and well position on LDH release [Ewald et al., 2026].

References

Seal et al. [2025]. Ewald et al. [2026].