Component-wise inference of shared and gene-specific perturbation response.
A perturbation's transcriptional response in a cell line splits into two parts:
z_cp = beta_cp * u_c + r_cp
for perturbation p in cell line c. u_c is the cell line's shared response axis, the
direction almost every perturbation moves along. beta_cp is how far along it p goes in
that cell, a single number. r_cp is the remainder, specific to that perturbation in that
cell.
Averaging the two components over the cell lines where p was measured gives beta_p and
g_p, the cross-cell quantities the estimators below transfer into a new cell line.
The split is useful because the two parts transfer differently between cell lines. The axis is a property of the cell and has to be estimated where you are predicting. The off-axis part is a property of the perturbation and can be carried over from other cell lines where it was measured.
pip install -e .Requires numpy and scikit-learn.
from compass import CompassH
model = CompassH(donor_beta, donor_residual, embedding)
model.fit(measured_responses, measured_perturbations)
predictions = model.predict(["TP53", "MYC"])measured_responses is (n_measured, n_genes), expressed as differences from your control
mean, one row per perturbation you have profiled in the target cell line.
The three differ only in where beta_p and g_p come from, so the choice follows from
what data you have, not from which is best.
| uses | when | |
|---|---|---|
CompassX |
other cell lines only | p measured elsewhere; few perturbations profiled in the target |
CompassN |
neighbours within the target | many perturbations profiled; p measured nowhere else |
CompassH |
both | you have both |
CompassX is nearly independent of how many perturbations you have profiled, since the
target cell contributes only its shared axis. CompassN needs no other cell lines but
cannot separate a perturbation from its neighbours, which limits how well it distinguishes
similar perturbations. CompassH takes each component from whichever source recovers it
better.
python examples/quickstart.pyRuns in seconds on synthetic data, no downloads. It shows the API, the input shapes, and how to build the donor components from several cell lines.
CompassN and CompassH need a per-perturbation embedding; any will do, and the paper
uses STRING. Neighbours are found by cosine distance among the perturbations you have
measured.
Donor components must be decomposed per cell line and then averaged across cell lines,
not the other way round. Averaging responses before decomposing mixes axes that point in
different directions. examples/quickstart.py shows the correct order.