survscope 0.4.3__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.
- survscope-0.4.3/LICENSE +21 -0
- survscope-0.4.3/PKG-INFO +113 -0
- survscope-0.4.3/README.md +81 -0
- survscope-0.4.3/pyproject.toml +63 -0
- survscope-0.4.3/setup.cfg +4 -0
- survscope-0.4.3/src/survscope/__init__.py +34 -0
- survscope-0.4.3/src/survscope/analysis.py +152 -0
- survscope-0.4.3/src/survscope/builder.py +657 -0
- survscope-0.4.3/src/survscope/cli.py +189 -0
- survscope-0.4.3/src/survscope/constants.py +125 -0
- survscope-0.4.3/src/survscope/cptac.py +620 -0
- survscope-0.4.3/src/survscope/data.py +236 -0
- survscope-0.4.3/src/survscope/grouping.py +163 -0
- survscope-0.4.3/src/survscope/models.py +128 -0
- survscope-0.4.3/src/survscope/plotting.py +163 -0
- survscope-0.4.3/src/survscope/quality.py +91 -0
- survscope-0.4.3/src/survscope/statistics.py +250 -0
- survscope-0.4.3/src/survscope/validation.py +192 -0
- survscope-0.4.3/src/survscope.egg-info/PKG-INFO +113 -0
- survscope-0.4.3/src/survscope.egg-info/SOURCES.txt +27 -0
- survscope-0.4.3/src/survscope.egg-info/dependency_links.txt +1 -0
- survscope-0.4.3/src/survscope.egg-info/entry_points.txt +3 -0
- survscope-0.4.3/src/survscope.egg-info/requires.txt +10 -0
- survscope-0.4.3/src/survscope.egg-info/top_level.txt +1 -0
- survscope-0.4.3/tests/test_cptac.py +394 -0
- survscope-0.4.3/tests/test_data.py +96 -0
- survscope-0.4.3/tests/test_grouping.py +159 -0
- survscope-0.4.3/tests/test_reference.py +104 -0
- survscope-0.4.3/tests/test_statistics.py +67 -0
survscope-0.4.3/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yaoxiang Li
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
survscope-0.4.3/PKG-INFO
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: survscope
|
|
3
|
+
Version: 0.4.3
|
|
4
|
+
Summary: Reproducible TCGA and CPTAC Kaplan-Meier survival plots from compact static data
|
|
5
|
+
Author: Yaoxiang Li
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://oncologylab.github.io/survscope/
|
|
8
|
+
Project-URL: Repository, https://github.com/oncologylab/survscope
|
|
9
|
+
Project-URL: Issues, https://github.com/oncologylab/survscope/issues
|
|
10
|
+
Keywords: TCGA,CPTAC,survival,Kaplan-Meier,oncology,bioinformatics
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: matplotlib<4,>=3.8
|
|
23
|
+
Requires-Dist: numpy<3,>=1.26
|
|
24
|
+
Requires-Dist: scipy<2,>=1.12
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: build<2,>=1.2; extra == "test"
|
|
27
|
+
Requires-Dist: pytest<9,>=8; extra == "test"
|
|
28
|
+
Requires-Dist: pytest-cov<8,>=5; extra == "test"
|
|
29
|
+
Requires-Dist: ruff<1,>=0.11; extra == "test"
|
|
30
|
+
Requires-Dist: twine<7,>=6; extra == "test"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# SurvScope
|
|
34
|
+
|
|
35
|
+
[](https://github.com/oncologylab/survscope/actions/workflows/ci.yml)
|
|
36
|
+
[](https://oncologylab.github.io/survscope/)
|
|
37
|
+
|
|
38
|
+
**Explore how a gene's RNA expression relates to survival in a cancer cohort, then edit and download the figure.** SurvScope works in your browser, without an account or installation.
|
|
39
|
+
|
|
40
|
+
[**Open SurvScope →**](https://oncologylab.github.io/survscope/) · [Step-by-step guide](docs/user-guide.md) · [Statistical methods](docs/methods.md)
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
## Make your first figure
|
|
45
|
+
|
|
46
|
+
1. Choose a cancer cohort and enter a gene symbol, such as **SRD5A1** or **TP53**.
|
|
47
|
+
2. Choose how to compare lower and higher expression. Start with the median, or try a percentile, the lowest/highest quarters or thirds, custom percentile groups, the mean, or a TPM threshold. Check the patient counts before running.
|
|
48
|
+
3. Select **Create survival plot**. Each panel shows a different outcome, when available.
|
|
49
|
+
4. **Double-click text on the figure to edit it.** Select and drag objects; use Properties for fonts, colors, dimensions, axes, and layout, or Layers to hide and lock objects. Optional confidence bands, censor marks, and number-at-risk tables add context.
|
|
50
|
+
5. Download **SVG**, **PDF**, or **PNG**. **Save project** lets you reopen the results and keep editing; a style preset reuses the appearance for another analysis.
|
|
51
|
+
|
|
52
|
+
The workspace fills your screen, with collapsible Analysis and Properties panels. Familiar Selection (V), Type (T), Hand (H), and Zoom (Z) tools help you arrange the figure. Icon commands explain themselves on hover or keyboard focus. Text supports bold, italic, superscripts, and subscripts, with seven bundled [font choices](docs/user-guide.md#choose-a-font), including clearly labeled Arial/Helvetica-style alternatives.
|
|
53
|
+
|
|
54
|
+
The blue/red, four-panel, 6.8-inch figure shows p-values and hazard ratios by default. Q-values are optional: enable **Properties → Survival details → Show adjusted q-value** to include them. Editing the figure's appearance does not change the calculated results.
|
|
55
|
+
|
|
56
|
+
## Which cancers and data are available?
|
|
57
|
+
|
|
58
|
+
The current release includes **all 33 TCGA cohorts and 18 CPTAC-3 tumor groups**, covering 59,317 uniquely mapped gene symbols. TCGA and CPTAC appear separately in the cohort menu, with patient counts; pancreatic cancer is one of many choices. Availability varies by gene and outcome.
|
|
59
|
+
|
|
60
|
+
TCGA provides overall survival (OS), disease-specific survival (DSS), progression-free interval (PFI), and disease-free interval (DFI), where the source supports them. CPTAC currently provides **RNA expression and OS**. Protein abundance and CPTAC-2 survival are not included. Some rare CPTAC groups have only one or two patients and cannot support an estimable comparison. See the [coverage table and sources](docs/cptac.md).
|
|
61
|
+
|
|
62
|
+
## Read the results carefully
|
|
63
|
+
|
|
64
|
+
A curve estimates the fraction of patients who remain event-free over time. The legend gives patients (`n`) and observed events (`e`) in each group. Patients without an observed event are censored, so `e ≤ n`. Double-click a whole legend entry to edit its displayed text; the calculated counts remain in the analysis results. The p-value compares the curves; the hazard ratio compares higher with lower expression. These are unadjusted associations, not proof that a gene causes a difference or predicts an individual's outcome.
|
|
65
|
+
|
|
66
|
+
Comparing selected lower and upper percentile groups leaves out the middle patients and can increase uncertainty. Trying several genes or group definitions adds multiple comparisons; the optional q-value adjusts only for the available outcomes **within one analysis**. With one tested outcome, as in current CPTAC analyses, `q = p`; the figure shows p alone. Choose comparisons for a scientific reason and report what you explored. [Learn to read the plot](docs/user-guide.md#understand-the-figure).
|
|
67
|
+
|
|
68
|
+
JavaScript calculations are checked against Python and independently executed R `survival`. Group membership, event counts, and risk counts agree exactly in the validation suite. Numerical tolerances and the preserved PAAD reference estimates are documented in [methods and validation](docs/methods.md#validation).
|
|
69
|
+
|
|
70
|
+
## Cite the data behind your figure
|
|
71
|
+
|
|
72
|
+
Choose **Cite this analysis** beside the save/export controls. Copy the references, download **BibTeX** or **RIS** for a reference manager, or copy a methods paragraph with your gene, cohort, group sizes, exclusions, and data version. Citations follow the displayed result, even while you are choosing the next analysis.
|
|
73
|
+
|
|
74
|
+
TCGA analyses cite **TCGA-CDR** for survival outcomes, **UCSC Xena** for data distribution, and **GDC** for the data resource. CPTAC analyses cite **CPTAC-3** and **GDC** and its survival documentation. Verified original cohort papers appear alongside those sources. The analyzed patients may differ from the publication's original cohort. [Full references and citation guidance](docs/citations.md).
|
|
75
|
+
|
|
76
|
+
## Use Python or the command line
|
|
77
|
+
|
|
78
|
+
Install the tested [GitHub release](https://github.com/oncologylab/survscope/releases/tag/v0.4.3):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
python -m pip install \
|
|
82
|
+
https://github.com/oncologylab/survscope/releases/download/v0.4.3/survscope-0.4.3-py3-none-any.whl
|
|
83
|
+
survscope plot --gene SRD5A1 --cohort PAAD --format pdf svg png --outdir plots
|
|
84
|
+
survscope plot --gene TP53 --cohort CPTAC-3-LUAD \
|
|
85
|
+
--grouping percentile_groups --lower-percent 25 --upper-percent 25 --json --outdir plots
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import survscope
|
|
90
|
+
from survscope import GroupingSpec
|
|
91
|
+
|
|
92
|
+
result = survscope.analyze(
|
|
93
|
+
"SRD5A1", "PAAD",
|
|
94
|
+
grouping=GroupingSpec("percentile_groups", lower_percent=25, upper_percent=25),
|
|
95
|
+
)
|
|
96
|
+
survscope.plot(result, formats=("pdf", "svg"), output_dir="plots")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The Python package shares the comparison methods and the default figure. Q-values are off by default here too; add `--show-q` on the command line or `show_q=True` to `survscope.plot(...)` to include them. Analysis JSON retains the calculated p- and q-values regardless of figure settings. The interactive editor and its project files are browser features. PyPI publication awaits its one-time [Trusted Publisher setup](docs/publishing.md); use the GitHub wheel meanwhile.
|
|
100
|
+
|
|
101
|
+
## Reproducibility and further reading
|
|
102
|
+
|
|
103
|
+
The website displays its data version. Data releases are immutable; saved projects record both data and software versions. Browser calculations use static assets served with the site, with no external data-service requests or telemetry. Published assets contain no patient identifiers or raw expression matrices.
|
|
104
|
+
|
|
105
|
+
- [User guide](docs/user-guide.md): comparisons, editing, downloads, and common questions
|
|
106
|
+
- [Methods](docs/methods.md): grouping, statistics, reference validation, and limitations
|
|
107
|
+
- [CPTAC coverage and research](docs/cptac.md): included cohorts, sources, and exclusions
|
|
108
|
+
- [Development and releases](docs/development.md): local setup, checks, and deployment
|
|
109
|
+
- [Data format](docs/data-format.md): compact assets and provenance
|
|
110
|
+
|
|
111
|
+
For scientific use, include the software and the references provided by **Cite this analysis** for your selected cohort. SurvScope is research software, not a diagnostic or clinical decision-making tool.
|
|
112
|
+
|
|
113
|
+
Source code uses the [MIT License](LICENSE). Bundled fonts and numerical code retain their [third-party notices](web/public/third-party-notices.txt); data retain their original terms and citations.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# SurvScope
|
|
2
|
+
|
|
3
|
+
[](https://github.com/oncologylab/survscope/actions/workflows/ci.yml)
|
|
4
|
+
[](https://oncologylab.github.io/survscope/)
|
|
5
|
+
|
|
6
|
+
**Explore how a gene's RNA expression relates to survival in a cancer cohort, then edit and download the figure.** SurvScope works in your browser, without an account or installation.
|
|
7
|
+
|
|
8
|
+
[**Open SurvScope →**](https://oncologylab.github.io/survscope/) · [Step-by-step guide](docs/user-guide.md) · [Statistical methods](docs/methods.md)
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Make your first figure
|
|
13
|
+
|
|
14
|
+
1. Choose a cancer cohort and enter a gene symbol, such as **SRD5A1** or **TP53**.
|
|
15
|
+
2. Choose how to compare lower and higher expression. Start with the median, or try a percentile, the lowest/highest quarters or thirds, custom percentile groups, the mean, or a TPM threshold. Check the patient counts before running.
|
|
16
|
+
3. Select **Create survival plot**. Each panel shows a different outcome, when available.
|
|
17
|
+
4. **Double-click text on the figure to edit it.** Select and drag objects; use Properties for fonts, colors, dimensions, axes, and layout, or Layers to hide and lock objects. Optional confidence bands, censor marks, and number-at-risk tables add context.
|
|
18
|
+
5. Download **SVG**, **PDF**, or **PNG**. **Save project** lets you reopen the results and keep editing; a style preset reuses the appearance for another analysis.
|
|
19
|
+
|
|
20
|
+
The workspace fills your screen, with collapsible Analysis and Properties panels. Familiar Selection (V), Type (T), Hand (H), and Zoom (Z) tools help you arrange the figure. Icon commands explain themselves on hover or keyboard focus. Text supports bold, italic, superscripts, and subscripts, with seven bundled [font choices](docs/user-guide.md#choose-a-font), including clearly labeled Arial/Helvetica-style alternatives.
|
|
21
|
+
|
|
22
|
+
The blue/red, four-panel, 6.8-inch figure shows p-values and hazard ratios by default. Q-values are optional: enable **Properties → Survival details → Show adjusted q-value** to include them. Editing the figure's appearance does not change the calculated results.
|
|
23
|
+
|
|
24
|
+
## Which cancers and data are available?
|
|
25
|
+
|
|
26
|
+
The current release includes **all 33 TCGA cohorts and 18 CPTAC-3 tumor groups**, covering 59,317 uniquely mapped gene symbols. TCGA and CPTAC appear separately in the cohort menu, with patient counts; pancreatic cancer is one of many choices. Availability varies by gene and outcome.
|
|
27
|
+
|
|
28
|
+
TCGA provides overall survival (OS), disease-specific survival (DSS), progression-free interval (PFI), and disease-free interval (DFI), where the source supports them. CPTAC currently provides **RNA expression and OS**. Protein abundance and CPTAC-2 survival are not included. Some rare CPTAC groups have only one or two patients and cannot support an estimable comparison. See the [coverage table and sources](docs/cptac.md).
|
|
29
|
+
|
|
30
|
+
## Read the results carefully
|
|
31
|
+
|
|
32
|
+
A curve estimates the fraction of patients who remain event-free over time. The legend gives patients (`n`) and observed events (`e`) in each group. Patients without an observed event are censored, so `e ≤ n`. Double-click a whole legend entry to edit its displayed text; the calculated counts remain in the analysis results. The p-value compares the curves; the hazard ratio compares higher with lower expression. These are unadjusted associations, not proof that a gene causes a difference or predicts an individual's outcome.
|
|
33
|
+
|
|
34
|
+
Comparing selected lower and upper percentile groups leaves out the middle patients and can increase uncertainty. Trying several genes or group definitions adds multiple comparisons; the optional q-value adjusts only for the available outcomes **within one analysis**. With one tested outcome, as in current CPTAC analyses, `q = p`; the figure shows p alone. Choose comparisons for a scientific reason and report what you explored. [Learn to read the plot](docs/user-guide.md#understand-the-figure).
|
|
35
|
+
|
|
36
|
+
JavaScript calculations are checked against Python and independently executed R `survival`. Group membership, event counts, and risk counts agree exactly in the validation suite. Numerical tolerances and the preserved PAAD reference estimates are documented in [methods and validation](docs/methods.md#validation).
|
|
37
|
+
|
|
38
|
+
## Cite the data behind your figure
|
|
39
|
+
|
|
40
|
+
Choose **Cite this analysis** beside the save/export controls. Copy the references, download **BibTeX** or **RIS** for a reference manager, or copy a methods paragraph with your gene, cohort, group sizes, exclusions, and data version. Citations follow the displayed result, even while you are choosing the next analysis.
|
|
41
|
+
|
|
42
|
+
TCGA analyses cite **TCGA-CDR** for survival outcomes, **UCSC Xena** for data distribution, and **GDC** for the data resource. CPTAC analyses cite **CPTAC-3** and **GDC** and its survival documentation. Verified original cohort papers appear alongside those sources. The analyzed patients may differ from the publication's original cohort. [Full references and citation guidance](docs/citations.md).
|
|
43
|
+
|
|
44
|
+
## Use Python or the command line
|
|
45
|
+
|
|
46
|
+
Install the tested [GitHub release](https://github.com/oncologylab/survscope/releases/tag/v0.4.3):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python -m pip install \
|
|
50
|
+
https://github.com/oncologylab/survscope/releases/download/v0.4.3/survscope-0.4.3-py3-none-any.whl
|
|
51
|
+
survscope plot --gene SRD5A1 --cohort PAAD --format pdf svg png --outdir plots
|
|
52
|
+
survscope plot --gene TP53 --cohort CPTAC-3-LUAD \
|
|
53
|
+
--grouping percentile_groups --lower-percent 25 --upper-percent 25 --json --outdir plots
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import survscope
|
|
58
|
+
from survscope import GroupingSpec
|
|
59
|
+
|
|
60
|
+
result = survscope.analyze(
|
|
61
|
+
"SRD5A1", "PAAD",
|
|
62
|
+
grouping=GroupingSpec("percentile_groups", lower_percent=25, upper_percent=25),
|
|
63
|
+
)
|
|
64
|
+
survscope.plot(result, formats=("pdf", "svg"), output_dir="plots")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The Python package shares the comparison methods and the default figure. Q-values are off by default here too; add `--show-q` on the command line or `show_q=True` to `survscope.plot(...)` to include them. Analysis JSON retains the calculated p- and q-values regardless of figure settings. The interactive editor and its project files are browser features. PyPI publication awaits its one-time [Trusted Publisher setup](docs/publishing.md); use the GitHub wheel meanwhile.
|
|
68
|
+
|
|
69
|
+
## Reproducibility and further reading
|
|
70
|
+
|
|
71
|
+
The website displays its data version. Data releases are immutable; saved projects record both data and software versions. Browser calculations use static assets served with the site, with no external data-service requests or telemetry. Published assets contain no patient identifiers or raw expression matrices.
|
|
72
|
+
|
|
73
|
+
- [User guide](docs/user-guide.md): comparisons, editing, downloads, and common questions
|
|
74
|
+
- [Methods](docs/methods.md): grouping, statistics, reference validation, and limitations
|
|
75
|
+
- [CPTAC coverage and research](docs/cptac.md): included cohorts, sources, and exclusions
|
|
76
|
+
- [Development and releases](docs/development.md): local setup, checks, and deployment
|
|
77
|
+
- [Data format](docs/data-format.md): compact assets and provenance
|
|
78
|
+
|
|
79
|
+
For scientific use, include the software and the references provided by **Cite this analysis** for your selected cohort. SurvScope is research software, not a diagnostic or clinical decision-making tool.
|
|
80
|
+
|
|
81
|
+
Source code uses the [MIT License](LICENSE). Bundled fonts and numerical code retain their [third-party notices](web/public/third-party-notices.txt); data retain their original terms and citations.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "survscope"
|
|
7
|
+
version = "0.4.3"
|
|
8
|
+
description = "Reproducible TCGA and CPTAC Kaplan-Meier survival plots from compact static data"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [{ name = "Yaoxiang Li" }]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
keywords = ["TCGA", "CPTAC", "survival", "Kaplan-Meier", "oncology", "bioinformatics"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"matplotlib>=3.8,<4",
|
|
26
|
+
"numpy>=1.26,<3",
|
|
27
|
+
"scipy>=1.12,<2",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
test = [
|
|
32
|
+
"build>=1.2,<2",
|
|
33
|
+
"pytest>=8,<9",
|
|
34
|
+
"pytest-cov>=5,<8",
|
|
35
|
+
"ruff>=0.11,<1",
|
|
36
|
+
"twine>=6,<7",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://oncologylab.github.io/survscope/"
|
|
41
|
+
Repository = "https://github.com/oncologylab/survscope"
|
|
42
|
+
Issues = "https://github.com/oncologylab/survscope/issues"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
survscope = "survscope.cli:main"
|
|
46
|
+
survscope-build-data = "survscope.builder:main"
|
|
47
|
+
|
|
48
|
+
[tool.setuptools]
|
|
49
|
+
package-dir = { "" = "src" }
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["src"]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
addopts = "-ra"
|
|
57
|
+
|
|
58
|
+
[tool.ruff]
|
|
59
|
+
line-length = 100
|
|
60
|
+
target-version = "py310"
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint]
|
|
63
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""SurvScope public Python API."""
|
|
2
|
+
|
|
3
|
+
from .analysis import analyze
|
|
4
|
+
from .data import DataStore
|
|
5
|
+
from .grouping import GroupingSpec
|
|
6
|
+
from .models import EndpointResult, SurvivalAnalysis
|
|
7
|
+
from .plotting import plot
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"DataStore",
|
|
11
|
+
"GroupingSpec",
|
|
12
|
+
"EndpointResult",
|
|
13
|
+
"SurvivalAnalysis",
|
|
14
|
+
"analyze",
|
|
15
|
+
"available_cohorts",
|
|
16
|
+
"plot",
|
|
17
|
+
"search_genes",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
__version__ = "0.4.3"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def available_cohorts(store: DataStore | None = None) -> list[dict]:
|
|
24
|
+
"""Return cohort records from the selected data release."""
|
|
25
|
+
return (store or DataStore()).available_cohorts()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def search_genes(
|
|
29
|
+
query: str,
|
|
30
|
+
cohort: str | None = None,
|
|
31
|
+
store: DataStore | None = None,
|
|
32
|
+
) -> list[dict]:
|
|
33
|
+
"""Search supported gene symbols or Ensembl identifiers."""
|
|
34
|
+
return (store or DataStore()).search_genes(query, cohort=cohort)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Survival analysis over compact SurvScope data."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from .constants import ENDPOINTS, MONTH_DAYS
|
|
10
|
+
from .data import DataStore, GeneData
|
|
11
|
+
from .grouping import GroupingSpec, assign_groups, grouping_label, normalize_grouping
|
|
12
|
+
from .models import Curve, EndpointResult, SurvivalAnalysis
|
|
13
|
+
from .statistics import COX_MESSAGES, bh_fdr, cox_fit, kaplan_meier, km_timeline, logrank_test
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Sequence
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _number_array(values: Sequence[float | int | None], dtype=float) -> np.ndarray:
|
|
20
|
+
return np.asarray([np.nan if value is None else value for value in values], dtype=dtype)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def analyze(
|
|
24
|
+
gene: str,
|
|
25
|
+
cohort: str,
|
|
26
|
+
cutoff: str | float = "median",
|
|
27
|
+
*,
|
|
28
|
+
store: DataStore | None = None,
|
|
29
|
+
grouping: GroupingSpec | dict | None = None,
|
|
30
|
+
) -> SurvivalAnalysis:
|
|
31
|
+
"""Analyze one gene in one TCGA or CPTAC cohort.
|
|
32
|
+
|
|
33
|
+
A numeric cutoff is interpreted as TPM. The special value ``"median"``
|
|
34
|
+
applies the exact endpoint-specific median grouping recorded during the
|
|
35
|
+
data build.
|
|
36
|
+
"""
|
|
37
|
+
data_store = store or DataStore()
|
|
38
|
+
gene_data = data_store.load_gene(gene, cohort)
|
|
39
|
+
return analyze_gene_data(gene_data, cutoff=cutoff, grouping=grouping)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def analyze_gene_data(
|
|
43
|
+
data: GeneData,
|
|
44
|
+
cutoff: str | float = "median",
|
|
45
|
+
*,
|
|
46
|
+
grouping: GroupingSpec | dict | None = None,
|
|
47
|
+
) -> SurvivalAnalysis:
|
|
48
|
+
spec = normalize_grouping(cutoff, grouping)
|
|
49
|
+
|
|
50
|
+
tpm = data.expression_tpm
|
|
51
|
+
results: dict[str, EndpointResult] = {}
|
|
52
|
+
pvalues: list[float] = []
|
|
53
|
+
for endpoint in ENDPOINTS:
|
|
54
|
+
clinical = data.clinical["endpoints"][endpoint]
|
|
55
|
+
time = _number_array(clinical["time"], dtype=float)
|
|
56
|
+
event = _number_array(clinical["event"], dtype=float)
|
|
57
|
+
valid = np.isfinite(tpm) & np.isfinite(time) & np.isin(event, [0, 1]) & (time > 0)
|
|
58
|
+
indices = np.flatnonzero(valid)
|
|
59
|
+
endpoint_time = time[valid]
|
|
60
|
+
endpoint_event = event[valid].astype(int)
|
|
61
|
+
endpoint_tpm = tpm[valid]
|
|
62
|
+
|
|
63
|
+
low_mask, high_mask, lower_threshold, upper_threshold = assign_groups(
|
|
64
|
+
endpoint_tpm,
|
|
65
|
+
indices,
|
|
66
|
+
data.medians.get(endpoint, {}),
|
|
67
|
+
spec,
|
|
68
|
+
)
|
|
69
|
+
included = low_mask | high_mask
|
|
70
|
+
eligible_n = len(endpoint_time)
|
|
71
|
+
excluded_middle = int(np.sum(~included))
|
|
72
|
+
endpoint_time = endpoint_time[included]
|
|
73
|
+
endpoint_event = endpoint_event[included]
|
|
74
|
+
high = high_mask[included]
|
|
75
|
+
low = ~high
|
|
76
|
+
endpoint_cutoff = lower_threshold if lower_threshold == upper_threshold else np.nan
|
|
77
|
+
|
|
78
|
+
warning = ""
|
|
79
|
+
if len(endpoint_time) == 0:
|
|
80
|
+
warning = "No endpoint-valid samples."
|
|
81
|
+
elif not bool(np.any(low)) or not bool(np.any(high)):
|
|
82
|
+
warning = "The cutoff leaves one expression group empty."
|
|
83
|
+
elif int(np.sum(endpoint_event[low])) == 0 or int(np.sum(endpoint_event[high])) == 0:
|
|
84
|
+
warning = "At least one group has no observed events; inferential statistics may be NA."
|
|
85
|
+
|
|
86
|
+
chi2, pvalue = logrank_test(endpoint_time, endpoint_event, high.astype(int))
|
|
87
|
+
hazard_ratio, cox_p, cox_status = cox_fit(
|
|
88
|
+
endpoint_time,
|
|
89
|
+
endpoint_event,
|
|
90
|
+
high.astype(int),
|
|
91
|
+
)
|
|
92
|
+
if cox_status != "ok" and len(endpoint_time):
|
|
93
|
+
warning = COX_MESSAGES[cox_status]
|
|
94
|
+
low_x, low_y = kaplan_meier(endpoint_time[low], endpoint_event[low])
|
|
95
|
+
high_x, high_y = kaplan_meier(endpoint_time[high], endpoint_event[high])
|
|
96
|
+
quality = clinical.get("quality", "caution")
|
|
97
|
+
quality_note = clinical.get("quality_note", "")
|
|
98
|
+
result = EndpointResult(
|
|
99
|
+
endpoint=endpoint,
|
|
100
|
+
quality=quality,
|
|
101
|
+
quality_note=quality_note,
|
|
102
|
+
n=len(endpoint_time),
|
|
103
|
+
n_low=int(np.sum(low)),
|
|
104
|
+
n_high=int(np.sum(high)),
|
|
105
|
+
events=int(np.sum(endpoint_event)),
|
|
106
|
+
events_low=int(np.sum(endpoint_event[low])),
|
|
107
|
+
events_high=int(np.sum(endpoint_event[high])),
|
|
108
|
+
cutoff_tpm=endpoint_cutoff,
|
|
109
|
+
logrank_chi2=chi2,
|
|
110
|
+
logrank_p=pvalue,
|
|
111
|
+
cox_hr=hazard_ratio,
|
|
112
|
+
cox_p=cox_p,
|
|
113
|
+
low=Curve(
|
|
114
|
+
x_months=low_x / MONTH_DAYS,
|
|
115
|
+
survival=low_y,
|
|
116
|
+
n=int(np.sum(low)),
|
|
117
|
+
events=int(np.sum(endpoint_event[low])),
|
|
118
|
+
timeline=km_timeline(endpoint_time[low], endpoint_event[low]),
|
|
119
|
+
),
|
|
120
|
+
high=Curve(
|
|
121
|
+
x_months=high_x / MONTH_DAYS,
|
|
122
|
+
survival=high_y,
|
|
123
|
+
n=int(np.sum(high)),
|
|
124
|
+
events=int(np.sum(endpoint_event[high])),
|
|
125
|
+
timeline=km_timeline(endpoint_time[high], endpoint_event[high]),
|
|
126
|
+
),
|
|
127
|
+
warning=warning,
|
|
128
|
+
eligible_n=eligible_n,
|
|
129
|
+
excluded_middle=excluded_middle,
|
|
130
|
+
lower_threshold=lower_threshold,
|
|
131
|
+
upper_threshold=upper_threshold,
|
|
132
|
+
cox_status=cox_status,
|
|
133
|
+
)
|
|
134
|
+
results[endpoint] = result
|
|
135
|
+
pvalues.append(pvalue)
|
|
136
|
+
|
|
137
|
+
for endpoint, qvalue in zip(ENDPOINTS, bh_fdr(pvalues), strict=True):
|
|
138
|
+
results[endpoint].logrank_q = qvalue
|
|
139
|
+
|
|
140
|
+
return SurvivalAnalysis(
|
|
141
|
+
gene=data.symbol,
|
|
142
|
+
ensembl=data.ensembl,
|
|
143
|
+
cohort=data.cohort,
|
|
144
|
+
cohort_label=data.cohort_label,
|
|
145
|
+
cutoff="median" if spec.kind == "median" else spec.threshold,
|
|
146
|
+
data_version=data.data_version,
|
|
147
|
+
source_expression=data.sources["expression"]["label"],
|
|
148
|
+
source_survival=data.sources["survival"]["label"],
|
|
149
|
+
endpoints=results,
|
|
150
|
+
grouping=spec.to_dict(),
|
|
151
|
+
grouping_label=grouping_label(spec),
|
|
152
|
+
)
|