Spatial: Stereo-seq whole embryo
Stereo-seq captures whole organisms at subcellular bin resolution — the files are big, which is exactly cytome territory. This tutorial uses the MOSTA mouse organogenesis atlas (Chen et al., Cell 2022): one E16.5 sagittal section, 121,767 bins × 28,204 genes, with the paper’s organ annotations.
1. Data (CNGB, direct download, ~4.8 GB)
curl -O https://ftp.cngb.org/pub/SciRAID/stomics/STDS0000058/stomics/E16.5_E1S1.MOSTA.h5adSmaller sections from the same atlas work identically (e.g.
Mouse_brain.h5ad, ~1 GB). The human embryogenesis counterpart (HESTA,
CS12–CS23) is browsed at db.cngb.org/hesta;
its per-section downloads go through the STOmics DB.
2. One conversion, everything indexed
import anndata as adimport cytomeimport piaso
a = ad.read_h5ad("E16.5_E1S1.MOSTA.h5ad") # obsm['spatial'], obs['annotation']ds = cytome.from_anndata(a, output="mosta_e16.cytome")piaso.settings.set_figure_params(style="cell") # one house style across every figure90 seconds, 1.55 GB on disk — and obsm['spatial'] became both the
spatial embedding and the R*-tree coordinate index (all 121,767 bins
queryable by rectangle). From here the h5ad is no longer needed.
3. The embryo
piaso.pl.plotEmbedding(ds, color="annotation", basis="spatial", point_size=0.8, legend_loc="right", legend_ncol=2)
4. Orient the section
Sections land on the chip in arbitrary orientation. piaso.pp rotates the
coordinates about their centroid — directly on the cytome, no AnnData
round-trip.
This section arrives head-up, as section 3 shows, so nothing here needs correcting. To make the effect visible, rotate a copy by 90° and leave the delivered frame alone:
import numpy as np
# work on a copy so the frame every other section plots stays putds.add_embedding("spatial_demo", np.asarray(ds.embeddings["spatial"]).astype(np.float32))piaso.pp.rotateSpatialCoordinates(ds, angle_degrees=90, spatial_key="spatial_demo")
fig, axes = plt.subplots(1, 2, figsize=(15, 7))piaso.pl.plotEmbedding(ds, color="annotation", basis="spatial_original", ax=axes[0], show=False, legend_loc=None)axes[0].set_title("as delivered — already head-up")piaso.pl.plotEmbedding(ds, color="annotation", basis="spatial_demo", ax=axes[1], show=False, legend_loc=None)axes[1].set_title("rotateSpatialCoordinates(angle_degrees=90)")
The rotation is written into the file, and when the rotated embedding is
the spatial one the spatial_coords R*-tree is rebuilt in the same call — so
cells_in_region (below) keeps agreeing with what you plot. spatial_key
resolves the same way plotting’s basis= does; backup_spatial_key= keeps
the original one assignment away; clockwise=True flips the direction.
Rotating in place when you did not need to is the mistake to avoid: every figure downstream inherits it, and there is no warning because nothing is wrong with the coordinates — only with the orientation you chose. Rotate a copy, look at it, and only then decide.
A path works too, if the dataset is not already open:
piaso.pp.rotateSpatialCoordinates("mosta_e16.cytome", angle_degrees=90, spatial_key="spatial")The same call takes an AnnData (rotateSpatialCoordinates(adata, ...),
reading obsm[spatial_key]), where inplace=False returns a rotated copy.
On a cytome inplace=False raises instead: a file cannot pretend to be a
copy.
5. Zoom by coordinates
The indexed rectangle query returns the row indices of the bins in that window. Coordinates and labels both come off the cytome directly, so the zoom needs no AnnData and no matrix read:
# a window around the head, taken from where the Brain bins actually arebrain = ds.embeddings["spatial"][np.asarray(ds.cells["annotation"]) == "Brain"]x0, x1 = np.percentile(brain[:, 0], [1, 99])y0, y1 = np.percentile(brain[:, 1], [1, 99])
sel = ds.cells_in_region(x=(x0, x1), y=(y0, y1))piaso.pl.plotEmbedding(ds, color="annotation", basis="spatial", cell_mask=sel)
That window returns 28,881 bins spanning 19 annotated types — brain filling it, with choroid plexus, meninges, inner ear and the head skeleton around the edge. Deriving the rectangle from the label rather than typing coordinates is what makes it reproducible on a section you have not seen.
If a
registered ssDNA image is available for your section, store it with
ds.add_spatial_image(...) and the same window drives
ds.spatial_images.crop(...) — one rectangle, cells and pixels — with
image=True overlays on every plot above (see the Xenium tutorial).
6. Where to take it
From the same cytome, streaming INFOG → SVD → GDR and marker detection run
in constant memory (see GDR at scale), and the regulon workflow in
cytorete applies directly — the
MOSTA paper’s own organ-identity regulon analysis is a natural exercise.