Pull everything back: highlights, labels and scores#

Open In Colab

Whatever you make in the viewer — a lasso, a label field, a gene signature score, a gene set — comes back onto your AnnData in one call, viewer.pull(adata), as obs columns and uns['cellucid']['gene_sets']. Nothing waits: the viewer sends each result to the kernel as it is made, so the next cell finds it.

Time: 10 minutes. The same tutorial in R (Seurat and SingleCellExperiment): Pull everything back in R.

# A fresh runtime (Google Colab, a new environment) installs Cellucid once.
import importlib.util

if importlib.util.find_spec("cellucid") is None:
    %pip install --quiet cellucid

The data#

The 2,531-cell mouse pancreas (endocrinogenesis, day 15.5) AnnData checked into the cellucid-python repository: UMAP in obsm['X_umap_2d'], cell types in obs['clusters'], 27,998 genes. In a clone of the repository the cell below finds it; anywhere else (Colab) it downloads it once. Use your own adata in its place.

from pathlib import Path
from urllib.request import urlretrieve

import anndata as ad

import cellucid

NAME = "endocrinogenesis_day15.5_raw.h5ad"
URL = (
    "https://raw.githubusercontent.com/theislab/cellucid-python/main/"
    "docs/user_guide/python_package/f_notebooks_tutorials/datasets/" + NAME
)


def pancreas_h5ad() -> Path:
    """The Pancreas file of a cellucid-python clone, else a copy downloaded once (35 MB)."""
    here = Path.cwd().resolve()
    for base in (here, *here.parents):
        for candidate in (base / "datasets" / NAME, base / "docs/user_guide/python_package/f_notebooks_tutorials/datasets" / NAME):
            if candidate.is_file():
                return candidate
    if not (here / NAME).is_file():
        urlretrieve(URL, here / NAME)
    return here / NAME


DATA_PATH = pancreas_h5ad()
adata = ad.read_h5ad(DATA_PATH)
adata
DATASET_ID = "pancreas-e15-5"

viewer = cellucid.show_anndata(DATA_PATH, dataset_name="Pancreas E15.5", dataset_id=DATASET_ID)

Work in the viewer#

Once the viewer shows the cells, make three things (Highlighting, Fields, Gene signature):

  1. A lasso. In Highlighting choose Lasso, drag around the Beta cells (colour by clusters to find them) and click Confirm.

  2. A label field. In the field list, Copy clusters; rename the copy my_labels (Rename); in its legend rename a category (the pencil) or drag one onto another to merge them.

  3. A score. In Analysis › Gene signature, enter Ins1, Ins2, Iapp and run it: it computes the set’s score for every cell.

Then run the next cell.

for result in viewer.results:
    print(f"{result.kind:12} {result.id:28} {result.meta.get('label') or result.meta.get('key') or result.meta.get('name')}")

viewer.results is the copy the kernel keeps of what the viewer holds. It raises only before the viewer has sent it once (that happens when the dataset opens): run the cell again then.

One call#

pull writes every result onto adata in place and returns what it wrote.

summary = viewer.pull(adata)
summary.columns

Result

obs column

dtype

a highlight group

cellucid_highlight__<group id>

bool

a label field (categorical)

its name (my_labels)

category

a score, a continuous field

its name (<set> score)

float32

a gene set

—

uns['cellucid']['gene_sets'][name]

A field you pushed from this notebook (viewer.push) is left out (adata has it already; include_pushed=True brings it back).

import pandas as pd

written = list(summary.columns)
lasso = [name for name in written if name.startswith("cellucid_highlight__")]
print(adata.obs[written].head())
for column in lasso:
    print(pd.crosstab(adata.obs["clusters"], adata.obs[column]))
print("Gene sets:", summary.gene_sets)

Again and again#

Keep working in the viewer and pull again: the columns an earlier pull wrote are replaced, and those whose result you deleted in the viewer are removed (summary.removed). A column of the same name that Cellucid did not write is never overwritten silently: on_conflict="error" (the default) raises, "replace" overwrites, "suffix" writes <name>_2 beside it.

summary = viewer.pull(adata)
print("written:", list(summary.columns))
print("removed:", summary.removed)

The cells themselves#

viewer.selection is the last selection, with obs names; viewer.subset(adata, which) gives the cells of a highlight group (by id or label) as a new AnnData; which="visible" gives the cells the filters leave visible.

groups = {result.id: result.meta["label"] for result in viewer.results if result.kind == "highlight"}
print(groups)
first = next(iter(groups))
beta_like = viewer.subset(adata, first)
beta_like

Keep it#

The columns are ordinary obs columns; uns['cellucid']['pulled'] records what each one holds.

adata.write_h5ad("pancreas_with_cellucid.h5ad")
viewer.stop()

Next#