{"slug":"alterlab-squidpy-spatial","title":"alterlab-squidpy-spatial","summary":"Analyzes spatial transcriptomics with squidpy (1.8.x) on AnnData and SpatialData objects, routing platforms correctly: Visium spots use spatial_neighbors(coord_type='grid') and pair with deconvolution, while Xenium/MERFISH single-cell data use coord_type='generic'/Delaunay neighb","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-23T18:56:58.125005Z","repo":{"url":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","stars":68,"forks":13,"license":"MIT","updatedAt":"2026-09-23T13:42:59Z"},"bodyHtml":"<hr>\n<h2>name: alterlab-squidpy-spatial\ndescription: \"Analyzes spatial transcriptomics with squidpy (1.8.x) on AnnData and SpatialData objects, routing platforms correctly: Visium spots use spatial_neighbors(coord_type='grid') and pair with deconvolution, while Xenium/MERFISH single-cell data use coord_type='generic'/Delaunay neighbors and spatialdata-io readers (xenium, visium_hd, merscope). Runs sq.gr.spatial_neighbors, nhood_enrichment, co_occurrence, spatial_autocorr (Moran's I for spatially variable genes), ripley, and ligrec. Use when the user wants spatial transcriptomics, squidpy, Visium/Xenium/MERFISH analysis, neighborhood enrichment, co-occurrence, or spatially variable genes; QC/clustering uses alterlab-scanpy and spot deconvolution (destVI/Tangram) uses alterlab-scvi-tools. Part of the AlterLab Academic Skills suite.\"\nlicense: MIT\nallowed-tools: Read Write Edit Bash(python:<em>) Bash(uv:</em>)\ncompatibility: \"Self-contained — runs under <code>uv run python</code> with squidpy (1.8.x, current 1.8.3 as of 2026-09; needs spatialdata&gt;=0.7.2, spatialdata-plot&gt;=0.3.3, scanpy&gt;=1.9.3, anndata&gt;=0.9, Python&gt;=3.12) installed; no API key or account required.\"\nmetadata:\nskill-author: AlterLab\nversion: \"1.1.0\"\nlast_updated: \"2026-09-23\"</h2>\n<h1>Squidpy: Spatial Transcriptomics</h1>\n<p>Squidpy is the scverse toolkit for spatially-resolved omics, built on AnnData and\nSpatialData. It answers questions a non-spatial scRNA-seq pipeline cannot: <em>which\ncell types sit next to which</em> (neighborhood enrichment), <em>how cell-type pairs\nco-occur across distance</em> (co-occurrence), <em>which genes vary across tissue space</em>\n(Moran's I / spatially variable genes), and <em>what ligand-receptor signalling is\nplausible</em> (ligrec). This skill does the spatial analysis; it hands non-spatial\nQC/clustering to <code>alterlab-scanpy</code> and spot deconvolution to <code>alterlab-scvi-tools</code>.</p>\n<h2>When to Use This Skill</h2>\n<p>Use when the request involves:</p>\n<ul>\n<li>Spatial transcriptomics / spatially-resolved omics on <strong>Visium, Visium HD, Xenium,\nMERFISH/MERSCOPE, or CosMx</strong> data.</li>\n<li>Building a <strong>spatial neighbor graph</strong> and running <strong>neighborhood enrichment</strong>,\n<strong>co-occurrence</strong>, <strong>interaction matrix</strong>, <strong>Ripley's statistics</strong>, or\n<strong>centrality scores</strong>.</li>\n<li>Finding <strong>spatially variable genes</strong> via Moran's I (<code>spatial_autocorr</code>) or Sepal.</li>\n<li><strong>Ligand-receptor</strong> analysis in a spatial context (<code>ligrec</code>).</li>\n<li>Identifying <strong>spatial niches / tissue domains</strong> (<code>calculate_niche_*</code>: neighborhood\nprofile, UTAG, CellCharter, SpatialLeiden).</li>\n<li>Reading platform output into AnnData/SpatialData and choosing the right\n<code>coord_type</code> for the platform.</li>\n</ul>\n<h3>Does NOT Trigger</h3>\n<table>\n<thead>\n<tr>\n<th>Request</th>\n<th>Route to</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Non-spatial scRNA-seq QC, normalization, PCA/UMAP, Leiden clustering, marker genes</td>\n<td><code>alterlab-scanpy</code></td>\n</tr>\n<tr>\n<td>Spot <strong>deconvolution</strong> / mapping cell types to Visium spots (destVI, Tangram), probabilistic batch correction/integration</td>\n<td><code>alterlab-scvi-tools</code> (see its <code>references/models-spatial.md</code>)</td>\n</tr>\n<tr>\n<td>Building/slicing/concatenating <code>.h5ad</code> AnnData objects, layer &amp; obsm wrangling (no spatial analysis)</td>\n<td><code>alterlab-anndata</code></td>\n</tr>\n<tr>\n<td>RNA-velocity / trajectory dynamics</td>\n<td><code>alterlab-scvelo</code></td>\n</tr>\n<tr>\n<td>Bulk RNA-seq <strong>differential expression</strong> from a count matrix</td>\n<td><code>alterlab-pydeseq2</code></td>\n</tr>\n<tr>\n<td>Raw FASTQ → expression matrix (read alignment/quantification)</td>\n<td><code>alterlab-rnaseq-quant</code></td>\n</tr>\n<tr>\n<td>Diversity / ecology statistics on a feature table</td>\n<td><code>alterlab-scikit-bio</code></td>\n</tr>\n</tbody>\n</table>\n<p>If the user wants the <em>whole</em> pipeline (\"cluster my Xenium data, then find which\ncell types are neighbors\"), run the scanpy clustering step under <code>alterlab-scanpy</code>\nfirst, then return here for the spatial graph and enrichment.</p>\n<h2>The One Decision That Matters: Platform → coord_type</h2>\n<p>Squidpy's spatial graph depends on the measurement geometry. Getting <code>coord_type</code>\nwrong silently produces a meaningless graph. (All parameter behavior below is from\nthe squidpy 1.8 <code>sq.gr.spatial_neighbors</code> API.)</p>\n<table>\n<thead>\n<tr>\n<th>Platform</th>\n<th>Resolution</th>\n<th>Builder</th>\n<th>Pair with</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Visium</strong></td>\n<td>spot (multi-cell, hex grid)</td>\n<td><code>coord_type=\"grid\"</code>, <code>n_neighs=6</code>, <code>n_rings=1..2</code></td>\n<td>deconvolution → <code>alterlab-scvi-tools</code></td>\n</tr>\n<tr>\n<td><strong>Visium HD</strong></td>\n<td>2/8/16 µm bins (square grid)</td>\n<td><code>coord_type=\"grid\"</code> (square lattice)</td>\n<td>binning choice up front</td>\n</tr>\n<tr>\n<td><strong>Xenium / MERFISH / CosMx</strong></td>\n<td>single cell</td>\n<td><code>coord_type=\"generic\"</code>, <code>delaunay=True</code> (or <code>n_neighs=k</code>)</td>\n<td>direct cell-type analysis</td>\n</tr>\n</tbody>\n</table>\n<ul>\n<li><code>coord_type=None</code> auto-picks <code>\"grid\"</code> only when <code>spatial</code> is in <code>adata.uns</code> with\n<code>n_neighs=6</code> (the Visium signature); otherwise it falls back to <code>\"generic\"</code>. <strong>Set\n<code>coord_type</code> explicitly</strong> rather than relying on auto-detection.</li>\n<li><code>delaunay=True</code> is only used when <code>coord_type=\"generic\"</code>; it builds the graph from\na Delaunay triangulation instead of k-nearest spots. <code>n_rings</code> is only used for\n<code>coord_type=\"grid\"</code>.</li>\n<li>Squidpy 1.8 also exposes the builders directly — <code>sq.gr.spatial_neighbors_grid</code>,\n<code>_knn</code>, <code>_radius</code>, <code>_delaunay</code> — each with only the parameters that apply to it.\nPrefer them when you know the geometry: <code>sq.gr.spatial_neighbors(..., delaunay=True)</code>\nsilently ignores <code>n_neighs</code>, which is a common source of \"my k did nothing\".</li>\n</ul>\n<h2>Loading Data (pick the reader for the platform)</h2>\n<pre><code>import squidpy as sq\nimport scanpy as sc\n\n# Visium (legacy spot data) — squidpy's own reader, returns AnnData\nadata = sq.read.visium(\"path/to/visium_outs/\")\n\n# Vizgen MERSCOPE / Nanostring CosMx via squidpy readers\nadata = sq.read.vizgen(\"path/to/merscope/\", counts_file=\"cell_by_gene.csv\",\n                       meta_file=\"cell_metadata.csv\")\nadata = sq.read.nanostring(\"path/to/cosmx/\", counts_file=\"exprMat_file.csv\",\n                           meta_file=\"metadata_file.csv\", fov_file=\"fov_positions.csv\")\n</code></pre>\n<p>For <strong>Xenium and Visium HD</strong>, use the <strong><code>spatialdata-io</code></strong> readers (squidpy has no\n<code>sq.read.xenium</code>) and operate on a <code>SpatialData</code> object:</p>\n<pre><code>from spatialdata_io import xenium, visium_hd, merscope\nsdata = xenium(\"path/to/xenium_outs/\")        # 10x Xenium\nsdata = visium_hd(\"path/to/visium_hd_outs/\")  # 10x Visium HD\nsdata = merscope(\"path/to/merscope/\")         # Vizgen MERSCOPE\n</code></pre>\n<p><code>spatialdata-io</code> reader names are verified against the spatialdata-io stable API.\nSquidpy 1.8 accepts SpatialData objects directly; see\n<code>references/spatialdata_io.md</code> for the SpatialData ↔ AnnData (table) flow.</p>\n<h2>Standard Spatial Workflow</h2>\n<p>QC, normalization, HVGs, PCA, neighbors, Leiden, and <code>sc.tl.umap</code> are <strong>scanpy</strong>\nsteps — run them via <code>alterlab-scanpy</code>. Once you have clusters / cell-type labels,\ndo the spatial part here.</p>\n<pre><code>import squidpy as sq\n\n# 1. Build the spatial neighbor graph (choose coord_type per the table above)\nsq.gr.spatial_neighbors(adata, coord_type=\"generic\", delaunay=True)   # Xenium/MERFISH\n# sq.gr.spatial_neighbors(adata, coord_type=\"grid\", n_neighs=6)       # Visium\n\n# 2. Neighborhood enrichment: which cluster pairs are spatially adjacent?\nsq.gr.nhood_enrichment(adata, cluster_key=\"leiden\")\nsq.pl.nhood_enrichment(adata, cluster_key=\"leiden\")\n\n# 3. Co-occurrence across distance\nsq.gr.co_occurrence(adata, cluster_key=\"leiden\")\nsq.pl.co_occurrence(adata, cluster_key=\"leiden\", clusters=\"0\")\n\n# 4. Spatially variable genes via Moran's I\nsq.gr.spatial_autocorr(adata, mode=\"moran\")\nsvgs = adata.uns[\"moranI\"].head(20)   # ranked by Moran's I\n\n# 5. Ligand-receptor interaction (Omnipath-backed)\nsq.gr.ligrec(adata, cluster_key=\"leiden\")\n</code></pre>\n<pre><code># 6. Spatial niches / tissue domains — cluster cells by their neighborhood, not\n#    just their own expression. Requires a spatial graph (step 1) already built.\nsq.gr.calculate_niche_neighborhood(adata, groups=\"leiden\", resolutions=[0.5, 1.0])\n# other flavors: calculate_niche_utag, calculate_niche_cellcharter,\n#                calculate_niche_spatialleiden\n</code></pre>\n<p>The generic <code>sq.gr.calculate_niche(..., flavor=...)</code> dispatcher still works but is\ndeprecated for removal in squidpy 1.9 — call the flavor-specific function, whose\nsignature only carries the parameters that flavor actually uses. Niches answer a\ndifferent question from <code>nhood_enrichment</code>: enrichment asks <em>which labelled cell types\nsit together on average</em>, niches assign <em>each cell to a recurring tissue\nmicroenvironment</em>.</p>\n<p>Other graph statistics: <code>sq.gr.interaction_matrix</code>, <code>sq.gr.centrality_scores</code>,\n<code>sq.gr.ripley</code> (clustering/dispersion vs. CSR), and <code>sq.gr.sepal</code> (an alternative\nspatially-variable-gene test). Visualize tissue with <code>sq.pl.spatial_scatter</code>\n(spot/point) or <code>sq.pl.spatial_segment</code> (segmented cells);\n<code>sq.pl.nhood_enrichment_dotplot</code> and <code>sq.pl.var_by_distance</code> (expression as a function\nof distance to an anchor) are the other two plots worth knowing. For image features on\nH&amp;E/IF, the <code>sq.im</code> module (<code>process</code>, <code>segment</code>, <code>calculate_image_features</code>)\noperates on an <code>ImageContainer</code>.</p>\n<p><strong>Helper script</strong> — build the graph and run the core statistics in one call:</p>\n<pre><code>uv run python skills/bioinformatics/alterlab-squidpy-spatial/scripts/spatial_neighborhood.py \\\n    clustered.h5ad --platform xenium --cluster-key leiden --out spatial_report.json\n</code></pre>\n<p>See <code>scripts/spatial_neighborhood.py --help</code>. It chooses <code>coord_type</code> from\n<code>--platform</code>, runs <code>spatial_neighbors</code>, <code>nhood_enrichment</code>, <code>co_occurrence</code>, and\n<code>spatial_autocorr</code>, and writes a JSON summary (top spatially variable genes + the\nenrichment z-score matrix) plus the updated <code>.h5ad</code>.</p>\n<h2>Deeper References</h2>\n<ul>\n<li><code>references/platform_routing.md</code> — full platform→<code>coord_type</code> decision table, the\n<code>n_neighs</code>/<code>n_rings</code>/<code>delaunay</code> parameter semantics, and per-platform gotchas.</li>\n<li><code>references/analysis_recipes.md</code> — copy-paste recipes for each <code>sq.gr</code> / <code>sq.pl</code>\nfunction with the parameters that matter and how to read the outputs.</li>\n<li><code>references/spatialdata_io.md</code> — reading Xenium / Visium HD / MERSCOPE into\nSpatialData and getting the AnnData <code>table</code> squidpy operates on.</li>\n</ul>\n<h2>Self-Check Before Reporting</h2>\n<ul>\n<li>Did you set <code>coord_type</code> to match the platform (grid for Visium, generic for\nsingle-cell)? A wrong graph invalidates every downstream statistic.</li>\n<li>Did clustering/QC run under <code>alterlab-scanpy</code> (this skill assumes labels exist)?</li>\n<li>For Visium spot data, did you flag that <strong>deconvolution</strong> (<code>alterlab-scvi-tools</code>)\nis needed before cell-type-level claims — spots are multi-cell?</li>\n<li>Are <code>nhood_enrichment</code> z-scores reported with the permutation context, not as raw\ncounts?</li>\n</ul>\n<p>Part of the AlterLab Academic Skills suite.</p>\n","files":[{"path":"evals/evals.json","sizeBytes":5768,"isText":true},{"path":"references/analysis_recipes.md","sizeBytes":3929,"isText":true},{"path":"references/platform_routing.md","sizeBytes":3665,"isText":true},{"path":"references/spatialdata_io.md","sizeBytes":2829,"isText":true},{"path":"scripts/spatial_neighborhood.py","sizeBytes":6558,"isText":true},{"path":"SKILL.md","sizeBytes":10426,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-23T18:57:58.518236Z","sha256":"A118AB14497A18A9019B8C1E1870D8ECA5529F839288A9ED71F8BF10CF12DB72","sizeBytes":14163},"review":null,"source":{"repositoryUrl":"https://github.com/AlterLab-IEU/AlterLab-Academic-Skills","path":"skills/bioinformatics/alterlab-squidpy-spatial","license":"MIT","commit":"e4836c08a20da195a11f30f203a8cf23ec30aa95","subtreeSha":"0FBFDA2BCACB8BBBAE3D36209802FFF399C7BC3DB912FA8DF5EA9A8C45E021E9","lastSyncedAt":"2026-09-23T18:56:52.297238Z"},"reviewedAt":"2026-09-23T18:59:50.260889Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/bioinformatics/alterlab-squidpy-spatial"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alterlab-ieu-alterlab-academic-skills@llmmart"},{"target":"git","command":"git clone https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git"}]}