Metadata-Version: 2.4
Name: phd_ms
Version: 2.0.0
Summary: Identifying multiscale tissue domains for spatial transcriptomic data using persistent homology.
Author: Perry Beamer
License-Expression: MIT
Project-URL: Homepage, https://github.com/pzbeamer/phd-ms
Keywords: multiscale domains,spatial transcriptomics,topological data analysis
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.md
Requires-Dist: numpy>=1.23.4
Requires-Dist: scipy>=1.9.1
Requires-Dist: pandas>=1.5
Requires-Dist: matplotlib>=3.5
Requires-Dist: scanpy>=1.9
Requires-Dist: leidenalg>=0.9.0
Requires-Dist: pot>=0.8.0
Requires-Dist: tqdm>=4.60
Requires-Dist: gudhi>=3.8.0
Requires-Dist: mpl_point_clicker
Provides-Extra: igraph
Requires-Dist: igraph; extra == "igraph"
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Dynamic: license-file


# PHD-MS: Persistent Homology for Domains at Multiple Scales

Multiscale domain identification for spatial transcriptomic data.

PHD-MS clusters a tissue at several resolutions, links overlapping clusters
across resolutions into a *cluster graph*, and uses 0-dimensional persistent
homology on that graph to find multiscale domains: groups of clusters that stay
together over a wide range of overlap thresholds. Each domain comes with a
persistence (how robust it is) and a per-spot *coreness* score.

## Installation

    pip install phd_ms

Or from source, in this directory:

    pip install .

## Quick start

We assume a spatially aware embedding (e.g. from GraphST) is stored in `adata.obsm`.

```python
import numpy as np
import scanpy as sc
import phd_ms

adata = sc.read_h5ad('adata_151673.h5ad')

# 1. Leiden clusterings at several resolutions (returns keys, coarse to fine)
keys = phd_ms.pp.leiden(adata, resolutions=np.linspace(0.15, 0.95, 8), use_rep='X_gst')

# 2. Multiscale domains -> adata.obsm['multiscale'], adata.uns['multiscale']
domains = phd_ms.tl.multiscale(adata, keys, index='containment')

# 3. Plots
phd_ms.pl.persistence_diagram(domains)
phd_ms.pl.domains(adata, indices=range(10))   # the 10 most persistent domains
phd_ms.pl.heterogeneity(adata)
```

Other useful entry points:

| | |
|---|---|
| `phd_ms.tl.assign_domains(adata, [0, 2, 5])` | hard clustering from selected domains |
| `phd_ms.bm.benchmark(adata.obs['cluster'], domains, adata.obsm['spatial'])` | Wasserstein / NMI against a ground truth |
| `phd_ms.interactive.point_click(adata)` | click a spot to see the domains containing it |
| `phd_ms.core.ClusterGraph.from_labelings([...])` | build the cluster graph from any clusterings (NumPy only) |

Or run `point_and_click.py` (set `DATA` to your file first) for the clickable interface.

Notebook tutorials are in the `examples` folder: `mouse_brain_v2.ipynb` (finding multiscale
domains) and `visium_benchmark_v2.ipynb` (benchmarking against annotated DLPFC layers).
The 1.x notebooks, with their original outputs, are in `examples/v1`.

## How coreness is defined

Every cluster *r* founds a domain that lives from its birth until it merges
into an older domain (its death). A spot *enters* the domain at the first
overlap threshold at which one of its clusters has joined *r*'s component. Its
coreness is the fraction of the domain's lifetime during which it belongs:

    coreness = (death - entry) / (death - birth),   and 0 if the spot never enters.

## Upgrading from 1.x

The 1.x functions (`phd_ms.tl.preprocess_leiden`, `cluster_filtration`,
`map_multiscale`, ...) still work but emit a `DeprecationWarning`; see
[CHANGELOG.md](CHANGELOG.md) for the new equivalents. Coreness values differ
from 1.x (see the changelog).
