Skip to content
Development
Skill

/spatial-transcriptomics-analysis-squidpy

Squidpy spatial statistics on annotated spatial transcriptomics: build the spatial graph (Visium grid, kNN, radius, Delaunay), neighbourhood enrichment z-scores, distance-resolved co-occurrence, interaction matrix and centrality scores, Ripley's L/F/G point patterns, spatially

BOOST
From plugin
sciagent-skills
382200 skills
Install
$ npx -y skills add jaechang-hits/SciAgent-Skills --skill spatial-transcriptomics-analysis-squidpy --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/spatial-transcriptomics-analysis-squidpy

Context preview

The summary Claude sees to decide when to auto-load this skill.

Squidpy spatial statistics on annotated spatial transcriptomics: build the spatial graph (Visium grid, kNN, radius, Delaunay), neighbourhood enrichment z-scores, distance-resolved co-occurrence, interaction matrix and centrality scores, Ripley's L/F/G point patterns, spatially

SKILL.md

spatial-transcriptomics-analysis-squidpy.SKILL.md
name: "spatial-transcriptomics-analysis-squidpy"
description: "Squidpy spatial statistics on annotated spatial transcriptomics: build the spatial graph (Visium grid, kNN, radius, Delaunay), neighbourhood enrichment z-scores, distance-resolved co-occurrence, interaction matrix and centrality scores, Ripley's L/F/G point patterns, spatially variable genes by Moran's I / Geary's C, and permutation ligand-receptor tests (ligrec, OmniPath). Works on Visium spots and single-cell-resolution data (Xenium, MERFISH, CosMx, Slide-seq). Needs cluster or cell type labels for all but the spatially variable gene step. For an end-to-end ligand-receptor workflow use spatial-transcriptomics-cell-cell-communication."
license: "CC-BY-4.0"

Squidpy Spatial Analysis

Overview

Graph and point-pattern statistics that describe how cell types (or spot clusters) are arranged in tissue and which genes follow a spatial pattern. Each Core API module answers one question and exports a CSV:

| Question | Module | |---|---| | Which cell types sit next to each other more than chance? | 2 Neighbourhood enrichment | | At what distance do two types co-occur? | 3 Co-occurrence | | How many contacts between types; which types are central? | 4 Interaction matrix + centrality | | Is a type clustered, random or dispersed? | 5 Ripley's statistics | | Which genes are spatially patterned? | 6 Spatial autocorrelation | | Which LR pairs are specific to a cluster pair? | 7 Ligand-receptor test |

When to Use

  • Testing which cell types are neighbours more or less often than chance
  • Measuring the distance range over which two cell types stay together
  • Deciding whether a cell type is clustered, random or evenly dispersed in tissue
  • Finding spatially variable genes by Moran's I or Geary's C
  • Running a quick permutation ligand-receptor test between labels
  • Use `spatial-transcriptomics-cell-cell-communication` instead for a multi-method LIANA ligand-receptor ranking with a spatial adjacency filter
  • For tissue regions without labels, use `spatial-domain-detection` first; for cell labels use `spatial-transcriptomics-cell-type-annotation`

Prerequisites

  • **Input**: AnnData with `obsm["spatial"]` and log-normalised `X`
  • **Labels**: a categorical column in `obs` (cell types for single-cell data; spot

clusters or domains for Visium - spots are mixtures, so read results as region-level)

  • **Network**: Module 7 downloads OmniPath interactions
  • **Versions**: squidpy >= 1.6. `sq.gr.spatial_neighbors` is deprecated from 1.7 in favour

of `spatial_neighbors_knn/_radius/_delaunay/_grid`; it still works through 1.8

# Install only what `import` reports missing. `--pypi` keeps the env's scanpy/numba
# stack; a conda add can swap it and bare pip can shadow it.
pixi add --pypi squidpy omnipath scanpy
pixi add --pypi liana        # only for the local-LR-list recipe

Quick Start

import scanpy as sc
import squidpy as sq

