Skip to main content

spOT-NMF

PyPI version Python versions Documentation Publish to PyPI License: GPL v3 bioRxiv

Gene expression deconvolution via Optimal Transport with proximity-aware niche mapping for spatial transcriptomics.

spOT-NMF is a Python package for reference-free deconvolution of spatial transcriptomics data. It replaces the reconstruction loss of non-negative matrix factorization (NMF) with an entropic optimal-transport (OT) loss, recovering gene programs and their per-spot usage, and adds modules to annotate those programs and to map them into spatial niches.

Abdelkareem, A.O. et al. Briefings in Bioinformatics (accepted, 2026) · Documentation · Issues

Contents

Features

  • OT-NMF deconvolution. Reference-free program discovery with an entropic OT loss, solved on GPU or CPU.
  • Consensus mode. Replicate factorizations clustered into consensus programs, in the style of cNMF.
  • Gene selection. Batch-aware selection of overdispersed (highly variable) genes.
  • Annotation. Pathway enrichment and gene-set overlap for each program.
  • Niche networks. Directed program co-incidence networks partitioned into spatial niches, with tools to calibrate and explore the thresholds.
  • Visualization. Spatial maps of program usage, network plots and connection heatmaps.
  • Two interfaces. A command-line tool and a Python API.

Installation

spOT-NMF requires Python 3.12 or later. PyTorch is not a hard dependency, so you can install the build (CPU or CUDA) that matches your hardware.

Using uv (recommended):

uv venv --python 3.12
source .venv/bin/activate            # Windows: .venv\Scripts\activate

# PyTorch: choose one (see https://pytorch.org for other builds)
uv pip install torch --index-url https://download.pytorch.org/whl/cpu
# uv pip install torch --index-url https://download.pytorch.org/whl/cu121

uv pip install spot-nmf

Using pip:

python -m venv .venv
source .venv/bin/activate            # Windows: .venv\Scripts\activate
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install spot-nmf

From source, for development:

git clone https://github.com/MorrissyLab/spOT-NMF.git
cd spOT-NMF
uv venv --python 3.12
uv pip install torch --index-url https://download.pytorch.org/whl/cpu
uv pip install -e ".[dev]"
uv run pytest -q

Check the installation with spotnmf --help. spOT-NMF uses a GPU when one is available and falls back to the CPU otherwise.

Quick start

Command line

Run the full pipeline (deconvolution, annotation, spatial plots and networks):

spotnmf spotnmf \
  --sample_name SAMPLE1 \
  --adata_path ./data/sample1.h5ad \
  --data_mode h5ad \
  --results_dir ./results \
  --k 5 \
  --genome GRCh38

--data_mode sets how the input is read: h5ad for an AnnData file, visium (the default) for a Space Ranger output directory, or visium_hd for Visium HD.

Each step can also be run on its own:

spotnmf deconvolve --sample_name SAMPLE1 --adata_path ./data/sample1.h5ad --data_mode h5ad --results_dir ./results --k 5
spotnmf plot       --sample_name SAMPLE1 --adata_path ./data/sample1.h5ad --data_mode h5ad --results_dir ./results
spotnmf annotate   --sample_name SAMPLE1 --results_dir ./results --genome GRCh38
spotnmf network    --sample_name SAMPLE1 --results_dir ./results

network reads the per-spot usages written by deconvolve.

Python

import spotnmf as spot

adata = spot.io.read_adata("data/sample1.h5ad", data_mode="h5ad")

results = spot.cli.run_experiment(
    adata_spatial=adata,
    k=5,                    # number of programs
    sample_name="SAMPLE1",
    results_dir="./results",
    genome="GRCh38",
)

Model hyperparameters (lr, h, w, eps, ...) have tuned defaults; override them with a model_params dictionary.

Consensus mode

By default spOT-NMF runs a single factorization. Consensus mode fits n_iter replicates (default 10), clusters them into consensus programs and refits the per-spot usages against the consensus spectra. The refit is non-negative least squares by default, as in cNMF; pass model_params={"refit": "ot"} for an optimal-transport refit. Runtime grows roughly linearly with n_iter, so a GPU is recommended for large datasets.

spotnmf deconvolve --sample_name SAMPLE1 --adata_path ./data/sample1.h5ad \
  --data_mode h5ad --results_dir ./results --k 5 --consensus --n_iter 10

In Python, pass consensus=True to run_experiment.

Niche networks

The niche module turns the per-spot usage matrix into a directed program-to-program network and partitions it into spatial niches with Infomap.

  1. Presence. A program is present at a location where its usage exceeds a threshold. Two detection rules are available:
    • absolute (default): usage > usage_threshold, default 0.05.
    • percentile: usage above each program's own percentile quantile (default 0.90). This is the rule used for Figures 6d and 7d of the paper.
  2. Directed co-incidence. The weight of edge p → t is the fraction of p's locations where t is also present, Laplace-smoothed as (pos + 1) / (n + 2).
  3. Edge criterion. Under spatial independence this fraction equals t's prevalence, so an edge is kept when co-incidence / prevalence(t) >= edge_lift. edge_threshold replaces the lift with an absolute cutoff, as in releases before 0.2.0.
  4. Niches. Infomap clusters the kept edges. A program without a kept edge is isolated and labelled -1.
Parameter (CLI / Python) Default Description
--detection_rule / detection_rule absolute absolute or percentile
--usage_threshold / usage_threshold 0.05 (absolute), 0 (percentile) Presence threshold on usage
--percentile / percentile 0.90 Per-program quantile (percentile rule only)
--edge_lift / edge_lift 2.5 (absolute), 2.0 (percentile) Edge criterion as a fold change over the target's prevalence
--edge_threshold / edge_threshold unset Absolute co-incidence cutoff; overrides the lift
--n_bins / n_bins 1000 Minimum number of locations where the source program is present

