Metadata-Version: 2.4
Name: destriper
Version: 0.1.0
Summary: Estimate and correct row/column multiplicative stripe artifacts in high-resolution spatial transcriptomics count data.
Project-URL: Homepage, https://github.com/paolamalsot/destriper
Project-URL: Source, https://github.com/paolamalsot/destriper
Author: Paola Malsot
License: MIT
License-File: LICENSE
Keywords: destriping,glm,negative-binomial,spatial-transcriptomics,visium-hd
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Requires-Python: >=3.11
Requires-Dist: glum==3.1.2
Requires-Dist: joblib
Requires-Dist: numpy<2
Requires-Dist: pandas<3
Requires-Dist: scikit-learn
Requires-Dist: scipy<1.15,>=1.9
Requires-Dist: tabmat==4.2.1
Requires-Dist: xxhash<4,>=3.6
Provides-Extra: anndata
Requires-Dist: anndata; extra == 'anndata'
Provides-Extra: dev
Requires-Dist: anndata; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Description-Content-Type: text/markdown

<h1><img src="docs/icon/icon.png" alt="D" width="45" align="bottom">estriper</h1>

**Destriper** corrects striping artefacts in
high-resolution spatial transcriptomics count data (e.g. Visium HD 2 µm bins).

Under the hood, the method fits a negative binomial model to each nucleic bin $ij$ total counts $k_{ij}$: $k_{ij} \sim NB(\text{mean} = c_p h_i w_j, \text{dispersion} = \theta)$, where $h_i$, $w_j$ are row/column stripe
factors, and $c_p$ a per-nucleus concentration [counts/bin]. The fitted stripe factors are then
applied to **all** bins to produce destriped counts.

<p align="center">
  <img src="docs/method_scheme.png" alt="Method overview" width="700">
</p>

* The underlying mean-variance relationship reads $\text{var} = \mu + \theta\mu^2$.
* Fitted stripe factors $h_i$ and $w_j$ equal 1 on average.

## Cite

To learn more about the method, or to cite, use:

> Paola Malsot, Malte Londschien, Valentina Boeva, Gunnar Rätsch, Striping artifact removal in VisiumHD data through nuclear counts modeling, *Bioinformatics*, Volume 42, Issue Supplement_1, July 2026, btag306, https://doi.org/10.1093/bioinformatics/btag306

## Install

```bash
pip install destriper            # low-level interface
pip install "destriper[anndata]" # + AnnData interface
```

## Tutorial

See the [tutorial notebook](docs/tutorial.ipynb) for an example on Visium HD mouse brain data.

## Usage

### Low-level interface

```python
import destriper as ds

# nucleus bins only; counts are per-bin TOTAL counts
result = ds.fit(
    counts,          # 1-D int array
    row_indices,     # 1-D int array (array-grid row)
    column_indices,  # 1-D int array (array-grid col)
    nucl_labels,     # 1-D array of nucleus ids (non-null)
    cv="spatial",    # "spatial" | "default" | int (KFold) | per-bin group array
    max_iter_theta=5,
    max_iter=100_000,
)

result.row_factors        # pandas Series h_i
result.col_factors        # pandas Series w_j
result.nucl_concentration # pandas Series c_p
result.dispersion         # fitted NB theta

# apply to ALL bins (nucleus bins -> quantile matching, others -> division)
corrected_totals = ds.destripe_tot_counts(
    tot_counts, row_indices, column_indices, nucl_labels, result
)

# rescale the sparse count matrix to the corrected totals
corrected_matrix, achieved_totals = ds.rescale(count_matrix, corrected_totals)
```

`achieved_totals` can differ from `corrected_totals` on **originally-empty bins**:
a zero row cannot be rescaled to a positive target, so it stays zero. Both are
returned so the discrepancy is explicit.

### Anndata interface

```python
import destriper as ds

result = ds.fit_adata(
    adata,
    nucl_key="nucleus_id",
    count_key="total_counts",   # None -> compute from adata.X
    row_key="array_row",
    col_key="array_col",
)

ds.destripe_adata(adata, result, source_layer=None, target_layer="destriped")
# writes adata.layers["destriped"], adata.obs["ds_destripe_factor"], adata.obs["ds_corrected_counts"]
# and adata.uns["destriper"]["result"]
```

Fitting uses only bins with a non-null `nucl_key`; unlabelled (cytoplasm) bins
are ignored during fitting and corrected by division at destriping time.

## Notes

- `glum==3.1.2` and `tabmat==4.2.1` are pinned exactly — the coordinate-descent
  solver relies on private internals of those versions.
- This package is derived from the [destriping-GLM](https://github.com/paolamalsot/destriping-glm)
  repo.