scatrans 0.9.6.dev0__tar.gz → 0.9.8.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.
Files changed (51) hide show
  1. scatrans-0.9.8.dev0/.pre-commit-config.yaml +34 -0
  2. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/CHANGELOG.md +10 -0
  3. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/PKG-INFO +1 -1
  4. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/scatrans.egg-info/SOURCES.txt +1 -0
  5. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/_permutation.py +0 -1
  6. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/_version.py +3 -3
  7. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/tl.py +12 -11
  8. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_basic.py +19 -0
  9. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_enrich_api.py +6 -4
  10. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_pp_bias_cli.py +2 -0
  11. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/.github/workflows/ci.yml +0 -0
  12. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/.github/workflows/publish.yml +0 -0
  13. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/.gitignore +0 -0
  14. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/CITATION.cff +0 -0
  15. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/LICENSE +0 -0
  16. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/MANIFEST.in +0 -0
  17. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/README.md +0 -0
  18. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/conftest.py +0 -0
  19. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/conftest_fixtures.py +0 -0
  20. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/examples/memento_de_example.py +0 -0
  21. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/examples/real_data_template.py +0 -0
  22. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/examples/synthetic_active_transcription.py +0 -0
  23. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/pyproject.toml +0 -0
  24. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/setup.cfg +0 -0
  25. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/__init__.py +0 -0
  26. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/_bias.py +0 -0
  27. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/_de.py +0 -0
  28. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/_utils.py +0 -0
  29. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/_velocity.py +0 -0
  30. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/DATA_LICENSES.md +0 -0
  31. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/Hs_GO_Biological_Process_2026.txt +0 -0
  32. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/Hs_KEGG_2026.txt +0 -0
  33. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/Mm_GO_Biological_Process_2026.txt +0 -0
  34. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/Mm_KEGG_2026.txt +0 -0
  35. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/Mus_musculus.GRCm39.115_gene_features.parquet +0 -0
  36. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/README.md +0 -0
  37. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/human_GRCh38_2024A_gene_features.parquet +0 -0
  38. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/data/mouse_2020A_gene_features.parquet +0 -0
  39. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/enrich.py +0 -0
  40. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/generate_gene_features.py +0 -0
  41. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/pl.py +0 -0
  42. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/pp_bias.py +0 -0
  43. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/src/scatrans/qc.py +0 -0
  44. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/__init__.py +0 -0
  45. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/conftest.py +0 -0
  46. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_de_backends.py +0 -0
  47. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_enrich_go.py +0 -0
  48. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_pl_coverage.py +0 -0
  49. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_pl_extended.py +0 -0
  50. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_public_api.py +0 -0
  51. {scatrans-0.9.6.dev0 → scatrans-0.9.8.dev0}/tests/test_tl_coverage.py +0 -0
@@ -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]"]
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.2 bugfix 2026-07-02]
9
+
10
+ ### Fixed
11
+ - **filter_active_genes permissive mode**: default/permissive thresholds for `pval_cutoff`, `active_score_fdr_cutoff`, and `unspliced_excess_fdr_cutoff` now use `float("inf")` instead of `1.0`, so genes with adjusted p-value or permutation FDR exactly equal to 1.0 are no longer silently dropped (strict `<` vs `1.0` bug).
12
+ - **CI lint**: removed unused `import scanpy as sc` from `tl.py` and `_permutation.py` (leftover from permutation refactor); fixed `test_enrich_api.py` formatting.
13
+ - **CI tests**: `test_pp_bias_cli` GTF generator tests now `pytest.importorskip("gtfparse")` so base installs without `scatrans[gene_features]` skip instead of failing.
14
+
15
+ ### Added
16
+ - Regression test `test_filter_active_genes_permissive_keeps_padj_one`.
17
+
8
18
  ## [Unreleased / Review 2026-06-27]
9
19
  ### Added / Improved
10
20
  - Clarified and documented the `gamma_method="empirical_bayes"` implementation as **hierarchical (分层) gamma estimation** for the reference U/S ratio (README keeps the CN term; source now English-only).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scatrans
3
- Version: 0.9.6.dev0
3
+ Version: 0.9.8.dev0
4
4
  Summary: Single-cell Active Transcription Analysis
5
5
  Author: scATrans Developers
6
6
  License: Apache-2.0
@@ -1,4 +1,5 @@
1
1
  .gitignore
2
+ .pre-commit-config.yaml
2
3
  CHANGELOG.md