Choosing the edge criterion. On the paper's colorectal Visium HD sample (24 µm bins, 50 programs), the absolute rule gives six niches for lifts between 2.00 and 2.75. At 2.5 they match the published niches one to one (ARI 0.841). At 2.0 the vascular programs merge into the immune niche, and at 3.0 a seventh niche appears. Because the lift sets the resolution of the partition, explore it on new data rather than relying on a single value:

import pandas as pd
from spotnmf import niche_networks as nn

usage = pd.read_csv("results/SAMPLE1/topics_per_spot_SAMPLE1.csv", index_col=0)

edges, niches = nn.infer_niches(usage)        # default: usage > 0.05, lift >= 2.5
grid = nn.sweep_niche_parameters(usage, detection_rule="absolute",
                                 lifts=[1.5, 2.0, 2.5, 3.0, 3.5])
grid[["edge_lift", "n_edges", "n_niches", "n_isolated", "mean_abs_asymmetry"]]

The module also provides:

  • permutation_edge_stats: a per-edge empirical p value and Benjamini–Hochberg q value against rigid toroidal shifts of the presence maps (requires array coordinates).
  • spatial_association: threshold-free bivariate Moran's I and Lee's L.
  • Edge direction in the edge table (reciprocal_val, asymmetry, reciprocal_kept). Under the percentile rule both directions of an edge always carry the same weight; only the absolute rule gives the network a direction.

Reproducing Figures 6 and 7 of the paper:

spotnmf network --sample_name SAMPLE1 --results_dir ./results \
  --detection_rule percentile --percentile 0.90 --usage_threshold 0 \
  --edge_threshold 0.199 --n_bins 1000

In Python: nn.plot_network_analysis(results_dir, sample, **nn.PUBLISHED_SETTINGS).

Note on python-igraph. Infomap partitions depend on the igraph version. The paper used python-igraph 0.11.8, and the package pins igraph>=0.11.8,<0.12 (with leidenalg<0.11, whose later releases require igraph 1.x). With python-igraph 1.0.0 the published settings merge the vascular and immune niches on the colorectal sample, giving five niches instead of six. The package warns when another igraph series is installed.

Command-line reference

Command Description
spotnmf Full pipeline: deconvolution, annotation, spatial plots and networks
deconvolve Run OT-NMF and save the factorization
plot Plot spatial program usage
annotate Annotate programs by pathway enrichment and gene-set overlap
network Build and plot niche networks

Run spotnmf <command> --help for the options of each command.

Outputs

File Contents
topics_per_spot_{sample}.csv Program usage per spot
genescores_per_topic_{sample}.csv Gene scores per program
ranked_genescores_{sample}.csv Ranked marker genes per program

Annotation, plotting and network runs also write enrichment and gene-set overlap tables, spatial plots, network plots and the niche partition.

Tutorials

The notebooks run on the bundled example dataset and are stored with their figures, so they render directly on GitHub.

  • Full pipeline: data loading, gene selection, deconvolution, spatial maps, marker genes and validation against ground-truth cell types.
  • Proximity (niche) networks: building the co-occurrence network, detecting niches and plotting the network and connection heatmap.

Reproducing the manuscript

The main branch contains the package. The notebooks and analysis scripts behind the manuscript figures, including the revision analyses, are on the manuscript branch:

git fetch origin
git checkout manuscript

Notebooks are in scripts/manuscript_notebooks/ and the revision analysis scripts in scripts/revision/; the README in each folder lists the data each one needs.

Citation

If you use spOT-NMF, please cite:

Abdelkareem, A.O., Gill, G.S., Manoharan, V.T., Verhey, T.B. & Morrissy, A.S. spOT-NMF: gene expression deconvolution via Optimal Transport with proximity-aware niche mapping for spatial transcriptomics. Briefings in Bioinformatics (accepted, 2026). Preprint: bioRxiv (2025), doi:10.1101/2025.08.02.668292.

@article{abdelkareem2026spotnmf,
  title   = {spOT-NMF: gene expression deconvolution via Optimal Transport with proximity-aware niche mapping for spatial transcriptomics},
  author  = {Abdelkareem, Aly O. and Gill, Gurveer S. and Manoharan, Varsha Thoppey and Verhey, Theodore B. and Morrissy, A. Sorana},
  journal = {Briefings in Bioinformatics},
  year    = {2026},
  note    = {Accepted. Preprint: bioRxiv doi:10.1101/2025.08.02.668292}
}

Support and license

Questions, bug reports and feature requests are welcome on the issue tracker.

spOT-NMF is released under the GNU General Public License v3.0; see LICENSE.

Metadata

Release files for spot-nmf 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spot-nmf 0.2.1
File Size Uploaded
spot_nmf-0.2.1.tar.gz 458.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for spot-nmf 0.2.1
File Interpreter ABI Platform
spot_nmf-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 905.1 kB

Release files / spot_nmf-0.2.1.tar.gz

Download URL spot_nmf-0.2.1.tar.gz
Size 458.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c1fb8c939b513f70ca4363c7bb04a616148878842c3a5db60e4cd7f9fd8adbae
BLAKE2b-256 checksum
How to use checksums
7d6ed7362493a78db62ebe33117559df5526414f4addff17472897db95a43469
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 5, 2026.

Transparency log

Release files / spot_nmf-0.2.1-py3-none-any.whl

Download URL spot_nmf-0.2.1-py3-none-any.whl
Size 446.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ec9cab8bf2618795293db0c5415142509756c8548e5a9ad56e3d99a2f2523a15
BLAKE2b-256 checksum
How to use checksums
0f4b9b228cf65920852924829b7b7e3e6de13f79b73ba006489096dd5ce05983
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 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page