mantispy.tl.bootstrap_features#

mantispy.tl.bootstrap_features(adata, groupby='Metadata_Gene', replicate='Metadata_sgRNA', reference='negcon', control_scope='pooled', condition=None, reference_condition=None, layer=None, key_added='bootstrap', copy=False)[source]#

Test each feature of each gene against the null of resampling the control guides’ cells.

Each guide (each replicate within a groupby group) is summarised by the median of its cells, per feature, and each gene by the median of its guides’ medians. The guide’s null draws one control guide with probability proportional to its cell count, draws as many of that guide’s cells as the tested guide has, with replacement, and takes their median; the gene’s null is the median across its guides’ such nulls. Because each draw comes from one control guide, the null carries the spread between control guides as well as the sampling error of a median.

The null is not simulated but computed exactly: the median of a resample is an order statistic (a binomial tail in the guide’s empirical distribution, and for an even cell count the mean of two consecutive order statistics), the guide null is the cell-count-weighted mixture of its control guides’ such distributions, and the gene null is the order-statistic distribution of its independent guide nulls. The p-values are therefore the ones an infinite bootstrap converges to, with no simulation noise and no lower limit.

The test is Brieflow’s bootstrap for optical pooled screens. Every feature of adata is tested, so subset adata to the features you want tested. The output has the layout of differential_features(), so feature_volcano() and effect_sizes() read it with key=key_added. differential_features() fits wells and refuses cells, while this function works on cells and uses the guide as the replicate, the unit of a pooled screen in which every well holds every guide.

Parameters:
adata AnnData

Cell-level object.

groupby str | list[str] (default: 'Metadata_Gene')

obs column, or columns, naming the gene each cell’s guide targets; each group is tested.

replicate str (default: 'Metadata_sgRNA')

obs column naming the guide, the unit the null resamples.

reference str | None (default: 'negcon')

The control cells: "negcon", or the name of a boolean obs column. Their guides form the null and are not tested. Unused under control_scope="within_perturbation".

control_scope str (default: 'pooled')

Which control cells a guide’s null is drawn from. "pooled" uses every control cell. "within_group" uses the control cells that share the guide’s condition. "reference_group" uses the control cells in reference_condition, so every guide, whatever its condition, is tested against the controls of one condition. "within_perturbation" uses the cells of the same gene in reference_condition, so each gene is its own control, and tests the gene’s other conditions against it. It needs condition among the groupby columns, so that a gene in each condition is its own group.

condition str | None (default: None)

obs column naming a treatment or other condition the scope pins the null to. With it set, a guide’s cells in two conditions are two guides, and so are a control guide’s.

reference_condition str | None (default: None)

The value of condition that "reference_group" and "within_perturbation" draw the null from.

layer str | None (default: None)

Read this layer instead of X.

key_added str (default: 'bootstrap')

Name for the output tables.

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 gene’s median minus the median of its guides’ control pools, in the units of X), pvalue and qvalue (Benjamini-Hochberg over every gene and feature at once). Also writes uns["mantispy"][key_added + "_guides"], the same per guide, with replicate and n_cells columns and its own q-values.

Raises:

ValueError – control_scope is unknown, a scope that needs condition or reference_condition lacks it, "within_perturbation" is used without condition in groupby, the controls in reference_condition are empty, or no guide has a null to draw from.

Notes

The p-value is two-sided, 2 * min(p, 1 - p) with p the probability that a null median exceeds the observed one. It is exact rather than simulated, so it has no lower floor and can be zero when the observed median lies beyond every attainable null median. A null median equal to the observed one counts half, so a feature where most cells share one value, such as a count that is mostly zero, is not called because its median cannot move; Brieflow counts such ties as below. Medians skip missing values, and a feature whose observed median is missing is left NaN.

The null resamples a control guide’s own cells, so it carries that sample’s error on top of the spread between control guides, while a tested guide carries only its own. The test therefore runs conservative when guides barely differ from one another, and near the nominal rate when they differ by more than the error of a guide’s median. Check the rate on your own screen by grouping held-out control guides into pseudo-genes and testing them against the other control guides.