3
4
  CITATION.cff
4
5
  LICENSE
@@ -12,7 +12,6 @@ import warnings
12
12
  from typing import Any
13
13
 
14
14
  import numpy as np
15
- import scanpy as sc
16
15
  from joblib import Parallel, delayed
17
16
  from statsmodels.stats.multitest import multipletests
18
17
 
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.9.6.dev0'
22
- __version_tuple__ = version_tuple = (0, 9, 6, 'dev0')
21
+ __version__ = version = '0.9.8.dev0'
22
+ __version_tuple__ = version_tuple = (0, 9, 8, 'dev0')
23
23
 
24
- __commit_id__ = commit_id = 'g9392b05f0'
24
+ __commit_id__ = commit_id = None
@@ -23,7 +23,6 @@ import anndata as ad
23
23
  import joblib
24
24
  import numpy as np
25
25
  import pandas as pd
26
- import scanpy as sc
27
26
  import scipy.sparse as sparse # for type hints in signatures (e.g. spmatrix)
28
27
 
29
28
  # qc is imported lazily inside active_score to keep startup light, but exposed at package level
@@ -36,13 +35,13 @@ from ._utils import (
36
35
  UNSPLICED_EXCESS_FDR_COL,
37
36
  UNSPLICED_EXCESS_PVAL_COL,
38
37
  UNSPLICED_EXCESS_RESIDUAL_COL,
38
+ _apply_de_preprocess,
39
+ _clear_log_preprocess_metadata,
39
40
  _get_exponential_scale_lambda,
40
41
  _is_integer_counts_like,
41
42
  _normalize_group_label,
42
43
  _normalize_velocity_layers_by_size_factor,
43
44
  _pseudobulk_with_layers,
44
- _apply_de_preprocess,
45
- _clear_log_preprocess_metadata,
46
45
  _resolve_aligned_raw_counts,
47
46
  _soft_scale,
48
47
  _validate_group_contrast,
@@ -2104,12 +2103,12 @@ def filter_active_genes(
2104
2103
  elif p in ("permissive", "none", "all", "no_filter"):
2105
2104
  preset_vals = {
2106
2105
  "active_score_cutoff": 0.0,
2107
- "pval_cutoff": 1.0,
2106
+ "pval_cutoff": float("inf"),
2108
2107
  "velocity_residual_cutoff": float("-inf"),
2109
2108
  "unspliced_excess_residual_cutoff": float("-inf"),
2110
2109
  "logfc_cutoff": float("inf"),
2111
- "active_score_fdr_cutoff": 1.0,
2112
- "unspliced_excess_fdr_cutoff": 1.0,
2110
+ "active_score_fdr_cutoff": float("inf"),
2111
+ "unspliced_excess_fdr_cutoff": float("inf"),
2113
2112
  "effective_gamma_min": float("-inf"),
2114
2113
  "effective_gamma_max": None,
2115
2114
  "delta_variance_min": None,
@@ -2143,10 +2142,10 @@ def filter_active_genes(
2143
2142
  "active_score_cutoff",
2144
2143
  )
2145
2144
  pval_cutoff = _coerce_numeric_cutoff(
2146
- _resolve("pval_cutoff", pval_cutoff, 1.0), 1.0, "pval_cutoff"
2145
+ _resolve("pval_cutoff", pval_cutoff, float("inf")), float("inf"), "pval_cutoff"
2147
2146
  )
2148
- if not math.isfinite(pval_cutoff) or pval_cutoff < 0:
2149
- raise ValueError("pval_cutoff must be a finite non-negative number.")
2147
+ if pval_cutoff < 0 or (not math.isfinite(pval_cutoff) and not math.isinf(pval_cutoff)):
2148
+ raise ValueError("pval_cutoff must be non-negative, finite, or +inf (permissive).")
2150
2149
  if (
2151
2150
  velocity_residual_cutoff is not _NOT_PROVIDED
2152
2151
  and unspliced_excess_residual_cutoff is _NOT_PROVIDED
@@ -2175,9 +2174,11 @@ def filter_active_genes(
2175
2174
  raise ValueError(
2176
2175
  f'logfc_direction={logfc_direction!r} not recognized. Use one of: "up", "down", "both".'
2177
2176
  )
2178
- active_score_fdr_cutoff = _resolve("active_score_fdr_cutoff", active_score_fdr_cutoff, 1.0)
2177
+ active_score_fdr_cutoff = _resolve(
2178
+ "active_score_fdr_cutoff", active_score_fdr_cutoff, float("inf")
2179
+ )
2179
2180
  unspliced_excess_fdr_cutoff = _resolve(
2180
- "unspliced_excess_fdr_cutoff", unspliced_excess_fdr_cutoff, 1.0
2181
+ "unspliced_excess_fdr_cutoff", unspliced_excess_fdr_cutoff, float("inf")
2181
2182
  )
2182
2183
  effective_gamma_min = _resolve("effective_gamma_min", effective_gamma_min, float("-inf"))
2183
2184
  effective_gamma_max = _resolve("effective_gamma_max", effective_gamma_max, None)
@@ -161,6 +161,25 @@ def test_filter_active_genes_permissive_inf_logfc(adata_basic):
161
161
  assert len(out) == len(allr)
162
162
 
163
163
 
164
+ def test_filter_active_genes_permissive_keeps_padj_one():
165
+ """Default/permissive mode must not drop genes with p_adj or FDR exactly 1.0."""
166
+ df = pd.DataFrame(
167
+ {
168
+ "logFC": [0.1, -0.2, 0.5, -0.9, 0.0],
169
+ "p_val": [1.0, 0.9, 0.5, 0.2, 1.0],
170
+ "p_adj": [1.0, 1.0, 0.8, 0.3, 1.0],
171
+ "active_score_fdr": [1.0, 0.9, 0.5, 0.2, 1.0],
172
+ "unspliced_excess_fdr": [1.0, 1.0, 0.8, 0.3, 1.0],
173
+ },
174
+ index=[f"g{i}" for i in range(5)],
175
+ )
176
+ out_default = scat.filter_active_genes(df)
177
+ out_permissive = scat.filter_active_genes(df, preset="permissive")
178
+ assert len(out_default) == 5
179
+ assert len(out_permissive) == 5
180
+ assert set(out_default.index) == set(df.index)
181
+
182
+
164
183
  def test_add_gene_features_and_list(adata_basic):
165
184
  # Should not crash even if features are incomplete
166
185
  adata_basic.var.columns.tolist()
@@ -53,7 +53,11 @@ def test_extract_gene_lists_single_df():
53
53
  def test_extract_gene_lists_prefers_gene_column_over_range_index():
54
54
  """Scanpy-style DE tables use a 'gene'/'names' column with a default RangeIndex."""
55
55
  df = pd.DataFrame(
56
- {"gene": ["G_up", "G_down", "G_ns"], "logFC": [1.2, -0.9, 0.1], "p_adj": [0.01, 0.02, 0.5]},
56
+ {
57
+ "gene": ["G_up", "G_down", "G_ns"],
58
+ "logFC": [1.2, -0.9, 0.1],
59
+ "p_adj": [0.01, 0.02, 0.5],
60
+ },
57
61
  )
58
62
  out = scat.extract_gene_lists(df, logfc_cutoff=0.5, pval_cutoff=0.05, logfc_direction="up")
59
63
  assert out["contrast"] == ["G_up"]
@@ -67,9 +71,7 @@ def test_extract_gene_lists_separate_directions_single_df_matches_dict():
67
71
  single = scat.extract_gene_lists(
68
72
  df, logfc_direction="up", separate_directions=True, name_prefix="X"
69
73
  )
70
- multi = scat.extract_gene_lists(
71
- {"X": df}, logfc_direction="up", separate_directions=True
72
- )
74
+ multi = scat.extract_gene_lists({"X": df}, logfc_direction="up", separate_directions=True)
73
75
  assert single == multi
74
76
  assert single == {"X_up": ["G1", "G3"], "X_down": ["G2", "G4"]}
75
77
 
@@ -8,6 +8,7 @@ import scatrans as scat
8
8
 
9
9
 
10
10
  def test_generate_gene_features_no_exon_rows(tmp_path):
11
+ pytest.importorskip("gtfparse")
11
12
  from scatrans.pp_bias import generate_gene_features_from_gtf
12
13
 
13
14
  gtf = tmp_path / "no_exon.gtf"
@@ -22,6 +23,7 @@ def test_generate_gene_features_no_exon_rows(tmp_path):
22
23
 
23
24
 
24
25
  def test_generate_gene_features_dedup_gene_names(tmp_path):
26
+ pytest.importorskip("gtfparse")
25
27
  from scatrans.pp_bias import generate_gene_features_from_gtf
26
28
 
27
29
  gtf = tmp_path / "dup.gtf"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes