SuperAPI AI Directory

alterlab-squidpy-spatial

Data & analytics

View repository

Updated 10-15 19:56

About this skill

Squidpy: Spatial Transcriptomics

Squidpy is the scverse toolkit for spatially-resolved omics, built on AnnData and SpatialData. It answers questions a non-spatial scRNA-seq pipeline cannot: which cell types sit next to which (neighborhood enrichment), how cell-type pairs co-occur across distance (co-occurrence), which genes vary across tissue space (Moran's I / spatially variable genes), and what ligand-receptor signalling is plausible (ligrec). This skill does the spatial analysis; it hands non-spatial QC/clustering to alterlab-scanpy and spot deconvolution to alterlab-scvi-tools.

When to Use This Skill

Use when the request involves:

  • Spatial transcriptomics / spatially-resolved omics on Visium, Visium HD, Xenium, MERFISH/MERSCOPE, or CosMx data.
  • Building a spatial neighbor graph and running neighborhood enrichment, co-occurrence, interaction matrix, Ripley's statistics, or centrality scores.
  • Finding spatially variable genes via Moran's I (spatial_autocorr) or Sepal.
  • Ligand-receptor analysis in a spatial context (ligrec).
  • Identifying spatial niches / tissue domains (calculate_niche_*: neighborhood profile, UTAG, CellCharter, SpatialLeiden).
  • Reading platform output into AnnData/SpatialData and choosing the right coord_type for the platform.

Does NOT Trigger

Request Route to
Non-spatial scRNA-seq QC, normalization, PCA/UMAP, Leiden clustering, marker genes alterlab-scanpy
Spot deconvolution / mapping cell types to Visium spots (destVI, Tangram), probabilistic batch correction/integration alterlab-scvi-tools (see its references/models-spatial.md)
Building/slicing/concatenating .h5ad AnnData objects, layer & obsm wrangling (no spatial analysis) alterlab-anndata
RNA-velocity / trajectory dynamics alterlab-scvelo
Bulk RNA-seq differential expression from a count matrix alterlab-pydeseq2
Raw FASTQ → expression matrix (read alignment/quantification) alterlab-rnaseq-quant
Diversity / ecology statistics on a feature table alterlab-scikit-bio

If the user wants the whole pipeline ("cluster my Xenium data, then find which cell types are neighbors"), run the scanpy clustering step under alterlab-scanpy first, then return here for the spatial graph and enrichment.

The One Decision That Matters: Platform → coord_type

Squidpy's spatial graph depends on the measurement geometry. Getting coord_type wrong silently produces a meaningless graph. (All parameter behavior below is from the squidpy 1.8 sq.gr.spatial_neighbors API.)

Platform Resolution Builder Pair with
Visium spot (multi-cell, hex grid) coord_type="grid", n_neighs=6, n_rings=1..2 deconvolution → alterlab-scvi-tools
Visium HD 2/8/16 µm bins (square grid) coord_type="grid" (square lattice) binning choice up front
Xenium / MERFISH / CosMx single cell coord_type="generic", delaunay=True (or n_neighs=k) direct cell-type analysis
  • coord_type=None auto-picks "grid" only when spatial is in adata.uns with n_neighs=6 (the Visium signature); otherwise it falls back to "generic". Set coord_type explicitly rather than relying on auto-detection.
  • delaunay=True is only used when coord_type="generic"; it builds the graph from a Delaunay triangulation instead of k-nearest spots. n_rings is only used for coord_type="grid".
  • Squidpy 1.8 also exposes the builders directly — sq.gr.spatial_neighbors_grid, _knn, _radius, _delaunay — each with only the parameters that apply to it. Prefer them when you know the geometry: sq.gr.spatial_neighbors(..., delaunay=True) silently ignores n_neighs, which is a common source of "my k did nothing".

Loading Data (pick the reader for the platform)

import squidpy as sq
import scanpy as sc

# Visium (legacy spot data) — squidpy's own reader, returns AnnData
adata = sq.read.visium("path/to/visium_outs/")

# Vizgen MERSCOPE / Nanostring CosMx via squidpy readers
adata = sq.read.vizgen("path/to/merscope/", counts_file="cell_by_gene.csv",
                       meta_file="cell_metadata.csv")
adata = sq.read.nanostring("path/to/cosmx/", counts_file="exprMat_file.csv",
                           meta_file="metadata_file.csv", fov_file="fov_positions.csv")

For Xenium and Visium HD, use the spatialdata-io readers (squidpy has no sq.read.xenium) and operate on a SpatialData object:

from spatialdata_io import xenium, visium_hd, merscope
sdata = xenium("path/to/xenium_outs/")        # 10x Xenium
sdata = visium_hd("path/to/visium_hd_outs/")  # 10x Visium HD
sdata = merscope("path/to/merscope/")         # Vizgen MERSCOPE

spatialdata-io reader names are verified against the spatialdata-io stable API. Squidpy 1.8 accepts SpatialData objects directly; see references/spatialdata_io.md for the SpatialData ↔ AnnData (table) flow.

Standard Spatial Workflow

QC, normalization, HVGs, PCA, neighbors, Leiden, and sc.tl.umap are scanpy steps — run them via alterlab-scanpy. Once you have clusters / cell-type labels, do the spatial part here.

import squidpy as sq

# 1. Build the spatial neighbor graph (choose coord_type per the table above)
sq.gr.spatial_neighbors(adata, coord_type="generic", delaunay=True)   # Xenium/MERFISH
# sq.gr.spatial_neighbors(adata, coord_type="grid", n_neighs=6)       # Visium

# 2. Neighborhood enrichment: which cluster pairs are spatially adjacent?
sq.gr.nhood_enrichment(adata, cluster_key="leiden")
sq.pl.nhood_enrichment(adata, cluster_key="leiden")

# 3. Co-occurrence across distance
sq.gr.co_occurrence(adata, cluster_key="leiden")
sq.pl.co_occurrence(adata, cluster_key="leiden", clusters="0")

# 4. Spatially variable genes via Moran's I
sq.gr.spatial_autocorr(adata, mode="moran")
svgs = adata.uns["moranI"].head(20)   # ranked by Moran's I

# 5. Ligand-receptor interaction (Omnipath-backed)
sq.gr.ligrec(adata, cluster_key="leiden")
# 6. Spatial niches / tissue domains — cluster cells by their neighborhood, not
#    just their own expression. Requires a spatial graph (step 1) already built.
sq.gr.calculate_niche_neighborhood(adata, groups="leiden", resolutions=[0.5, 1.0])
# other flavors: calculate_niche_utag, calculate_niche_cellcharter,
#                calculate_niche_spatialleiden

The generic sq.gr.calculate_niche(..., flavor=...) dispatcher still works but is deprecated for removal in squidpy 1.9 — call the flavor-specific function, whose signature only carries the parameters that flavor actually uses. Niches answer a different question from nhood_enrichment: enrichment asks which labelled cell types sit together on average, niches assign each cell to a recurring tissue microenvironment.

Other graph statistics: sq.gr.interaction_matrix, sq.gr.centrality_scores, sq.gr.ripley (clustering/dispersion vs. CSR), and sq.gr.sepal (an alternative spatially-variable-gene test). Visualize tissue with sq.pl.spatial_scatter (spot/point) or sq.pl.spatial_segment (segmented cells); sq.pl.nhood_enrichment_dotplot and sq.pl.var_by_distance (expression as a function of distance to an anchor) are the other two plots worth knowing. For image features on H&E/IF, the sq.im module (process, segment, calculate_image_features) operates on an ImageContainer.

Helper script — build the graph and run the core statistics in one call:

uv run python skills/bioinformatics/alterlab-squidpy-spatial/scripts/spatial_neighborhood.py \
    clustered.h5ad --platform xenium --cluster-key leiden --out spatial_report.json

See scripts/spatial_neighborhood.py --help. It chooses coord_type from --platform, runs spatial_neighbors, nhood_enrichment, co_occurrence, and spatial_autocorr, and writes a JSON summary (top spatially variable genes + the enrichment z-score matrix) plus the updated .h5ad.

Deeper References

  • references/platform_routing.md — full platform→coord_type decision table, the n_neighs/n_rings/delaunay parameter semantics, and per-platform gotchas.
  • references/analysis_recipes.md — copy-paste recipes for each sq.gr / sq.pl function with the parameters that matter and how to read the outputs.
  • references/spatialdata_io.md — reading Xenium / Visium HD / MERSCOPE into SpatialData and getting the AnnData table squidpy operates on.

Self-Check Before Reporting

  • Did you set coord_type to match the platform (grid for Visium, generic for single-cell)? A wrong graph invalidates every downstream statistic.
  • Did clustering/QC run under alterlab-scanpy (this skill assumes labels exist)?
  • For Visium spot data, did you flag that deconvolution (alterlab-scvi-tools) is needed before cell-type-level claims — spots are multi-cell?
  • Are nhood_enrichment z-scores reported with the permutation context, not as raw counts?

Part of the AlterLab Academic Skills suite.