adata = sc.read_h5ad("celltype_annotated.h5ad")
adata.obs["cell_type"] = adata.obs["cell_type"].astype("category")
sq.gr.spatial_neighbors(adata, coord_type="generic", n_neighs=10)     # Visium: coord_type="grid", n_rings=1
sq.gr.nhood_enrichment(adata, cluster_key="cell_type", n_perms=1000, seed=0)
sq.pl.nhood_enrichment(adata, cluster_key="cell_type", save="nhood_quick.png")

Core API

Module 1: Build the Spatial Graph

All graph-based modules (2, 4, 6) read `obsp["spatial_connectivities"]`.

import numpy as np
import pandas as pd
import scanpy as sc
import squidpy as sq
import matplotlib.pyplot as plt

adata = sc.read_h5ad("celltype_annotated.h5ad")
CLUSTER_KEY = "cell_type"
adata.obs[CLUSTER_KEY] = adata.obs[CLUSTER_KEY].astype("category")
cats = adata.obs[CLUSTER_KEY].cat.categories

PLATFORM = "single_cell"      # "visium" for Visium spots
if PLATFORM == "visium":
    sq.gr.spatial_neighbors(adata, coord_type="grid", n_neighs=6, n_rings=1)   # hexagonal ring
else:
    sq.gr.spatial_neighbors(adata, coord_type="generic", n_neighs=10)
    # alternatives: delaunay=True (natural neighbours) or radius=30 (same units as coordinates)

deg = np.asarray(adata.obsp["spatial_connectivities"].sum(1)).ravel()
print(f"{adata.n_obs} nodes, median degree {np.median(deg):.0f}, isolated {(deg == 0).sum()}")

Module 2: Neighbourhood Enrichment

Permutation test of how often two labels are neighbours versus shuffled labels:

sq.gr.nhood_enrichment(adata, cluster_key=CLUSTER_KEY, n_perms=1000, seed=0)
z = pd.DataFrame(adata.uns[f"{CLUSTER_KEY}_nhood_enrichment"]["zscore"], index=cats, columns=cats)
z.to_csv("nhood_enrichment_z.csv")
sq.pl.nhood_enrichment(adata, cluster_key=CLUSTER_KEY, method="average", figsize=(8, 8),
                       save="nhood_enrichment.png")

pairs = z.where(np.triu(np.ones(z.shape, bool), k=1)).stack().sort_values()
print("most co-located:\n", pairs.tail(8).round(1))
print("most avoiding:\n", pairs.head(8).round(1))

z > 2: neighbours more often than chance; z < -2: avoid each other. The diagonal measures self-clustering.

Module 3: Co-occurrence Across Distances

Ratio of P(type B | within distance d of type A) to P(type B). Memory grows with cell count squared, so subsample large data:

MAX_CELLS = 20000
sub = adata if adata.n_obs <= MAX_CELLS else sc.pp.subsample(adata, n_obs=MAX_CELLS, random_state=0, copy=True)
occ, interval = sq.gr.co_occurrence(sub, cluster_key=CLUSTER_KEY, interval=50, copy=True)
# occ: n_clusters x n_clusters x n_distances; distance bins = interval[1:]
dist = interval[1:] if len(interval) == occ.shape[2] + 1 else interval
sub_cats = sub.obs[CLUSTER_KEY].cat.categories
rows = [(a, b, d, occ[i, j, k]) for i, a in enumerate(sub_cats) for j, b in enumerate(sub_cats)
        for k, d in enumerate(dist)]
co = pd.DataFrame(rows, columns=["anchor", "other", "distance", "ratio"])
co.to_csv("co_occurrence.csv", index=False)

ANCHOR = sub_cats[0]           # set to the cell type of interest
sq.pl
Read more
Ships withsciagent-skills

Turn your AI coding agent into a life sciences expert — 199 bioinformatics skills for Claude Code covering RNA-seq, single-cell analysis, genomics, proteomics, drug discovery, and more. Boosted BixBench from 65% to 92%. Open source.

Get the whole plugin

Other skills on sciagent-skills.