SCALLOPS ARV-471#

scallops_arv471() serves the drug arm of a genome-scale optical pooled CRISPR screen from Genentech/scallops-manuscript. Cells carry a guide library and are treated with ARV-471 (vepdegestrant), a PROTAC that recruits the CRL4-CRBN E3 ligase to the estrogen receptor and drives its degradation. In-situ sequencing reads each cell’s guide, and a phenotype round stains DNA and the estrogen receptor and counts ESR1, CCND1 and GREB1 transcripts. It is a single-cell screen: the base is over two million cells, each with nine phenotype features. Those nine hand-picked readouts are the targeted-readout counterpart to the broad, roughly 1,278-feature morphology of cp_posh().

A guide that knocks out a gene the drug needs to work rescues the receptor, so its cells keep the estrogen-receptor level of an untreated cell. The genes with that known mechanism are the members of the hijacked ligase, CRBN, DDB1, CUL4A and CUL4B, and ESR1 itself.

Use it if your own data is a single-cell optical pooled screen, especially one with a small targeted phenotype readout rather than a broad morphology profile. No tutorial is built on it; aggregate cells to guides as CRISPR knockouts does before scoring.

import numpy as np
import pandas as pd
import plotly.express as px

import mantispy as mt

This page uses the guide-level aggregate, one median profile per guide, so it stays small.

adata = mt.ds.scallops_arv471(aggregated=True)
adata
AnnData object with n_obs × n_vars = 680 × 9
    obs: 'Metadata_Gene', 'Metadata_sgRNA', 'Metadata_CellCount', 'Metadata_Control_Type', 'Metadata_Control', 'Metadata_Perturbation', 'Metadata_Well', 'Metadata_Perturbation_Type'
    var: 'object', 'feature_group', 'feature', 'channel', 'scale', 'angle', 'gray_levels', 'radial_bin', 'params', 'is_feature'
    uns: 'mantispy'
    layers: None (.X)

The rescue reads out in the estrogen receptor#

Grouping the per-guide estrogen-receptor intensity by gene puts the ligase members and ESR1 beside the non-targeting guides. Knocking out CRBN or DDB1 keeps the receptor high, because the drug can no longer assemble the ligase that degrades it; guides against ESR1 sit at the bottom, since there is no receptor left to stain.

genes = ["CRBN", "DDB1", "CUL4A", "CUL4B", "ESR1", "nontargeting"]
selected = adata[adata.obs["Metadata_Gene"].isin(genes)]
er = np.asarray(selected[:, "Nuclei_Intensity_MedianIntensity_ER"].X).ravel()
df = pd.DataFrame({"gene": selected.obs["Metadata_Gene"].astype(str).to_numpy(), "ER intensity": er})
fig = px.box(
    df,
    x="gene",
    y="ER intensity",
    points="all",
    category_orders={"gene": genes},
    title="Estrogen-receptor intensity per guide",
)
fig.show()