scatrans 0.9.4.dev0__tar.gz → 0.9.4.2.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scatrans-0.9.4.2.dev0/.github/workflows/ci.yml +70 -0
- scatrans-0.9.4.2.dev0/.gitignore +177 -0
- scatrans-0.9.4.2.dev0/CITATION.cff +20 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/MANIFEST.in +5 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/PKG-INFO +2 -1
- scatrans-0.9.4.2.dev0/conftest.py +15 -0
- scatrans-0.9.4.2.dev0/conftest_fixtures.py +146 -0
- scatrans-0.9.4.2.dev0/coverage +0 -0
- {scatrans-0.9.4.dev0/.github → scatrans-0.9.4.2.dev0/github}/workflows/ci.yml +16 -6
- scatrans-0.9.4.2.dev0/github/workflows/publish.yml +74 -0
- scatrans-0.9.4.dev0/.gitignore → scatrans-0.9.4.2.dev0/gitignore +2 -0
- scatrans-0.9.4.2.dev0/pre-commit-config.yaml +34 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/pyproject.toml +4 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/scatrans.egg-info/SOURCES.txt +20 -2
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/_de.py +9 -5
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/_permutation.py +6 -2
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/_version.py +3 -3
- scatrans-0.9.4.2.dev0/src/scatrans/data/human_GRCh38_2024A_gene_features.parquet +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/enrich.py +68 -71
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/pl.py +53 -44
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/pp_bias.py +14 -9
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/qc.py +0 -2
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/tl.py +18 -13
- scatrans-0.9.4.2.dev0/tests/__init__.py +0 -0
- scatrans-0.9.4.2.dev0/tests/conftest.py +7 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/tests/test_basic.py +15 -5
- scatrans-0.9.4.2.dev0/tests/test_de_backends.py +196 -0
- scatrans-0.9.4.2.dev0/tests/test_enrich_api.py +140 -0
- scatrans-0.9.4.2.dev0/tests/test_enrich_go.py +42 -0
- scatrans-0.9.4.2.dev0/tests/test_pl_coverage.py +61 -0
- scatrans-0.9.4.2.dev0/tests/test_pl_extended.py +59 -0
- scatrans-0.9.4.2.dev0/tests/test_pp_bias_cli.py +37 -0
- scatrans-0.9.4.2.dev0/tests/test_public_api.py +50 -0
- scatrans-0.9.4.2.dev0/tests/test_tl_coverage.py +302 -0
- scatrans-0.9.4.dev0/src/scatrans/_bias.py +0 -24
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/.github/workflows/publish.yml +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/CHANGELOG.md +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/LICENSE +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/README.md +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/examples/memento_de_example.py +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/examples/real_data_template.py +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/examples/synthetic_active_transcription.py +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/setup.cfg +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/__init__.py +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/_utils.py +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/_velocity.py +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/data/Hs_GO_Biological_Process_2026.txt +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/data/Hs_KEGG_2026.txt +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/data/Mm_GO_Biological_Process_2026.txt +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/data/Mm_KEGG_2026.txt +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/data/Mus_musculus.GRCm39.115_gene_features.parquet +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/data/README.md +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/data/mouse_2020A_gene_features.parquet +0 -0
- {scatrans-0.9.4.dev0 → scatrans-0.9.4.2.dev0}/src/scatrans/generate_gene_features.py +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
16
|
+
install-extra:
|
|
17
|
+
- ""
|
|
18
|
+
- "advanced"
|
|
19
|
+
- "pseudobulk"
|
|
20
|
+
- "gene_features"
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
|
|
27
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
cache: "pip"
|
|
32
|
+
|
|
33
|
+
- name: Install package with extras
|
|
34
|
+
run: |
|
|
35
|
+
pip install --upgrade pip
|
|
36
|
+
EXTRA="${{ matrix.install-extra }}"
|
|
37
|
+
if [ -n "$EXTRA" ]; then
|
|
38
|
+
pip install -e ".[dev,$EXTRA]"
|
|
39
|
+
else
|
|
40
|
+
pip install -e ".[dev]"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
- name: Ruff lint
|
|
44
|
+
run: |
|
|
45
|
+
ruff check src/scatrans tests
|
|
46
|
+
ruff format --check src/scatrans tests
|
|
47
|
+
|
|
48
|
+
- name: Run fast tests
|
|
49
|
+
run: PYTHONPATH=src:. python -m pytest -m "not plot and not slow" -q
|
|
50
|
+
|
|
51
|
+
- name: Run slow tests (core / Python 3.11 only)
|
|
52
|
+
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
53
|
+
run: PYTHONPATH=src:. python -m pytest -m "slow and not plot" -q
|
|
54
|
+
|
|
55
|
+
- name: Run plot tests (core / Python 3.11 only)
|
|
56
|
+
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
57
|
+
run: PYTHONPATH=src:. python -m pytest -m plot -q
|
|
58
|
+
|
|
59
|
+
- name: Coverage report (core / Python 3.11 only)
|
|
60
|
+
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
61
|
+
run: |
|
|
62
|
+
PYTHONPATH=src:. python -m pytest -m "not plot" --cov=src/scatrans --cov-report=xml -q \
|
|
63
|
+
|| PYTHONPATH=src:. python -m pytest -m "not plot" -q
|
|
64
|
+
|
|
65
|
+
- name: Upload coverage (optional)
|
|
66
|
+
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
67
|
+
uses: codecov/codecov-action@v4
|
|
68
|
+
with:
|
|
69
|
+
files: ./coverage.xml
|
|
70
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
scatrans.egg-info/
|
|
25
|
+
src/scatrans.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
|
|
38
|
+
# Unit test / coverage reports
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.nox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*.cover
|
|
48
|
+
*.py,cover
|
|
49
|
+
.hypothesis/
|
|
50
|
+
.pytest_cache/
|
|
51
|
+
cover/
|
|
52
|
+
|
|
53
|
+
# Translations
|
|
54
|
+
*.mo
|
|
55
|
+
*.pot
|
|
56
|
+
|
|
57
|
+
# Django stuff:
|
|
58
|
+
*.log
|
|
59
|
+
local_settings.py
|
|
60
|
+
db.sqlite3
|
|
61
|
+
db.sqlite3-journal
|
|
62
|
+
|
|
63
|
+
# Flask stuff:
|
|
64
|
+
instance/
|
|
65
|
+
.webassets-cache
|
|
66
|
+
|
|
67
|
+
# Scrapy stuff:
|
|
68
|
+
.scrapy
|
|
69
|
+
|
|
70
|
+
# Sphinx documentation
|
|
71
|
+
docs/_build/
|
|
72
|
+
|
|
73
|
+
# PyBuilder
|
|
74
|
+
.pybuilder/
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
.python-version
|
|
86
|
+
|
|
87
|
+
# pipenv
|
|
88
|
+
Pipfile.lock
|
|
89
|
+
|
|
90
|
+
# poetry
|
|
91
|
+
poetry.lock
|
|
92
|
+
|
|
93
|
+
# pdm
|
|
94
|
+
.pdm.toml
|
|
95
|
+
.pdm-python
|
|
96
|
+
.pdm-build/
|
|
97
|
+
|
|
98
|
+
# PEP 582
|
|
99
|
+
__pypackages__/
|
|
100
|
+
|
|
101
|
+
# Celery stuff
|
|
102
|
+
celerybeat-schedule
|
|
103
|
+
celerybeat.pid
|
|
104
|
+
|
|
105
|
+
# SageMath parsed files
|
|
106
|
+
*.sage.py
|
|
107
|
+
|
|
108
|
+
# Environments
|
|
109
|
+
.env
|
|
110
|
+
.venv
|
|
111
|
+
env/
|
|
112
|
+
venv/
|
|
113
|
+
ENV/
|
|
114
|
+
env.bak/
|
|
115
|
+
venv.bak/
|
|
116
|
+
|
|
117
|
+
# Spyder project settings
|
|
118
|
+
.spyderproject
|
|
119
|
+
.spyproject
|
|
120
|
+
|
|
121
|
+
# Rope project settings
|
|
122
|
+
.ropeproject
|
|
123
|
+
|
|
124
|
+
# mkdocs documentation
|
|
125
|
+
/site
|
|
126
|
+
|
|
127
|
+
# mypy
|
|
128
|
+
.mypy_cache/
|
|
129
|
+
.dmypy.json
|
|
130
|
+
dmypy.json
|
|
131
|
+
|
|
132
|
+
# Pyre type checker
|
|
133
|
+
.pyre/
|
|
134
|
+
|
|
135
|
+
# pytype static type analyzer
|
|
136
|
+
.pytype/
|
|
137
|
+
|
|
138
|
+
# Cython debug symbols
|
|
139
|
+
cython_debug/
|
|
140
|
+
|
|
141
|
+
# Ruff
|
|
142
|
+
.ruff_cache/
|
|
143
|
+
|
|
144
|
+
# IDE / editors
|
|
145
|
+
.idea/
|
|
146
|
+
.vscode/
|
|
147
|
+
*.swp
|
|
148
|
+
*.swo
|
|
149
|
+
*~
|
|
150
|
+
|
|
151
|
+
# OS generated files
|
|
152
|
+
.DS_Store
|
|
153
|
+
.DS_Store?
|
|
154
|
+
._*
|
|
155
|
+
.Spotlight-V100
|
|
156
|
+
.Trashes
|
|
157
|
+
ehthumbs.db
|
|
158
|
+
Thumbs.db
|
|
159
|
+
|
|
160
|
+
# Project specific
|
|
161
|
+
# Bundled large data is committed intentionally (gene features)
|
|
162
|
+
# but do not add user-generated .parquet outputs here by mistake
|
|
163
|
+
*.h5ad
|
|
164
|
+
*.loom
|
|
165
|
+
*.zarr
|
|
166
|
+
# Keep source data/ but ignore generated outputs at root
|
|
167
|
+
gene_features.parquet
|
|
168
|
+
*_gene_features.parquet
|
|
169
|
+
*_features.parquet
|
|
170
|
+
# Bundled package data (must be tracked for CI / wheels)
|
|
171
|
+
!src/scatrans/data/
|
|
172
|
+
!src/scatrans/data/*.parquet
|
|
173
|
+
# Temporary analysis outputs
|
|
174
|
+
*.pdf
|
|
175
|
+
*.png
|
|
176
|
+
*.svg
|
|
177
|
+
active_results/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
title: "scATrans: Single-cell Active Transcription Analysis"
|
|
3
|
+
message: >-
|
|
4
|
+
If you use this software, please cite it using the metadata below.
|
|
5
|
+
type: software
|
|
6
|
+
authors:
|
|
7
|
+
- name: "scATrans Developers"
|
|
8
|
+
repository-code: "https://github.com/scATrans/scatrans"
|
|
9
|
+
url: "https://github.com/scATrans/scatrans"
|
|
10
|
+
license: Apache-2.0
|
|
11
|
+
version: 0.9.2
|
|
12
|
+
keywords:
|
|
13
|
+
- single-cell
|
|
14
|
+
- RNA-seq
|
|
15
|
+
- unspliced RNA
|
|
16
|
+
- nascent RNA
|
|
17
|
+
- active transcription
|
|
18
|
+
- bioinformatics
|
|
19
|
+
- differential expression
|
|
20
|
+
- gene set enrichment
|
|
@@ -7,6 +7,11 @@ include README.md
|
|
|
7
7
|
include CHANGELOG.md
|
|
8
8
|
include CITATION.cff
|
|
9
9
|
include pyproject.toml
|
|
10
|
+
include conftest.py
|
|
11
|
+
include conftest_fixtures.py
|
|
12
|
+
|
|
13
|
+
# Include the full test suite so that `pytest` can be run from sdist/unpacked source
|
|
14
|
+
graft tests
|
|
10
15
|
|
|
11
16
|
# Include the GitHub Actions workflows (requested)
|
|
12
17
|
include .github/workflows/ci.yml
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scatrans
|
|
3
|
-
Version: 0.9.4.dev0
|
|
3
|
+
Version: 0.9.4.2.dev0
|
|
4
4
|
Summary: Single-cell Active Transcription Analysis
|
|
5
5
|
Author: scATrans Developers
|
|
6
6
|
License: Apache-2.0
|
|
@@ -18,6 +18,7 @@ Requires-Dist: scanpy>=1.9
|
|
|
18
18
|
Requires-Dist: anndata>=0.8
|
|
19
19
|
Requires-Dist: numpy>=1.21
|
|
20
20
|
Requires-Dist: pandas>=1.3
|
|
21
|
+
Requires-Dist: pyarrow>=10.0
|
|
21
22
|
Requires-Dist: scipy>=1.7
|
|
22
23
|
Requires-Dist: scikit-learn>=1.0
|
|
23
24
|
Requires-Dist: joblib>=1.2
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Root-level conftest.py.
|
|
2
|
+
|
|
3
|
+
Ensures project root and src/ are on sys.path (CI, --cov, editable installs,
|
|
4
|
+
unpacked sdists) and registers shared test fixtures via pytest_plugins.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
root = Path(__file__).resolve().parent
|
|
11
|
+
for p in (str(root), str(root / "src")):
|
|
12
|
+
if p not in sys.path:
|
|
13
|
+
sys.path.insert(0, p)
|
|
14
|
+
|
|
15
|
+
pytest_plugins = ["conftest_fixtures"]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Pytest plugin: shared AnnData fixtures for the scATrans test suite.
|
|
2
|
+
|
|
3
|
+
Loaded via ``pytest_plugins`` from root ``conftest.py`` (and ``tests/conftest.py``)
|
|
4
|
+
so fixtures are registered even when nested conftest discovery fails in CI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import pytest
|
|
10
|
+
import scanpy as sc
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture(scope="module")
|
|
14
|
+
def adata_basic():
|
|
15
|
+
"""AnnData with spliced/unspliced layers + gene features."""
|
|
16
|
+
np.random.seed(42)
|
|
17
|
+
n_cells, n_genes = 120, 250
|
|
18
|
+
X = np.random.negative_binomial(4, 0.45, size=(n_cells, n_genes)).astype(float)
|
|
19
|
+
ad = sc.AnnData(X)
|
|
20
|
+
ad.obs["condition"] = ["Disease"] * 60 + ["Control"] * 60
|
|
21
|
+
ad.obs["sample"] = ["s" + str(i % 8) for i in range(n_cells)]
|
|
22
|
+
ad.layers["spliced"] = X.copy()
|
|
23
|
+
ad.layers["unspliced"] = X * 0.55
|
|
24
|
+
ad.var["gene_length"] = np.random.randint(700, 4500, n_genes)
|
|
25
|
+
ad.var["intron_number"] = np.random.randint(0, 12, n_genes)
|
|
26
|
+
return ad
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture(scope="module")
|
|
30
|
+
def adata_de_only():
|
|
31
|
+
"""Count AnnData without velocity layers (pure DE path)."""
|
|
32
|
+
np.random.seed(99)
|
|
33
|
+
n_cells, n_genes = 80, 120
|
|
34
|
+
X = np.random.negative_binomial(5, 0.4, size=(n_cells, n_genes)).astype(float)
|
|
35
|
+
ad = sc.AnnData(X)
|
|
36
|
+
ad.obs["condition"] = ["Disease"] * 40 + ["Control"] * 40
|
|
37
|
+
ad.obs["sample"] = ["s" + str(i % 6) for i in range(n_cells)]
|
|
38
|
+
return ad
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.fixture(scope="module")
|
|
42
|
+
def adata_small_reference():
|
|
43
|
+
"""Synthetic data with a small reference group (empirical Bayes gamma)."""
|
|
44
|
+
np.random.seed(123)
|
|
45
|
+
n_ref, n_tgt, n_genes = 18, 80, 200
|
|
46
|
+
n_cells = n_ref + n_tgt
|
|
47
|
+
X = np.random.negative_binomial(4, 0.45, size=(n_cells, n_genes)).astype(float)
|
|
48
|
+
ad = sc.AnnData(X)
|
|
49
|
+
ad.obs["condition"] = ["Control"] * n_ref + ["Disease"] * n_tgt
|
|
50
|
+
ad.layers["spliced"] = X.copy()
|
|
51
|
+
ad.layers["unspliced"] = X * 0.5
|
|
52
|
+
ad.var["gene_length"] = np.random.randint(700, 4500, n_genes)
|
|
53
|
+
ad.var["intron_number"] = np.random.randint(0, 12, n_genes)
|
|
54
|
+
return ad
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.fixture(scope="module")
|
|
58
|
+
def adata_high_unspliced():
|
|
59
|
+
"""Very high unspliced fraction for qc warning path."""
|
|
60
|
+
np.random.seed(7)
|
|
61
|
+
n_cells, n_genes = 40, 50
|
|
62
|
+
X = np.random.negative_binomial(3, 0.5, size=(n_cells, n_genes)).astype(float)
|
|
63
|
+
ad = sc.AnnData(X)
|
|
64
|
+
ad.obs["condition"] = ["A"] * 20 + ["B"] * 20
|
|
65
|
+
ad.layers["spliced"] = X * 0.2
|
|
66
|
+
ad.layers["unspliced"] = X * 2.0
|
|
67
|
+
return ad
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.fixture(scope="module")
|
|
71
|
+
def adata_mature_nascent():
|
|
72
|
+
"""kb_python style layer names."""
|
|
73
|
+
np.random.seed(123)
|
|
74
|
+
n_cells, n_genes = 80, 180
|
|
75
|
+
X = np.random.negative_binomial(3, 0.5, size=(n_cells, n_genes)).astype(float)
|
|
76
|
+
ad = sc.AnnData(X)
|
|
77
|
+
ad.obs["condition"] = ["GA"] * 40 + ["Ctrl"] * 40
|
|
78
|
+
ad.layers["mature"] = X.copy()
|
|
79
|
+
ad.layers["nascent"] = X * 0.5
|
|
80
|
+
ad.var["gene_length"] = np.random.randint(800, 4000, n_genes)
|
|
81
|
+
ad.var["intron_number"] = np.random.randint(1, 9, n_genes)
|
|
82
|
+
return ad
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@pytest.fixture(scope="module")
|
|
86
|
+
def adata_mixed_small():
|
|
87
|
+
"""Small fixture for mixed-model + filter_active_genes tests."""
|
|
88
|
+
np.random.seed(42)
|
|
89
|
+
n_cells, n_genes = 60, 70
|
|
90
|
+
X = np.random.negative_binomial(3, 0.5, size=(n_cells, n_genes)).astype(float)
|
|
91
|
+
ad = sc.AnnData(X)
|
|
92
|
+
ad.obs["condition"] = ["Disease"] * 30 + ["Control"] * 30
|
|
93
|
+
ad.obs["sample"] = ["s" + str(i % 6) for i in range(n_cells)]
|
|
94
|
+
ad.layers["spliced"] = X.copy()
|
|
95
|
+
ad.layers["unspliced"] = X * 0.45
|
|
96
|
+
ad.var["gene_length"] = np.random.randint(600, 3500, n_genes)
|
|
97
|
+
ad.var["intron_number"] = np.random.randint(0, 8, n_genes)
|
|
98
|
+
return ad
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@pytest.fixture(scope="module")
|
|
102
|
+
def adata_pb():
|
|
103
|
+
"""Pseudobulk-oriented fixture (4 samples per group)."""
|
|
104
|
+
np.random.seed(11)
|
|
105
|
+
n_cells, n_genes = 96, 80
|
|
106
|
+
X = np.random.negative_binomial(6, 0.35, size=(n_cells, n_genes)).astype(float)
|
|
107
|
+
ad = sc.AnnData(X)
|
|
108
|
+
ad.obs["condition"] = ["Disease"] * 48 + ["Control"] * 48
|
|
109
|
+
ad.obs["sample"] = [f"S{i // 12}" for i in range(n_cells)]
|
|
110
|
+
ad.layers["spliced"] = X.copy()
|
|
111
|
+
ad.layers["unspliced"] = X * 0.5
|
|
112
|
+
return ad
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.fixture(scope="module")
|
|
116
|
+
def results_df(adata_basic):
|
|
117
|
+
"""Shared active_score table for plotting tests (one run per module)."""
|
|
118
|
+
import scatrans as scat
|
|
119
|
+
|
|
120
|
+
_, _, allr = scat.active_score(
|
|
121
|
+
adata_basic,
|
|
122
|
+
groupby="condition",
|
|
123
|
+
target_group="Disease",
|
|
124
|
+
reference_group="Control",
|
|
125
|
+
use_permutation=False,
|
|
126
|
+
show_plot=False,
|
|
127
|
+
)
|
|
128
|
+
return allr
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@pytest.fixture(scope="module")
|
|
132
|
+
def enrich_df():
|
|
133
|
+
"""Minimal enrichment result for plotting smoke tests."""
|
|
134
|
+
return pd.DataFrame(
|
|
135
|
+
{
|
|
136
|
+
"Term": ["T1", "T2", "T3"],
|
|
137
|
+
"Description": ["desc1", "desc2", "desc3"],
|
|
138
|
+
"Count": [5, 3, 8],
|
|
139
|
+
"GeneRatio": [0.1, 0.05, 0.2],
|
|
140
|
+
"FoldEnrichment": [2.0, 1.5, 3.0],
|
|
141
|
+
"pvalue": [0.001, 0.01, 0.0001],
|
|
142
|
+
"p.adjust": [0.01, 0.05, 0.001],
|
|
143
|
+
"neg_log10_padj": [2.0, 1.3, 3.0],
|
|
144
|
+
"Genes": ["G1;G2", "G2;G3", "G4;G5;G6"],
|
|
145
|
+
}
|
|
146
|
+
)
|
|
Binary file
|
|
@@ -13,7 +13,6 @@ jobs:
|
|
|
13
13
|
fail-fast: false
|
|
14
14
|
matrix:
|
|
15
15
|
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
16
|
-
# Core + each optional extra (some may be heavy / have native deps)
|
|
17
16
|
install-extra:
|
|
18
17
|
- ""
|
|
19
18
|
- "advanced"
|
|
@@ -23,7 +22,7 @@ jobs:
|
|
|
23
22
|
steps:
|
|
24
23
|
- uses: actions/checkout@v4
|
|
25
24
|
with:
|
|
26
|
-
fetch-depth: 0
|
|
25
|
+
fetch-depth: 0
|
|
27
26
|
|
|
28
27
|
- name: Set up Python ${{ matrix.python-version }}
|
|
29
28
|
uses: actions/setup-python@v5
|
|
@@ -46,13 +45,24 @@ jobs:
|
|
|
46
45
|
ruff check src/scatrans tests
|
|
47
46
|
ruff format --check src/scatrans tests
|
|
48
47
|
|
|
49
|
-
- name: Run tests
|
|
50
|
-
run:
|
|
51
|
-
|
|
48
|
+
- name: Run fast tests
|
|
49
|
+
run: PYTHONPATH=src:. python -m pytest -m "not plot and not slow" -q
|
|
50
|
+
|
|
51
|
+
- name: Run slow tests (core / Python 3.11 only)
|
|
52
|
+
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
53
|
+
run: PYTHONPATH=src:. python -m pytest -m "slow and not plot" -q
|
|
54
|
+
|
|
55
|
+
- name: Run plot tests (core / Python 3.11 only)
|
|
56
|
+
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
57
|
+
run: PYTHONPATH=src:. python -m pytest -m plot -q
|
|
58
|
+
|
|
59
|
+
- name: Coverage report (core / Python 3.11 only)
|
|
60
|
+
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
61
|
+
run: PYTHONPATH=src:. python -m pytest -m "not plot" --cov=src/scatrans --cov-report=xml -q
|
|
52
62
|
|
|
53
63
|
- name: Upload coverage (optional)
|
|
54
64
|
if: matrix.python-version == '3.11' && matrix.install-extra == ''
|
|
55
65
|
uses: codecov/codecov-action@v4
|
|
56
66
|
with:
|
|
57
67
|
files: ./coverage.xml
|
|
58
|
-
fail_ci_if_error: false
|
|
68
|
+
fail_ci_if_error: false
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
release:
|
|
8
|
+
types: [published]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
version:
|
|
12
|
+
description: "Force a specific version (SETUPTOOLS_SCM_PRETEND_VERSION). Useful for dev releases when not on a tag."
|
|
13
|
+
required: false
|
|
14
|
+
default: ""
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: Build distribution 📦
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0 # Critical for setuptools_scm to detect tags and produce correct version
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.11"
|
|
29
|
+
|
|
30
|
+
- name: Install build tools
|
|
31
|
+
run: python -m pip install --upgrade build
|
|
32
|
+
|
|
33
|
+
- name: Build source and wheel distributions
|
|
34
|
+
run: |
|
|
35
|
+
if [ -n "${{ github.event.inputs.version }}" ]; then
|
|
36
|
+
echo "Using forced version: ${{ github.event.inputs.version }}"
|
|
37
|
+
SETUPTOOLS_SCM_PRETEND_VERSION="${{ github.event.inputs.version }}" python -m build
|
|
38
|
+
else
|
|
39
|
+
python -m build
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
- name: Upload distribution artifacts
|
|
43
|
+
uses: actions/upload-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: python-package-distributions
|
|
46
|
+
path: dist/
|
|
47
|
+
|
|
48
|
+
publish:
|
|
49
|
+
name: Publish to PyPI
|
|
50
|
+
needs: build
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
|
|
53
|
+
# Required for Trusted Publishing (OIDC) - no API token secret needed
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write
|
|
56
|
+
|
|
57
|
+
# Recommended: tie to a protected GitHub Environment (create "pypi" environment in repo settings)
|
|
58
|
+
# You can add required reviewers or branch restrictions in the environment settings.
|
|
59
|
+
# environment:
|
|
60
|
+
# name: pypi
|
|
61
|
+
|
|
62
|
+
steps:
|
|
63
|
+
- name: Download all dists
|
|
64
|
+
uses: actions/download-artifact@v4
|
|
65
|
+
with:
|
|
66
|
+
name: python-package-distributions
|
|
67
|
+
path: dist/
|
|
68
|
+
|
|
69
|
+
- name: Publish distribution 📦 to PyPI
|
|
70
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
71
|
+
# For publishing to TestPyPI instead (for testing the workflow):
|
|
72
|
+
# with:
|
|
73
|
+
# repository-url: https://test.pypi.org/legacy/
|
|
74
|
+
# verbose: true
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Pre-commit hooks for scATrans
|
|
2
|
+
# Install: pip install pre-commit
|
|
3
|
+
# Setup: pre-commit install
|
|
4
|
+
# Run manually: pre-commit run --all-files
|
|
5
|
+
|
|
6
|
+
repos:
|
|
7
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
8
|
+
rev: v0.4.4
|
|
9
|
+
hooks:
|
|
10
|
+
- id: ruff
|
|
11
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
12
|
+
- id: ruff-format
|
|
13
|
+
|
|
14
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
15
|
+
rev: v1.10.0
|
|
16
|
+
hooks:
|
|
17
|
+
- id: mypy
|
|
18
|
+
additional_dependencies: []
|
|
19
|
+
args: [--ignore-missing-imports, --no-error-summary]
|
|
20
|
+
# mypy on the src layout; relax for now to avoid blocking on third-party stubs
|
|
21
|
+
exclude: |
|
|
22
|
+
(?x)^(
|
|
23
|
+
tests/|
|
|
24
|
+
backup/|
|
|
25
|
+
src/scatrans/_version.py
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Optional pyright (static type checker, often stricter). Requires node + pyright.
|
|
29
|
+
# Uncomment if you have pyright in your env (npm i -g pyright or via pyright package).
|
|
30
|
+
# - repo: https://github.com/RobertCraigie/pyright-python
|
|
31
|
+
# rev: v1.1.367
|
|
32
|
+
# hooks:
|
|
33
|
+
# - id: pyright
|
|
34
|
+
# additional_dependencies: [".[dev]"]
|
|
@@ -26,6 +26,7 @@ dependencies = [
|
|
|
26
26
|
"anndata>=0.8",
|
|
27
27
|
"numpy>=1.21",
|
|
28
28
|
"pandas>=1.3",
|
|
29
|
+
"pyarrow>=10.0",
|
|
29
30
|
"scipy>=1.7",
|
|
30
31
|
"scikit-learn>=1.0",
|
|
31
32
|
"joblib>=1.2",
|
|
@@ -108,6 +109,9 @@ indent-style = "space"
|
|
|
108
109
|
[tool.pytest.ini_options]
|
|
109
110
|
minversion = "7.0"
|
|
110
111
|
testpaths = ["tests"]
|
|
112
|
+
# Ensure the project root is on sys.path for fixture discovery and src-layout editable installs
|
|
113
|
+
# (helps in CI, zip extractions, and when cwd differs slightly)
|
|
114
|
+
pythonpath = ["src", "."]
|
|
111
115
|
# Daily dev: skip plot + slow. Override examples:
|
|
112
116
|
# pytest -m "not plot" # include slow integration tests
|
|
113
117
|
# pytest -m plot # plotting only
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
.gitignore
|
|
2
2
|
CHANGELOG.md
|
|
3
|
+
CITATION.cff
|
|
3
4
|
LICENSE
|
|
4
5
|
MANIFEST.in
|
|
5
6
|
PKG-INFO
|
|
6
7
|
README.md
|
|
8
|
+
conftest.py
|
|
9
|
+
conftest_fixtures.py
|
|
10
|
+
coverage
|
|
11
|
+
gitignore
|
|
12
|
+
pre-commit-config.yaml
|
|
7
13
|
pyproject.toml
|
|
8
14
|
setup.cfg
|
|
9
15
|
.github/workflows/ci.yml
|
|
@@ -11,8 +17,9 @@ setup.cfg
|
|
|
11
17
|
examples/memento_de_example.py
|
|
12
18
|
examples/real_data_template.py
|
|
13
19
|
examples/synthetic_active_transcription.py
|
|
20
|
+
github/workflows/ci.yml
|
|
21
|
+
github/workflows/publish.yml
|
|
14
22
|
src/scatrans/__init__.py
|
|
15
|
-
src/scatrans/_bias.py
|
|
16
23
|
src/scatrans/_de.py
|
|
17
24
|
src/scatrans/_permutation.py
|
|
18
25
|
src/scatrans/_utils.py
|
|
@@ -30,5 +37,16 @@ src/scatrans/data/Mm_GO_Biological_Process_2026.txt
|
|
|
30
37
|
src/scatrans/data/Mm_KEGG_2026.txt
|
|
31
38
|
src/scatrans/data/Mus_musculus.GRCm39.115_gene_features.parquet
|
|
32
39
|
src/scatrans/data/README.md
|
|
40
|
+
src/scatrans/data/human_GRCh38_2024A_gene_features.parquet
|
|
33
41
|
src/scatrans/data/mouse_2020A_gene_features.parquet
|
|
34
|
-
tests/
|
|
42
|
+
tests/__init__.py
|
|
43
|
+
tests/conftest.py
|
|
44
|
+
tests/test_basic.py
|
|
45
|
+
tests/test_de_backends.py
|
|
46
|
+
tests/test_enrich_api.py
|
|
47
|
+
tests/test_enrich_go.py
|
|
48
|
+
tests/test_pl_coverage.py
|
|
49
|
+
tests/test_pl_extended.py
|
|
50
|
+
tests/test_pp_bias_cli.py
|
|
51
|
+
tests/test_public_api.py
|
|
52
|
+
tests/test_tl_coverage.py
|