Expression Is Not The Whole Tissue
A cell's transcriptome says what is active. Its neighbors say which microenvironment it occupies. Flatten those coordinates into a table and tumor boundaries, immune exclusion, and stromal niches disappear.
spatialniche builds a k-nearest-neighbor graph directly from spatial coordinates, counts cell-type contacts, and asks whether each pairing exceeds a label-permuted null model.
Make The Null Model Inspectable
The engine is deterministic: seeded permutations, explicit edge counts, and sortable observed, expected, and z-score output. The same function drives the CLI, JSON API, browser workbench, and tests.
The Demo
Twelve cells produce 19 graph edges. Across 250 permutations, tumor-to-tumor enrichment reaches 5.10, stroma-to-stroma reaches 5.06, and every cross-type pair comes back depleted: immune-tumor at -2.40, immune-stroma at -2.93, and stroma-tumor at the most extreme -3.43. Three niches, each clustering with their own kind and avoiding the other two.
Update: the two stronger depletion signals used to be silently missing — the engine only tracked pairs that touched at least once, so complete spatial exclusion (the single strongest form of "these avoid each other") got dropped instead of reported. Fixed to enumerate every possible pair up front, including the ones with zero contacts. Full writeup in the repo README.
The compact JSON input is intentionally smaller than AnnData or SpatialData. Segmentation, batch correction, and biological validation remain upstream responsibilities.
Research Basis
The statistic follows Squidpy's neighborhood-enrichment approach: compare observed cluster contacts with contacts after repeatedly permuting cluster labels.
Read the Squidpy neighborhood enrichment.