Metadata-Version: 2.4
Name: sctrial
Version: 0.3.3
Summary: Participant-level differential analysis for longitudinal single-cell experiments: DiD, paired comparisons, module scoring, pseudobulk, and power analysis.
Project-URL: Homepage, https://github.com/TheOmarLab/sctrial
Project-URL: Documentation, https://www.omar-lab.com/sctrial/
Project-URL: Repository, https://github.com/TheOmarLab/sctrial
Project-URL: Issues, https://github.com/TheOmarLab/sctrial/issues
Author-email: "Mohamed N. Omar" <mohamed.omar@cshs.org>
License: MIT
License-File: LICENSE
Keywords: anndata,clinical-trial,difference-in-differences,longitudinal,pseudobulk,scRNA-seq,single-cell
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: anndata>=0.9
Requires-Dist: joblib>=1.2
Requires-Dist: numpy>=1.22
Requires-Dist: pandas>=1.5
Requires-Dist: scipy>=1.8
Requires-Dist: statsmodels>=0.13
Requires-Dist: typing-extensions>=4.0; python_version < '3.11'
Provides-Extra: all
Requires-Dist: adjusttext>=1.0; extra == 'all'
Requires-Dist: gseapy>=1.0; extra == 'all'
Requires-Dist: lifelines>=0.27; extra == 'all'
Requires-Dist: matplotlib>=3.6; extra == 'all'
Requires-Dist: plotly>=5.0; extra == 'all'
Requires-Dist: pymc>=5.0; extra == 'all'
Requires-Dist: pyscenic>=0.12; extra == 'all'
Requires-Dist: scanpy>=1.9; extra == 'all'
Requires-Dist: seaborn>=0.12; extra == 'all'
Provides-Extra: aucell
Requires-Dist: pyscenic>=0.12; extra == 'aucell'
Provides-Extra: bayes
Requires-Dist: pymc>=5.0; extra == 'bayes'
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: mypy>=1.5; extra == 'dev'
Requires-Dist: myst-parser>=2.0; extra == 'dev'
Requires-Dist: nbsphinx>=0.9; extra == 'dev'
Requires-Dist: pandoc; extra == 'dev'
Requires-Dist: pre-commit>=3.0; extra == 'dev'
Requires-Dist: psutil>=5.9; extra == 'dev'
Requires-Dist: pydata-sphinx-theme>=0.15; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: sphinx-copybutton>=0.5; extra == 'dev'
Requires-Dist: sphinx-design>=0.5; extra == 'dev'
Requires-Dist: sphinx>=7.0; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: myst-parser>=2.0; extra == 'docs'
Requires-Dist: nbsphinx>=0.9; extra == 'docs'
Requires-Dist: pandoc; extra == 'docs'
Requires-Dist: pydata-sphinx-theme>=0.15; extra == 'docs'
Requires-Dist: sphinx-copybutton>=0.5; extra == 'docs'
Requires-Dist: sphinx-design>=0.5; extra == 'docs'
Requires-Dist: sphinx>=7.0; extra == 'docs'
Provides-Extra: gsea
Requires-Dist: gseapy>=1.0; extra == 'gsea'
Provides-Extra: plots
Requires-Dist: adjusttext>=1.0; extra == 'plots'
Requires-Dist: matplotlib>=3.6; extra == 'plots'
Requires-Dist: plotly>=5.0; extra == 'plots'
Requires-Dist: scanpy>=1.9; extra == 'plots'
Requires-Dist: seaborn>=0.12; extra == 'plots'
Provides-Extra: survival
Requires-Dist: lifelines>=0.27; extra == 'survival'
Description-Content-Type: text/markdown

<p align="center">
  <img src="docs/source/_static/logo.svg" alt="sctrial" width="280">
</p>
<p align="center"><strong>Participant-Level Differential Analysis for Longitudinal Single-Cell Experiments</strong></p>

<p align="center">
  <a href="https://github.com/TheOmarLab/sctrial/actions/workflows/test.yml">
    <img src="https://github.com/TheOmarLab/sctrial/actions/workflows/test.yml/badge.svg?branch=main" alt="Test Status">
  </a>
  <a href="https://github.com/TheOmarLab/sctrial/releases">
    <img src="https://img.shields.io/github/v/release/TheOmarLab/sctrial?label=version" alt="Release Version">
  </a>
  <img src="https://img.shields.io/badge/python-3.9%2B-blue" alt="Python Versions">
  <a href="https://opensource.org/licenses/MIT">
    <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT">
  </a>
  <a href="https://pypi.org/project/sctrial/">
    <img src="https://img.shields.io/pypi/v/sctrial.svg" alt="PyPI">
  </a>
  <a href="https://www.omar-lab.com/sctrial/">
    <img src="https://readthedocs.org/projects/sctrial/badge/?version=latest" alt="Documentation">
  </a>
</p>

---

## Overview

