mantispy.tl.differential_features#

mantispy.tl.differential_features(adata, groupby='Metadata_Perturbation', reference='negcon', contrast='reference', block='Metadata_Plate', min_replicates=2, key_added='differential', copy=False)[source]#

Moderated t-test per feature, per group, with wells as the replicates.

Parameters:
adata AnnData

Well-level profiles. Cell-level objects are refused; aggregate them first with aggregate(), since the well is the unit that was randomized.

groupby str (default: 'Metadata_Perturbation')

Column naming the perturbation to test.

reference str | None (default: 'negcon')

Rows to test against: "negcon", or the name of a boolean obs column.

contrast str (default: 'reference')

"reference" tests each group against the reference rows, which gives what the perturbation changed. "rest" tests it against every other perturbation and leaves the reference out, which gives what distinguishes it from the others. This is the marker-gene contrast, and it removes the component all active perturbations share.

"rest" gives sharper mechanism retrieval, but reproduces less well across plates because its comparison set depends on the rest of the screen. On a dose series, where the rest for a compound includes its own other doses, split-half agreement falls further. Use "rest" to tell perturbations apart and "reference" for results to compare between screens.

block str | None (default: 'Metadata_Plate')

Column whose levels enter the model as fixed effects, normally the plate. Without it, plate variance stays in the residual and costs power; blocking recovers it. None fits the contrast alone.

min_replicates int (default: 2)

Groups with fewer wells than this are left unscored.

key_added str (default: 'differential')

Name for the output table.

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], a tidy frame of group, feature, effect (the fitted contrast, in the units of X), t, pvalue and qvalue (Benjamini-Hochberg over the whole table), and uns["mantispy"][key_added + "_prior_df"], the prior degrees of freedom of the empirical Bayes step per group. Large values mean the variances were homogeneous and strongly shrunk.

Raises:
  • ValueError – The object is at cell resolution, contrast is neither "reference" nor "rest", reference selects fewer than min_replicates wells, or no group had enough replicates in blocks shared with what it is compared against.

  • KeyError – obs has no column block to block on.

Notes

With block set, a group is skipped with a warning when none of its plates also holds rows it is compared against, since its treatment and its plate are then confounded. On a confounded null, the unblocked test reports high power and an inflated false positive rate because it detects the plate. None of the four screens packaged with mantispy has such a group.

A feature with a non-finite value in one of the wells a comparison uses is left out of that fit. One infinity would make the batched least squares return NaN for every feature. The mask is computed per group, over the rows that group is fitted on, so a bad well only costs the groups compared against it.

This test is meant for low-replicate screens. Across five configurations, the Mann-Whitney test in effect_size() could not call a feature at three or fewer wells per treatment and was adequate from ten wells up, where this function is a rescaling of Cohen’s d. BBBC021 has three wells per treatment and the full JUMP TARGET-2 has 132.

Calibration depends on the replicate count and the feature distribution, which vary by an order of magnitude between screens. Check the p-values on your own screen with diagnose_testing(), which relabels control wells as pseudo-treatments of the same size and reports the resulting false positive rate.