Pull everything back: highlights, labels and scores#
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):
A lasso. In Highlighting choose Lasso, drag around the Beta cells (colour by
clustersto find them) and click Confirm.A label field. In the field list, Copy
clusters; rename the copymy_labels(Rename); in its legend rename a category (the pencil) or drag one onto another to merge them.A score. In Analysis › Gene signature, enter
Ins1, Ins2, Iappand 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 |
|
dtype |
|---|---|---|
a highlight group |
|
|
a label field (categorical) |
its name ( |
|
a score, a continuous field |
its name ( |
|
a gene set |
— |
|
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#
Without a notebook: the web app’s exports, read in one call.
Reference: Round trip API