**sctrial** is a Python package for participant-level differential analysis of longitudinal single-cell RNA-seq data from clinical trials and translational studies. Built on [AnnData](https://anndata.readthedocs.io/) and the [scverse](https://scverse.org/) ecosystem, it provides:

- **Difference-in-Differences (DiD)** analysis with participant fixed effects
- **Paired within-arm** pre→post contrasts
- **Between-arm** comparisons at fixed timepoints
- **Cell-type abundance** change testing
- **Gene set enrichment** analysis (GSEA) on DiD rankings
- **Power analysis** and sample size calculations
- **Effect size** estimation with confidence intervals

<p align="center">
  <img src="docs/source/_static/overview_figure.png" alt="sctrial overview — from scRNA-seq input through trial-aware analysis to statistical outputs" width="100%">
</p>

## Key Features

| Feature | Description |
|---------|-------------|
| **Trial-Aware Design** | Define participant, visit, arm, and cell type columns once |
| **Robust Statistics** | Wild cluster bootstrap, participant-level aggregation |
| **Multiple Comparisons** | Built-in FDR correction across features and cell types |
| **Power Analysis** | Two-arm DiD and single-arm paired power/sample size calculations |
| **Single-Arm Support** | `arm_col=None` for studies without a control arm |
| **Publication-Ready Plots** | Forest plots, interaction plots, GSEA heatmaps |
| **Scalable** | Efficient processing of large single-cell datasets |

## Installation

```bash
pip install sctrial
```

For development:
```bash
git clone https://github.com/TheOmarLab/sctrial.git
cd sctrial
pip install -e ".[dev]"
```

## Quick Start

```bash
pip install "sctrial[plots]"   # includes dataset loaders and visualization
```

```python
import sctrial as st

# 1. Load a real immunotherapy trial dataset (auto-downloads on first use)
adata = st.load_sade_feldman()
adata = st.harmonize_response(adata)  # majority-vote response labels

# 2. Define trial design
design = st.TrialDesign(
    participant_col="participant_id",
    visit_col="visit",
    arm_col="response_harmonized",
    arm_treated="Responder",
    arm_control="Non-responder",
    celltype_col="cell_type",
)

# 3. Score gene sets (dataset ships pre-normalized with log1p_tpm layer)
gene_sets = {
    "Cytotoxicity": ["GZMA", "GZMB", "PRF1", "GNLY", "NKG7"],
    "Exhaustion":   ["PDCD1", "CTLA4", "HAVCR2", "LAG3", "TIGIT"],
}
adata = st.score_gene_sets(adata, gene_sets, layer="log1p_tpm", method="zmean", prefix="ms_")

# 4. Run Difference-in-Differences on CD8 T cells
features = [c for c in adata.obs.columns if c.startswith("ms_")]
results = st.did_table(adata, features, design, visits=("Pre", "Post"), celltype="CD8 T cell")
print(results[["feature", "beta_DiD", "se_DiD", "p_DiD", "FDR_DiD"]])
```

Or use the one-liner convenience wrapper for a quick multi-cell-type scan:

```python
results = st.quick_did(
    adata,
    module_scores=gene_sets,
    visits=("Pre", "Post"),
    arm_col="response_harmonized",
    arm_treated="Responder",
    arm_control="Non-responder",
    celltype_col="cell_type",
)
```

## Supported Study Designs & Datasets

sctrial ships with five real clinical trial datasets, accessible via built-in loaders (`st.load_*()`). Each demonstrates a different study design:

| Design | Description | Dataset | Source | Tutorial |
|--------|-------------|---------|--------|----------|
| **Two-arm paired DiD** | Pre/post × treatment/control interaction | Sade-Feldman et al., *Cell* 2018 — melanoma immunotherapy | [GSE120575](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE120575) | [Immunotherapy](tutorials/example_immunotherapy_sade_feldman.ipynb) |
| **Single-arm pre/post** | Paired within-arm contrasts over time | ImmPort GSE171964 — PBMC vaccine response | [GSE171964](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE171964) | [Vaccine](tutorials/example_vaccine_immport.ipynb) |
| **Single-arm pre/post** | Paired within-arm, multi-timepoint | van Galen et al., *Cell* 2019 — AML chemotherapy | [GSE116256](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE116256) | — |
| **Single-arm multi-timepoint** | Longitudinal tracking across 4 visits | GSE290722 — CAR-T cell therapy (ZUMA-1) | [GSE290722](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE290722) | — |
| **Cross-sectional between-arm** | Between-group comparison at one timepoint | Stephenson et al., *Nature Medicine* 2021 — COVID-19 severity | [E-MTAB-10026](https://www.ebi.ac.uk/biostudies/arrayexpress/studies/E-MTAB-10026) | [COVID-19](tutorials/example_covid19_stephenson.ipynb) |

Additional tutorial: [Scalability Benchmark](tutorials/stress_test_real_scale.ipynb) — performance testing on the Sade-Feldman dataset.

## Documentation

Full documentation: [https://www.omar-lab.com/sctrial/](https://www.omar-lab.com/sctrial/)

- [API Reference](https://www.omar-lab.com/sctrial/api/index.html)
- [Tutorials](https://www.omar-lab.com/sctrial/tutorials/index.html)
- [FAQ](https://www.omar-lab.com/sctrial/faq.html)

## Citation

If you use **sctrial** in your research, please cite:

> Vasanthakumari P, Valencia I, Aghmiouni MR, Magana B, Omar MN. **sctrial: Participant-Level Differential Analysis for Longitudinal Single-Cell Experiments.** *bioRxiv* (2026).

```bibtex
@article{vasanthakumari2026sctrial,
  title = {sctrial: Participant-Level Differential Analysis for Longitudinal Single-Cell Experiments},
  author = {Vasanthakumari, Priyanka and Valencia, Itzel and Aghmiouni, Maryam R. and Magana, Bryan and Omar, Mohamed N.},
  journal = {bioRxiv},
  year = {2026},
  url = {https://github.com/TheOmarLab/sctrial}
}
```

## License

MIT License - see [LICENSE](LICENSE) for details.
