spatioloji_s
spatioloji_s is a Python package for image-based spatial transcriptomics analysis, purpose-built for CosMx, MERFISH, and Xenium. It provides an integrated workflow from raw data loading through quality control, processing, spatial analysis, and polygon-native cell-cell communication.
- PyPI: pypi.org/project/spatioloji-s
- GitHub: github.com/gynecoloji/spatioloji_s
- License: MIT
Key Features
- Custom data structure --- A
spatiolojiobject that unifies expression matrices, cell metadata, spatial coordinates, cell polygons, and FOV images under a single master cell index. - Two spatial modes --- Point-based (centroid, fast) and polygon-based (boundary geometry, accurate) analysis with shared interface, gradient, infiltration, and motif APIs.
- Polygon-native CCC --- Cell-cell communication using actual membrane geometry, not centroid distance. Edge-level scoring with exponential distance decay, analytical and permutation significance testing, interface zone stratification, and morphology-aware analysis.
- 80+ visualization functions --- Embedding plots, spatial maps (dot and polygon), neighborhood enrichment, Ripley's K/L, morphology, CCC heatmaps/networks/gradients, motif maps, and more.
- Comprehensive processing --- Normalization, HVG selection (6 methods), PCA/UMAP/tSNE/diffusion maps, Leiden/KMeans/hierarchical clustering, DEG (5 methods), batch correction (ComBat, Harmony, scVI), imputation, and gene set scoring.
Installation
pip install spatioloji-s
Requires Python >= 3.12.
Optional extras:
pip install "spatioloji-s[clustering]" # Leiden (leidenalg + igraph)
pip install "spatioloji-s[reduction]" # UMAP
pip install "spatioloji-s[batch]" # Harmony, ComBat
pip install "spatioloji-s[deg]" # DESeq2, statsmodels
pip install "spatioloji-s[anndata]" # AnnData/scanpy interop
pip install "spatioloji-s[annotation]" # Cell type annotation (CellTypist)
pip install "spatioloji-s[decoupler]" # Pathway scoring
pip install "spatioloji-s[ripley]" # Ripley's K/L
pip install "spatioloji-s[all]" # All of the above
[all] covers every pip-installable analysis feature. The deep-learning
imputation backends are excluded on purpose — scVI and MAGIC are meant to run in
a dedicated environment (pass conda_env= to scvi_impute / magic_impute),
and pulling PyTorch into a default install costs several GB. Install them
in-process with pip install "spatioloji-s[imputation]" if you prefer.
Not sure what you have? spatioloji_s info lists every optional feature and
whether it is available.
Quick Start
import spatioloji_s as sj
# 1. Load data
sp = sj.spatioloji(
expression=expression_df,
cell_meta=cell_meta_df,
spatial_coords=spatial_df,
polygons=polygon_dict,
)
# 2. Process
sj.processing.normalize_total(sp)
sj.processing.log_transform(sp)
sj.processing.pca(sp)
sj.processing.umap(sp)
sj.processing.leiden_clustering(sp)
# 3. Spatial analysis
from spatioloji_s.spatial.polygon import build_buffer_graph, neighborhood_enrichment
graph = build_buffer_graph(sp, buffer_distance=15)
neighborhood_enrichment(sp, graph, "cell_type")
# 4. Cell-cell communication
from spatioloji_s.ccc import CCCConfig, run_ccc
config = CCCConfig(group_col="cell_type", layer="log_normalized")
result = run_ccc(sp, config)
# 5. Visualize
sj.visualization.plot_umap(sp, color_by="cell_type")
sj.visualization.plot_ccc_heatmap(result)
sj.visualization.plot_ccc_network(result)
Module Overview
| Module | Functions | Description |
|---|---|---|
sj.data |
12 | Core spatioloji object, QC, image handling, export |
sj.processing |
44 | Normalization, HVG, dim reduction, clustering, DEG, batch correction, imputation, gene sets |
sj.spatial.point |
30 | Centroid-based: KNN/radius/Delaunay graphs, neighborhoods, Moran's I, Getis-Ord, Ripley's K/L, motifs |
sj.spatial.polygon |
35 | Polygon-based: contact graphs, morphology, boundaries, neighborhoods, interface, gradient, infiltration, motifs |
sj.ccc |
14 | LR database, edge scoring, significance testing, zone/gradient/morphology stratification |
sj.visualization |
74 | Embedding, spatial maps, point/polygon analysis, CCC plots |
Cell-Cell Communication
spatioloji_s uses polygon geometry for biologically accurate CCC inference:
Scoring formula: score(i,j) = sqrt(L_i x R_j) x w_ij
| Signal type | Weight w_ij |
Graph |
|---|---|---|
| Juxtacrine | contact_frac_a x contact_frac_b |
Polygon contact graph |
| Secreted | exp(-distance / sigma) |
Radius graph (default 200 um) |
| ECM | exp(-distance / sigma) |
Radius graph |
Sizing the radius: expected neighbors per cell ≈ π·r²·density, so edge count (memory and run time) grows quadratically with the radius. The 200 µm default suits sparse tissue — at ~6,000 cells/mm² (a typical Xenium tumor section) it already means ~740 neighbors per cell; use ~100 µm there and less for denser tissue (e.g. 75 µm for a reactive lymph node at ~15,000 cells/mm²).
Features:
- 50 built-in LR pairs + CellChatDB (3,234 interactions) support
- Analytical z-score (fast) or permutation significance testing with BH-FDR
- Interface zone comparison --- interface vs. interior enrichment
- Communication gradient --- OLS regression of score vs. signed distance
- Morphology stratification --- CCC by sender cell shape (round/elongated)
- Fitted contact radii --- per-cell-type centroid radius as a fast stand-in for the polygon contact graph (see below)
| Feature | CellChat | COMMOT | SpatialDM | spatioloji_s |
|---|---|---|---|---|
| Single-cell resolution | Partial | Yes | Partial | Yes |
| Polygon contact geometry | No | No | No | Yes |
| Spatial edge scoring | No | OT (centroid) | Moran's I | sqrt(LR) x w |
| Interface zone analysis | No | No | No | Yes |
| Communication gradient | No | No | No | Yes |
| Morphology stratification | No | No | No | Yes |
| Significance testing | Permutation | No | Permutation | Both |
Fitted contact radii (when polygons are missing or too slow)
Juxtacrine signalling needs a contact graph, and polygons are the accurate
source. Where they are unavailable — a sample whose boundaries were never
exported — or too expensive, a centroid radius graph stands in. A single
radius cannot serve a tissue whose cell types differ in size, so
optimize_contact_radius fits one per cell type against the polygon graph:
from spatioloji_s.ccc.radius import build_typed_radius_graph, optimize_contact_radius
fit = optimize_contact_radius(sp_roi, group_col="cell_type")
fit.radius_map # {'Tumor': 12.0, 'Mast': 10.0, ...} — covers every type
fit.per_type # best_radius, jaccard, missed_rate, false_rate, low_evidence
fit.per_pair # the same optimum per cell-type pair
# apply it: directly, or through the pipeline
graph = build_typed_radius_graph(sp, fit.radius_map) # d <= max(r_i, r_j)
result = run_ccc(sp, CCCConfig(juxtacrine_radius_map=fit.radius_map))
What it buys, measured on two Xenium ROIs (ovarian carcinoma, 14k cells; reactive lymph node, 48k cells):
| Ovarian carcinoma | Reactive lymph node | |
|---|---|---|
| fitted radii | 10–12 um (7 types) | 8 um (all 8 types) |
| edge agreement with polygon contact (Jaccard) | 0.63 | 0.79 |
| graph build time | 0.1 s vs 15.3 s (polygon) | 0.2 s vs 66.5 s (polygon) |
The graph is built 100–300x faster, and the tissue difference is the point: in the dense, uniform lymph node one radius serves every cell type, while the morphologically heterogeneous tumour pulls the per-type optima apart and agrees less well with real contact.
Read the fit as a diagnosis, not a free win. Agreement peaks at 0.67–0.80
across tissues even when the radius is fitted against the ground truth, so
the approximation is good, never exact — optimize_contact_radius warns when
your tissue fits poorly. Downstream, juxtacrine scores keep their ranking
(Spearman rho 0.92–0.95 against the polygon result) but significance is
conservative: on these ROIs the radius graph recovered 22/32 and 19/48 of
the polygon graph's significant calls. Use it for speed and for data without
polygons; use the polygon graph when a call list is the deliverable.
Spatial Analysis Highlights
Interface & Gradient
from spatioloji_s.spatial.polygon import identify_interface, compute_gradient
iface = identify_interface(sp, group_col="cell_type", region_a="Tumor", region_b="Stroma")
grad = compute_gradient(sp, iface, genes=["TGFB1", "VIM"])
Morphology
from spatioloji_s.spatial.polygon import compute_morphology, classify_morphology
compute_morphology(sp, store=True) # area, circularity, elongation, solidity, ...
classify_morphology(sp) # round / intermediate / elongated
Spatial Motifs
from spatioloji_s.spatial.polygon import run_motif_pipeline
motifs = run_motif_pipeline(sp, graph, group_col="cell_type", match_builtin="TME")
# Built-in: TLS, tumor buds, immune aggregate, perivascular niche, immune desert
Data Structure
The spatioloji object stores all data aligned to a master cell index:
| Component | Type | Description |
|---|---|---|
sp.expression |
ExpressionMatrix |
Sparse/dense gene x cell matrix (auto-switched) |
sp.cell_meta |
pd.DataFrame |
Per-cell metadata, QC metrics, cluster labels |
sp.gene_meta |
pd.DataFrame |
Per-gene metadata (NegProbe flags, HVG status) |
sp.spatial |
SpatialData |
Global and local x/y coordinates |
sp.polygons |
GeoDataFrame |
Cell boundary polygons (Shapely) |
sp.images |
ImageHandler |
Lazy-loaded FOV images with LRU cache |
sp.embeddings |
dict |
PCA, UMAP, tSNE, diffusion map coordinates |
sp.layers |
dict |
Named expression layers (raw, normalized, scaled) |
Examples
Example notebooks are being reworked alongside the documentation rebuild. The workflows they cover:
- Basic Workflow --- Loading, QC, normalization, clustering, visualization
- Spatial Analysis --- Graphs, neighborhoods, Moran's I, interface detection
- CCC Analysis --- LR scoring, significance, zone comparison, morphology
- Interface Detection --- Tumor-stroma boundary, gradient analysis
- Pathway Scoring --- Gene set analysis via decoupler
Citation
If you use spatioloji_s in your research, please cite it. Use the "Cite this
repository" button on the GitHub repository page (generated from
CITATION.cff), or cite the archived release on Zenodo via the
DOI badge above --- the concept DOI always resolves to the latest version.
License
MIT License --- see LICENSE for details.
Contributing
Contributions are welcome! See CONTRIBUTING.md.
git clone https://github.com/gynecoloji/spatioloji_s.git
cd spatioloji_s
pip install -e ".[test]"
pytest tests/ -v
ruff check src/ tests/ --fix
Commits follow Conventional Commits;
versioning, CHANGELOG.md, and releases are automated with
release-please. See RELEASING.md.
Metadata
Release files for spatioloji-s 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| spatioloji_s-1.0.0.tar.gz | 397.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spatioloji_s-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 748.8 kB
Release files / spatioloji_s-1.0.0.tar.gz
| Download URL | spatioloji_s-1.0.0.tar.gz |
|---|---|
| Size | 397.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6c71bde0799786233a320164d84f4bb016594ff93b5d355c550f367f40d1701f
|
|
BLAKE2b-256 checksum How to use checksums |
de1adb192df5a3fa9a62cf60be115692e676b2ca8bb055ecfd0ad0923b50a357
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / spatioloji_s-1.0.0-py3-none-any.whl
| Download URL | spatioloji_s-1.0.0-py3-none-any.whl |
|---|---|
| Size | 351.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
85c100bd6baa2d1487bdef87a93811475500d9938fa910acac11fc54e86ff619
|
|
BLAKE2b-256 checksum How to use checksums |
67b60c022b2547d2e8708a3733bdee4afbbb057ef9f8ea376c54801fc3ece877
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log