Xenium In Situ Spatial Transcriptomics Tutorial
Single-cell-resolution spatial analysis from a 10x Xenium output bundle — native cell-feature matrix loading, density-based artefact removal, transcriptional clustering, and squidpy neighborhood enrichment in micron-precise tissue space.
What you'll learn
- Load a Xenium output bundle (cell_feature_matrix.h5 + cells.parquet centroids) into AnnData with the Load Xenium node, which aligns the matrix and centroids for you.
- Filter empty / low-density tissue regions (bubbles, edge artifacts) using the Spatial Density QC workspace.
- Cluster cells transcriptionally and quantify which cell types co-localize in tissue space using squidpy's neighborhood enrichment z-scores.
Prerequisites
- • Oreumics installed (install guide).
- • Familiarity with the standard scRNA-seq workflow — complete the PBMC tutorial first if Normalize → Log1p → PCA → Leiden are unfamiliar.
- • An AnnData object — the standard cells × genes container that Oreumics nodes pass between each other (defined inline in the glossary at the bottom of this page).
- • Data: a Xenium output folder of your own (Oreumics does not bundle a full Xenium bundle). The bundled
sampledata/spatial/xenium_demo.h5adis a small demo for the Spatial Density QC step only — see the Dataset section.
Dataset
Real Xenium output bundle (your data)
A complete Xenium output folder contains cell_feature_matrix.h5 (10x-compatible counts; a MEX directory also works), cells.parquet (centroids + per-cell QC metrics; cells.csv is accepted too), cell_boundaries.parquet, and transcripts.parquet. The Load Xenium node reads the matrix and the centroids together — stitching them by hand is where Xenium analyses usually go wrong, because the row order is not guaranteed to match. Whole-slide morphology images are not loaded.
Bundled demo (Spatial Density QC only)
sampledata/spatial/xenium_demo.h5ad~435 KB — 500 synthetic cells × 300 genes; obs holds x_centroid, y_centroid and a cell_type labelThis file stores coordinates only as obs columns, not in obsm["spatial"]. Spatial Density QC accepts that (its x_col / y_col fallback), so Load H5AD → Spatial Density QC works out of the box for trying the QC workspace. Spatial Neighbors and everything after it require obsm["spatial"], which Load Xenium writes natively for a real bundle (or Add Coordinates from a centroid CSV).
What Xenium is
A 10x Genomics in situ platform. Unlike Visium spots (each spot averages several cells), Xenium resolves individual cells with single-molecule transcript localization. Coordinates are in micrometers (µm). Gene panels are targeted (hundreds of genes rather than the whole transcriptome), with sub-micron spatial precision.
Xenium End-to-End Pipeline
Load the Xenium counts + centroids natively, filter low-density tissue regions, run standard preprocessing & clustering on transcriptomes, then compute squidpy's neighborhood enrichment to ask which cell types are spatially adjacent in the tissue. There is no shipped Xenium template yet — build this chain from the Node Library's Spatial view (the nodes are listed there in this order) or ask the AI Copilot to lay it out.
Pipeline
Point directory_path at the Xenium output folder holding cell_feature_matrix.h5 (preferred) or the MEX directory, plus cells.parquet or cells.csv. The node aligns the matrix rows with the centroids, writes micron coordinates to obsm["spatial"], carries the per-cell metadata from cells.parquet into obs, and records the platform in uns["xenium"]. gex_only drops control probes when feature types are present.
directory_path<your Xenium output folder>library_idblank = folder namegex_onlytruekeep Gene Expression features onlyvar_namesgene_symbolsWhat you'll see
AnnData with cells × panel genes. obsm["spatial"] holds (x, y) micron centroids; obs carries transcript_counts, control probe counts, cell_area and the other cells.parquet columns.
Tip
To try only the Density QC step without a real bundle, use Load H5AD on sampledata/spatial/xenium_demo.h5ad instead — its coordinates live in obs (x_centroid / y_centroid), which the next node accepts as a fallback.
Xenium tissue sections always have artefacts: bubbles, tissue edges, low-density regions where segmentation produced isolated single-cell ghosts. This node divides the tissue into a square grid and keeps only grid cells with strictly more than min_cells cells. Coordinates are read from obsm["spatial"] when present, otherwise from the obs columns named by x_col / y_col. Defaults (grid_size 100 µm, min_cells 2) are sensible Xenium starting points — fine-tune in the workspace modal.
grid_size100100 µm gridmin_cells2keep grids with more than 2 cellsx_colx_centroidfallback obs column when obsm["spatial"] is absenty_coly_centroidWhat you'll see
A subset of cells passes the density filter; obs["spatial_density_qc"] records PASS/FAIL. Open the Spatial Density QC Workspace from the node to inspect the Plotly scatter and tune the two sliders interactively (the preview updates without a backend re-run).
Tip
For dense tissue (cortex, cerebellum), increase min_cells to 5–10. For sparser tissue (lung alveolar regions, tumor edges), keep min_cells at 1–2 to avoid removing biology.
Scale every cell to the same total count. Xenium counts are sparse — a cell typically has tens to a few hundred transcripts vs. thousands in scRNA-seq — so target_sum 10000 still works but the resulting values are heavily inflated. Some users prefer target_sum=1000 or even no scaling for Xenium.
target_sum10000Apply log(x + 1) transformation. With a targeted Xenium panel, Log1p is enough — you can usually skip Highly Variable Genes since the panel is already a curated marker selection.
basenullnatural log (default)Compute principal components on the panel of detected genes. Use n_comps: 30 — a panel of a few hundred genes does not benefit from 50 PCs the way a whole-transcriptome dataset does. use_highly_variable: false because the entire panel is already a focused marker set (and no HVG node ran).
n_comps30use_highly_variablefalseXenium panels are pre-curatedBuild the transcriptional kNN graph in PCA space. Same parameters as scRNA-seq — this graph drives Leiden clustering and (optionally) UMAP. Note: this is the TRANSCRIPTIONAL neighbor graph; the SPATIAL neighbor graph comes later.
n_neighbors15use_repX_pcan_pcs15defaultRun Leiden community detection. Resolution 0.5 is a good default for Xenium because the panel size constrains the number of distinguishable cell states. Cluster labels are stored in obs["leiden"] and used by both Nhood Enrichment and Spatial Visualization.
methodleidenresolution0.5key_addedleidenWhat you'll see
Cell clusters labeled 0, 1, 2… Each cluster corresponds to a tissue cell type — for tumour data: tumor cells, fibroblasts, T cells, macrophages, endothelial cells, etc.
Build a SPATIAL neighbor graph based on physical proximity in tissue. For Xenium keep coord_type at generic (the node default — cells sit at arbitrary positions, not on a grid; "grid" is for Visium). n_neighs: 10 connects each cell to its 10 nearest physical neighbors — typical for Xenium where cells are densely packed. The spatial graph is stored in obsp["spatial_connectivities"] and obsp["spatial_distances"] and is the prerequisite for ALL spatial statistics nodes. This node reads obsm["spatial"] only.
coord_typegenericdefault — "grid" is Visium-onlyn_neighs1010–20 typical for Xenium/MERSCOPEdelaunayfalsespatial_keyspatialTip
The common mistake here is the reverse of the Visium one: copying coord_type=grid from a Visium workflow. Xenium cells are not on a hexagonal lattice — leave the default generic.
Squidpy's permutation test for cluster-pair spatial co-localization. For each permutation, cluster labels are randomly shuffled across cells and the co-adjacency counts re-computed. Z-scores compare observed counts to the permutation distribution. Positive z = these cell types are more often spatially adjacent than chance; negative z = spatial avoidance.
cluster_keyleidenn_perms1000default; 500 for quick explorationseed0What you'll see
Z-score matrix in adata.uns["nhood_enrichment"]. Strong positive z-scores reveal niches: tumor + fibroblast + macrophage pockets, T cell + DC interaction zones, etc. Negative z = spatial separation (e.g., tumor core vs. surrounding stroma).
Render the cells in physical space colored by cluster. spatial_scatter is the canonical Xenium overview plot — use it both as a sanity check after clustering and as a publication figure. size: 0.5–1.0 works for Xenium's dense single-cell layouts; lower if cells overlap visually.
plot_typespatial_scattercolor_byleidensize0.5cmaptab20What you'll see
A scatter of dots in micrometer coordinates colored by cluster. Tissue regions emerge: tumor nests appear as compact same-colored islands; stromal regions show interspersed cell types; immune infiltrate clusters along tumor-stroma boundaries.
Tip
Add a second Spatial Visualization with plot_type: nhood_enrichment to render the z-score heatmap from the previous node. The two plots together (tissue map + heatmap) tell the full neighborhood story. For interactive exploration use the Spatial Viewer node instead.
Expected Results
- • Loaded data: cells × panel genes with obsm[“spatial”] in micrometers and the cells.parquet metadata in obs
- • After Spatial Density QC: most cells retained; edge / bubble cells removed — the exact fraction depends on tissue quality and is shown on the node card
- • Leiden clustering: a handful to a dozen or so clusters at resolution 0.5, corresponding to tissue cell types
- • Spatial neighbor graph: obsp[“spatial_connectivities”] populated; ~10 connections per cell
- • Nhood enrichment: z-score matrix with positive blocks for biologically adjacent cell-type pairs and negative blocks for separated populations
- • Spatial scatter: tissue map showing cell-type-colored cells in micrometer coordinates — spatial domains visible by eye
Tips & Gotchas
coord_type must be "generic" for Xenium
Spatial Neighbors defaults to coord_type="generic". Do not change it to "grid" — grid mode is Visium-only and assumes a regular hexagonal arrangement that Xenium cells do not have.
Skip Highly Variable Genes for Xenium panels
Xenium gene panels are pre-curated marker sets. Running HVG selection on top is unnecessary and can drop low-expression but biologically critical markers. Set use_highly_variable=false in PCA.
Transcript-level data
To overlay individual molecules, add a Load Transcripts node after the loader pointing at transcripts.parquet (min_qscore 20 is the Xenium standard; cap with max_transcripts on large runs). The molecules land in uns["transcripts"] and show up as dot overlays in the Spatial Viewer — adata.X is unchanged.
Sparse counts — lower target_sum if needed
Xenium cells carry far fewer transcripts than droplet scRNA-seq cells. Normalize's default target_sum=10000 still works but produces large normalized values. Some users prefer target_sum=1000 for Xenium (or skip Normalize entirely with smaller panels).
Going Further
- Run Spatial Autocorrelation (Moran's I) to find spatially variable genes within the panel without using cluster labels — see the Visium tutorial Phase 4.
- Group cells by the company they keep with Cellular Neighborhood (Schürch-style microenvironments from the cell-type mix around each cell), or segment contiguous tissue domains with Spatial Domains (BANKSY).
- Explore interactively in the Spatial Viewer (colour by gene or obs; lasso, box or polygon selection; per-group highlight) and persist drawn regions as an obs label with Spatial Grouping for Subset Data.
- Annotate clusters with Auto Annotation or CellTypist Annotation and re-run Nhood Enrichment with cell-type names instead of leiden integers. MERSCOPE and CosMx exports have their own native loaders (Load MERSCOPE, Load CosMx) and then follow this same pipeline.
Glossary
- AnnData
- An annotated data matrix — cells (rows) × genes (columns) with side panels for cell metadata (obs), gene metadata (var), embeddings (obsm), and unstructured results (uns). The standard container that flows between Oreumics nodes.
- obsm["spatial"]
- An n_cells × 2 array of (x, y) coordinates — in micrometers for Xenium. Written by Load Xenium (or Add Coordinates); Spatial Neighbors, Ripley, Co-Occurrence and the Spatial Viewer read only from here.
- In situ spatial transcriptomics
- A class of platforms (Xenium, MERSCOPE, CosMx) that detect transcripts in intact tissue using imaging-based barcoded probes — preserving single-cell resolution and exact spatial location.
- Neighborhood enrichment
- A permutation test (squidpy.gr.nhood_enrichment) that asks: do these two cell types appear next to each other more often than random? Output is a z-score per cluster pair.
- Cell-feature matrix
- The Xenium counts file (cell_feature_matrix.h5), produced by 10x Onboard analysis, that contains per-cell counts of every probe in the panel. Load Xenium reads it together with cells.parquet so the counts and centroids stay aligned.
