pystatsbio 1.0.0__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.
- pystatsbio-1.0.0/.github/workflows/publish.yml +45 -0
- pystatsbio-1.0.0/.gitignore +31 -0
- pystatsbio-1.0.0/CHANGELOG.md +34 -0
- pystatsbio-1.0.0/CLAUDE.md +190 -0
- pystatsbio-1.0.0/LICENSE +21 -0
- pystatsbio-1.0.0/PKG-INFO +287 -0
- pystatsbio-1.0.0/README.md +240 -0
- pystatsbio-1.0.0/docs/Forge.md +291 -0
- pystatsbio-1.0.0/docs/Makefile +14 -0
- pystatsbio-1.0.0/docs/PROGRESS.md +169 -0
- pystatsbio-1.0.0/docs/PYSTATSBIO_CONTEXT.md +785 -0
- pystatsbio-1.0.0/docs/Powerhouse.md +25 -0
- pystatsbio-1.0.0/docs/SGC_BIO_DIRECTIVE.md +443 -0
- pystatsbio-1.0.0/docs/SGC_CORE_DIRECTIVE.md +606 -0
- pystatsbio-1.0.0/docs/_build/html/.buildinfo +4 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/__intersphinx_cache__/numpy_objects.inv +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/__intersphinx_cache__/python_objects.inv +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/__intersphinx_cache__/scipy_objects.inv +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/diagnostic.doctree +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/doseresponse.doctree +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/environment.pickle +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/index.doctree +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/pk.doctree +0 -0
- pystatsbio-1.0.0/docs/_build/html/.doctrees/power.doctree +0 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/index.html +300 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/diagnostic/_accuracy.html +486 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/diagnostic/_batch.html +579 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/diagnostic/_common.html +399 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/diagnostic/_cutoff.html +400 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/diagnostic/_roc.html +678 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/doseresponse/_batch.html +610 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/doseresponse/_bmd.html +495 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/doseresponse/_common.html +426 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/doseresponse/_fit.html +617 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/doseresponse/_models.html +537 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/doseresponse/_potency.html +464 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/pk/_common.html +345 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/pk/_nca.html +748 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_anova.html +565 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_cluster.html +428 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_common.html +437 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_crossover.html +475 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_means.html +528 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_noninferiority.html +725 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_proportions.html +496 -0
- pystatsbio-1.0.0/docs/_build/html/_modules/pystatsbio/power/_survival.html +524 -0
- pystatsbio-1.0.0/docs/_build/html/_sources/diagnostic.rst.txt +11 -0
- pystatsbio-1.0.0/docs/_build/html/_sources/doseresponse.rst.txt +11 -0
- pystatsbio-1.0.0/docs/_build/html/_sources/index.rst.txt +20 -0
- pystatsbio-1.0.0/docs/_build/html/_sources/pk.rst.txt +11 -0
- pystatsbio-1.0.0/docs/_build/html/_sources/power.rst.txt +11 -0
- pystatsbio-1.0.0/docs/_build/html/_static/base-stemmer.js +476 -0
- pystatsbio-1.0.0/docs/_build/html/_static/basic.css +906 -0
- pystatsbio-1.0.0/docs/_build/html/_static/custom.css +5 -0
- pystatsbio-1.0.0/docs/_build/html/_static/debug.css +69 -0
- pystatsbio-1.0.0/docs/_build/html/_static/doctools.js +150 -0
- pystatsbio-1.0.0/docs/_build/html/_static/documentation_options.js +13 -0
- pystatsbio-1.0.0/docs/_build/html/_static/english-stemmer.js +1066 -0
- pystatsbio-1.0.0/docs/_build/html/_static/file.png +0 -0
- pystatsbio-1.0.0/docs/_build/html/_static/language_data.js +13 -0
- pystatsbio-1.0.0/docs/_build/html/_static/minus.png +0 -0
- pystatsbio-1.0.0/docs/_build/html/_static/plus.png +0 -0
- pystatsbio-1.0.0/docs/_build/html/_static/pygments.css +250 -0
- pystatsbio-1.0.0/docs/_build/html/_static/scripts/furo-extensions.js +0 -0
- pystatsbio-1.0.0/docs/_build/html/_static/scripts/furo.js +3 -0
- pystatsbio-1.0.0/docs/_build/html/_static/scripts/furo.js.LICENSE.txt +7 -0
- pystatsbio-1.0.0/docs/_build/html/_static/scripts/furo.js.map +1 -0
- pystatsbio-1.0.0/docs/_build/html/_static/searchtools.js +693 -0
- pystatsbio-1.0.0/docs/_build/html/_static/skeleton.css +296 -0
- pystatsbio-1.0.0/docs/_build/html/_static/sphinx_highlight.js +159 -0
- pystatsbio-1.0.0/docs/_build/html/_static/styles/furo-extensions.css +2 -0
- pystatsbio-1.0.0/docs/_build/html/_static/styles/furo-extensions.css.map +1 -0
- pystatsbio-1.0.0/docs/_build/html/_static/styles/furo.css +2 -0
- pystatsbio-1.0.0/docs/_build/html/_static/styles/furo.css.map +1 -0
- pystatsbio-1.0.0/docs/_build/html/diagnostic.html +981 -0
- pystatsbio-1.0.0/docs/_build/html/doseresponse.html +1134 -0
- pystatsbio-1.0.0/docs/_build/html/genindex.html +849 -0
- pystatsbio-1.0.0/docs/_build/html/index.html +368 -0
- pystatsbio-1.0.0/docs/_build/html/objects.inv +0 -0
- pystatsbio-1.0.0/docs/_build/html/pk.html +494 -0
- pystatsbio-1.0.0/docs/_build/html/power.html +818 -0
- pystatsbio-1.0.0/docs/_build/html/py-modindex.html +321 -0
- pystatsbio-1.0.0/docs/_build/html/search.html +293 -0
- pystatsbio-1.0.0/docs/_build/html/searchindex.js +1 -0
- pystatsbio-1.0.0/docs/_static/custom.css +5 -0
- pystatsbio-1.0.0/docs/conf.py +63 -0
- pystatsbio-1.0.0/docs/diagnostic.rst +11 -0
- pystatsbio-1.0.0/docs/doseresponse.rst +11 -0
- pystatsbio-1.0.0/docs/index.rst +20 -0
- pystatsbio-1.0.0/docs/pk.rst +11 -0
- pystatsbio-1.0.0/docs/power.rst +11 -0
- pystatsbio-1.0.0/pyproject.toml +97 -0
- pystatsbio-1.0.0/pystatsbio/__init__.py +24 -0
- pystatsbio-1.0.0/pystatsbio/diagnostic/__init__.py +27 -0
- pystatsbio-1.0.0/pystatsbio/diagnostic/_accuracy.py +193 -0
- pystatsbio-1.0.0/pystatsbio/diagnostic/_batch.py +291 -0
- pystatsbio-1.0.0/pystatsbio/diagnostic/_common.py +110 -0
- pystatsbio-1.0.0/pystatsbio/diagnostic/_cutoff.py +114 -0
- pystatsbio-1.0.0/pystatsbio/diagnostic/_roc.py +386 -0
- pystatsbio-1.0.0/pystatsbio/doseresponse/__init__.py +50 -0
- pystatsbio-1.0.0/pystatsbio/doseresponse/_batch.py +332 -0
- pystatsbio-1.0.0/pystatsbio/doseresponse/_bmd.py +256 -0
- pystatsbio-1.0.0/pystatsbio/doseresponse/_common.py +123 -0
- pystatsbio-1.0.0/pystatsbio/doseresponse/_fit.py +334 -0
- pystatsbio-1.0.0/pystatsbio/doseresponse/_models.py +242 -0
- pystatsbio-1.0.0/pystatsbio/doseresponse/_potency.py +172 -0
- pystatsbio-1.0.0/pystatsbio/pk/__init__.py +19 -0
- pystatsbio-1.0.0/pystatsbio/pk/_common.py +57 -0
- pystatsbio-1.0.0/pystatsbio/pk/_nca.py +480 -0
- pystatsbio-1.0.0/pystatsbio/power/__init__.py +39 -0
- pystatsbio-1.0.0/pystatsbio/power/_anova.py +281 -0
- pystatsbio-1.0.0/pystatsbio/power/_cluster.py +145 -0
- pystatsbio-1.0.0/pystatsbio/power/_common.py +149 -0
- pystatsbio-1.0.0/pystatsbio/power/_crossover.py +192 -0
- pystatsbio-1.0.0/pystatsbio/power/_means.py +243 -0
- pystatsbio-1.0.0/pystatsbio/power/_noninferiority.py +433 -0
- pystatsbio-1.0.0/pystatsbio/power/_proportions.py +211 -0
- pystatsbio-1.0.0/pystatsbio/power/_survival.py +242 -0
- pystatsbio-1.0.0/pystatsbio/py.typed +0 -0
- pystatsbio-1.0.0/tests/__init__.py +0 -0
- pystatsbio-1.0.0/tests/diagnostic/__init__.py +0 -0
- pystatsbio-1.0.0/tests/diagnostic/test_accuracy.py +205 -0
- pystatsbio-1.0.0/tests/diagnostic/test_batch.py +138 -0
- pystatsbio-1.0.0/tests/diagnostic/test_common.py +155 -0
- pystatsbio-1.0.0/tests/diagnostic/test_cutoff.py +95 -0
- pystatsbio-1.0.0/tests/diagnostic/test_roc.py +213 -0
- pystatsbio-1.0.0/tests/diagnostic/test_roc_test.py +92 -0
- pystatsbio-1.0.0/tests/doseresponse/__init__.py +0 -0
- pystatsbio-1.0.0/tests/doseresponse/test_batch.py +131 -0
- pystatsbio-1.0.0/tests/doseresponse/test_bmd.py +133 -0
- pystatsbio-1.0.0/tests/doseresponse/test_common.py +165 -0
- pystatsbio-1.0.0/tests/doseresponse/test_fit.py +188 -0
- pystatsbio-1.0.0/tests/doseresponse/test_models.py +137 -0
- pystatsbio-1.0.0/tests/doseresponse/test_potency.py +120 -0
- pystatsbio-1.0.0/tests/pk/__init__.py +0 -0
- pystatsbio-1.0.0/tests/pk/test_common.py +146 -0
- pystatsbio-1.0.0/tests/pk/test_nca.py +577 -0
- pystatsbio-1.0.0/tests/power/__init__.py +0 -0
- pystatsbio-1.0.0/tests/power/test_anova.py +80 -0
- pystatsbio-1.0.0/tests/power/test_cluster.py +59 -0
- pystatsbio-1.0.0/tests/power/test_common.py +189 -0
- pystatsbio-1.0.0/tests/power/test_crossover.py +55 -0
- pystatsbio-1.0.0/tests/power/test_means.py +167 -0
- pystatsbio-1.0.0/tests/power/test_noninferiority.py +103 -0
- pystatsbio-1.0.0/tests/power/test_proportions.py +76 -0
- pystatsbio-1.0.0/tests/power/test_survival.py +67 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install build tools
|
|
20
|
+
run: pip install build
|
|
21
|
+
|
|
22
|
+
- name: Build sdist and wheel
|
|
23
|
+
run: python -m build
|
|
24
|
+
|
|
25
|
+
- name: Upload build artifacts
|
|
26
|
+
uses: actions/upload-artifact@v4
|
|
27
|
+
with:
|
|
28
|
+
name: dist
|
|
29
|
+
path: dist/
|
|
30
|
+
|
|
31
|
+
publish-pypi:
|
|
32
|
+
needs: build
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
environment: pypi
|
|
35
|
+
permissions:
|
|
36
|
+
id-token: write
|
|
37
|
+
steps:
|
|
38
|
+
- name: Download build artifacts
|
|
39
|
+
uses: actions/download-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
|
|
44
|
+
- name: Publish to PyPI
|
|
45
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# IDE
|
|
16
|
+
.idea/
|
|
17
|
+
.vscode/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
|
|
26
|
+
# OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
Thumbs.db
|
|
29
|
+
|
|
30
|
+
# Fixtures (generated, but tracked)
|
|
31
|
+
# tests/fixtures/*.json are tracked in git
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
Initial release.
|
|
6
|
+
|
|
7
|
+
### Modules
|
|
8
|
+
|
|
9
|
+
- **`power/`** — Sample size and power calculations for clinical trial designs:
|
|
10
|
+
two-sample and paired t-tests, proportions (chi-squared, Fisher exact), log-rank
|
|
11
|
+
(survival), one-way and factorial ANOVA, non-inferiority/equivalence/superiority
|
|
12
|
+
for means and proportions, crossover bioequivalence (PowerTOST method), and
|
|
13
|
+
cluster-randomized trials. Validated against R packages `pwr`, `TrialSize`,
|
|
14
|
+
`gsDesign`, `PowerTOST`, and `samplesize`.
|
|
15
|
+
|
|
16
|
+
- **`doseresponse/`** — Dose-response modeling for preclinical pharmacology:
|
|
17
|
+
4-parameter log-logistic (4PL/LL.4), 5-parameter log-logistic (5PL/LL.5),
|
|
18
|
+
Weibull-1, Weibull-2, Brain-Cousens hormesis models. EC50/IC50 estimation,
|
|
19
|
+
relative potency (parallelism-tested), benchmark dose (BMD/BMDL) analysis,
|
|
20
|
+
and GPU-accelerated batch fitting for high-throughput screening. Validated
|
|
21
|
+
against R packages `drc` and `BMDS`.
|
|
22
|
+
|
|
23
|
+
- **`diagnostic/`** — Diagnostic accuracy analysis for biomarker evaluation:
|
|
24
|
+
ROC curves with DeLong AUC and confidence intervals, Delong AUC comparison test,
|
|
25
|
+
sensitivity/specificity/PPV/NPV/likelihood ratios at any threshold, optimal
|
|
26
|
+
cutoff selection (Youden, min-distance, cost-weighted), and batch AUC computation
|
|
27
|
+
for biomarker panel screening. Validated against R packages `pROC`,
|
|
28
|
+
`OptimalCutpoints`, and `epiR`.
|
|
29
|
+
|
|
30
|
+
- **`pk/`** — Non-compartmental pharmacokinetic analysis (NCA): AUC (linear,
|
|
31
|
+
log-linear, linear-up/log-down trapezoidal), Cmax, Tmax, terminal elimination
|
|
32
|
+
rate constant (lambda_z), half-life, clearance (IV and extravascular), volume of
|
|
33
|
+
distribution, AUMC, and MRT. Validated against R packages `PKNCA` and
|
|
34
|
+
`NonCompart`.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Coding Bible
|
|
2
|
+
|
|
3
|
+
## Preamble
|
|
4
|
+
|
|
5
|
+
This document is not a style guide. It is not a set of preferences. It is not a list of
|
|
6
|
+
suggestions that can be weighed against convenience or time pressure.
|
|
7
|
+
|
|
8
|
+
Every rule in this document exists because its absence has caused real, costly, documented
|
|
9
|
+
failures in real systems — bugs that took days to trace, security holes that went undetected
|
|
10
|
+
for months, codebases that became unmaintainable within a year of being written. The rules
|
|
11
|
+
are the scar tissue. They are what you get when you ask experienced engineers what they wish
|
|
12
|
+
someone had forced them to do the first time.
|
|
13
|
+
|
|
14
|
+
When you are implementing something and a rule feels inconvenient, that feeling is a signal,
|
|
15
|
+
not a reason to deviate. Inconvenience usually means the rule is doing its job — preventing
|
|
16
|
+
the path of least resistance from becoming a future liability.
|
|
17
|
+
|
|
18
|
+
**These rules are non-negotiable and not subject to contextual override.** The following
|
|
19
|
+
exceptions are not acceptable justifications for deviation:
|
|
20
|
+
|
|
21
|
+
- "This is a prototype / MVP / quick fix." Prototypes become production. Quick fixes become
|
|
22
|
+
permanent. Code written without discipline under time pressure is the most dangerous code
|
|
23
|
+
that exists.
|
|
24
|
+
- "This is a small module that doesn't need it." Small modules grow. The time to enforce
|
|
25
|
+
structure is before the module is large enough to make restructuring painful.
|
|
26
|
+
- "The architecture requires it." If the architecture requires violating these rules, the
|
|
27
|
+
architecture is wrong. Fix the architecture.
|
|
28
|
+
- "It would take too long to do it correctly." It will take longer to fix it later. It always
|
|
29
|
+
does.
|
|
30
|
+
|
|
31
|
+
If a genuine, documented edge case exists where a rule cannot be followed, that exception
|
|
32
|
+
must be explicitly marked in a comment at the relevant location, stating which rule is being
|
|
33
|
+
waived, why, and what mitigating measures are in place. Undocumented deviations are bugs.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## The Underlying Principle
|
|
38
|
+
|
|
39
|
+
All seven rules are expressions of one idea:
|
|
40
|
+
|
|
41
|
+
**Make the wrong thing hard to do accidentally.**
|
|
42
|
+
|
|
43
|
+
Good architecture is not about making correct behavior possible. Correct behavior is always
|
|
44
|
+
possible. Good architecture is about making incorrect behavior require deliberate, visible,
|
|
45
|
+
documented effort — so that mistakes are loud, localized, and recoverable rather than silent,
|
|
46
|
+
diffuse, and permanent.
|
|
47
|
+
|
|
48
|
+
When in doubt, ask: *am I making the wrong thing easy to do accidentally?* If yes, stop and
|
|
49
|
+
apply the relevant rule before continuing.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
These rules are non-negotiable. They exist because every one of them corresponds to a
|
|
54
|
+
real failure mode with real consequences. If you think a rule doesn't apply to a specific
|
|
55
|
+
case, you are probably wrong. If you are certain it doesn't apply, document why explicitly
|
|
56
|
+
in a comment before proceeding.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 1. Fail Fast, Fail Loud, No Defaults
|
|
61
|
+
|
|
62
|
+
Do not insert default behavior that fails silently. If something breaks, the system must
|
|
63
|
+
break immediately, obviously, and traceably.
|
|
64
|
+
|
|
65
|
+
**Rules:**
|
|
66
|
+
- Raise explicit, descriptive errors. Never swallow exceptions.
|
|
67
|
+
- Never return a default value to mask a missing or invalid state.
|
|
68
|
+
- Log the failure with enough context to reproduce it.
|
|
69
|
+
|
|
70
|
+
**Corollary — No Optimistic Assumptions:**
|
|
71
|
+
Do not assume inputs are valid, dependencies are available, or external calls succeed.
|
|
72
|
+
Assert and verify. If an assumption is required, document it and enforce it with a guard.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 2. Trust Your Neighbors — Validate Input, Not Output
|
|
77
|
+
|
|
78
|
+
You own your output contract. You do not trust anyone else's input contract.
|
|
79
|
+
|
|
80
|
+
**Rules:**
|
|
81
|
+
- All external inputs (API calls, user input, file reads, environment variables) must be
|
|
82
|
+
validated at the boundary before use.
|
|
83
|
+
- Internal module output does not need re-validation by the caller — the module is
|
|
84
|
+
responsible for the correctness of what it emits.
|
|
85
|
+
|
|
86
|
+
**Corollary — Define Your Contracts:**
|
|
87
|
+
Every module must have an explicit, documented input/output contract. If a caller violates
|
|
88
|
+
the input contract, the module must fail loudly (see Rule 1), not silently compensate.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 3. UNIX Philosophy — One Module, One Job
|
|
93
|
+
|
|
94
|
+
Each module does one thing only, and does that one thing well. If a module needs to do
|
|
95
|
+
multiple things, it must be split into multiple files.
|
|
96
|
+
|
|
97
|
+
**Rules:**
|
|
98
|
+
- No file should be the sole owner of more than one domain concept.
|
|
99
|
+
- If you find yourself writing "and" when describing what a file does, split it.
|
|
100
|
+
- Tight coupling between modules is a design failure, not an implementation detail.
|
|
101
|
+
|
|
102
|
+
**Corollary — No God Files:**
|
|
103
|
+
A module that knows everything, owns everything, or touches everything is a liability.
|
|
104
|
+
If removing a module would require changes across more than two other modules, it is
|
|
105
|
+
probably doing too much.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 4. LOC Limits — No Bloated Monoliths
|
|
110
|
+
|
|
111
|
+
Files must not become monoliths. Complexity must be managed through decomposition,
|
|
112
|
+
not consolidation.
|
|
113
|
+
|
|
114
|
+
**Rules:**
|
|
115
|
+
- **Hard limit: 500 lines of code** (comments and docstrings excluded). Do not exceed this
|
|
116
|
+
under any circumstances.
|
|
117
|
+
- **Soft limit: 400 lines of code.** Above 400 lines, actively look for split opportunities.
|
|
118
|
+
- If a file "requires" more than 500 lines, that is a signal the abstraction is wrong —
|
|
119
|
+
split it into multiple focused files.
|
|
120
|
+
|
|
121
|
+
**Corollary — Splitting Is Not Optional:**
|
|
122
|
+
Exceeding the hard limit is never acceptable, even temporarily. Split first, implement after.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 5. No Hidden State — Explicit Data Flow
|
|
127
|
+
|
|
128
|
+
All state must be explicitly passed or managed through clearly defined interfaces.
|
|
129
|
+
Hidden state is a bug waiting to be discovered at the worst possible time.
|
|
130
|
+
|
|
131
|
+
**Rules:**
|
|
132
|
+
- No global variables. No module-level mutable state unless explicitly documented and
|
|
133
|
+
architecturally justified.
|
|
134
|
+
- No implicit state — if a function's behavior depends on something, that something must
|
|
135
|
+
appear in its signature or be explicitly injected.
|
|
136
|
+
- No hidden coupling between modules. If two modules share state, that relationship must
|
|
137
|
+
be visible and intentional.
|
|
138
|
+
|
|
139
|
+
**Corollary — Spooky Action at a Distance Is a Bug:**
|
|
140
|
+
If changing module A causes unexpected behavior in module B, and there is no explicit
|
|
141
|
+
interface between them, the architecture is broken. Fix the architecture, not the symptom.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 6. Deterministic Behavior by Default
|
|
146
|
+
|
|
147
|
+
All functions must be deterministic unless explicitly documented otherwise.
|
|
148
|
+
|
|
149
|
+
**Rules:**
|
|
150
|
+
- No randomness, time-dependent logic, or non-deterministic behavior without explicit
|
|
151
|
+
documentation and an injectable seed or clock interface for testing.
|
|
152
|
+
- Functions that depend on system time, random state, or external non-deterministic
|
|
153
|
+
sources must be flagged with a `# NON-DETERMINISTIC:` comment explaining why.
|
|
154
|
+
- Non-deterministic behavior must be isolatable — wrap it so the rest of the system
|
|
155
|
+
can be tested deterministically.
|
|
156
|
+
|
|
157
|
+
**Corollary — Reproducibility Is Not Optional:**
|
|
158
|
+
If a bug cannot be reliably reproduced, it cannot be reliably fixed. Non-determinism
|
|
159
|
+
is the enemy of reproducibility. Treat it like a dependency that must be explicitly
|
|
160
|
+
managed, not a feature.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 7. Tests Are First-Class Citizens
|
|
165
|
+
|
|
166
|
+
Every module must have corresponding tests. Tests are not optional, not afterthoughts,
|
|
167
|
+
and not someone else's job.
|
|
168
|
+
|
|
169
|
+
**Rules:**
|
|
170
|
+
- Every module must have a corresponding test file covering:
|
|
171
|
+
- **Normal cases** — expected inputs produce expected outputs.
|
|
172
|
+
- **Edge cases** — boundary conditions, empty inputs, maximum values, type boundaries.
|
|
173
|
+
- **Failure cases** — invalid inputs produce the correct explicit errors (see Rule 1).
|
|
174
|
+
- No module ships without tests. No exceptions.
|
|
175
|
+
- Tests must be runnable in isolation — no test should depend on the state left by
|
|
176
|
+
another test.
|
|
177
|
+
|
|
178
|
+
**Corollary — Untested Code Is Unverified Assumptions:**
|
|
179
|
+
If there is no test for a behavior, that behavior is assumed, not verified. Assumptions
|
|
180
|
+
accumulate into system failures. If you cannot write a test for something, that is a signal
|
|
181
|
+
the design is wrong.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Meta-Rule
|
|
186
|
+
|
|
187
|
+
If any of these rules feel inconvenient, that feeling is the point. These rules exist
|
|
188
|
+
precisely because the convenient path leads to the failure modes each rule was written
|
|
189
|
+
to prevent. When in doubt, ask: *am I making the wrong thing easy to do accidentally?*
|
|
190
|
+
If yes, apply the relevant rule.
|
pystatsbio-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Hai-Shuo
|
|
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.
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pystatsbio
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Biotech and pharmaceutical statistical computing for Python
|
|
5
|
+
Project-URL: Homepage, https://sgcx.org/technology/pystatsbio/
|
|
6
|
+
Project-URL: Documentation, https://sgcx.org/docs/pystatsbio/
|
|
7
|
+
Project-URL: Repository, https://github.com/sgcx-org/pystatsbio
|
|
8
|
+
Project-URL: Issues, https://github.com/sgcx-org/pystatsbio/issues
|
|
9
|
+
Author-email: Hai-Shuo <contact@sgcx.org>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: biostatistics,clinical-trials,diagnostics,dose-response,gpu,pharmacokinetics,power-analysis,sample-size
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: numpy>=1.24
|
|
26
|
+
Requires-Dist: pystatistics>=0.1.0
|
|
27
|
+
Requires-Dist: scipy>=1.10
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: furo; extra == 'all'
|
|
30
|
+
Requires-Dist: mypy>=1.0; extra == 'all'
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'all'
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == 'all'
|
|
33
|
+
Requires-Dist: ruff>=0.1; extra == 'all'
|
|
34
|
+
Requires-Dist: sphinx>=6.0; extra == 'all'
|
|
35
|
+
Requires-Dist: torch>=2.0; extra == 'all'
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
41
|
+
Provides-Extra: docs
|
|
42
|
+
Requires-Dist: furo; extra == 'docs'
|
|
43
|
+
Requires-Dist: sphinx>=6.0; extra == 'docs'
|
|
44
|
+
Provides-Extra: gpu
|
|
45
|
+
Requires-Dist: torch>=2.0; extra == 'gpu'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
# PyStatsBio
|
|
49
|
+
|
|
50
|
+
Biotech and pharmaceutical statistical computing for Python.
|
|
51
|
+
|
|
52
|
+
Built on [PyStatistics](https://github.com/sgcx-org/pystatistics) for the general statistical
|
|
53
|
+
computing layer. PyStatsBio provides domain-specific methods for the drug development pipeline:
|
|
54
|
+
dose-response modeling, sample size/power, diagnostic accuracy, and non-compartmental
|
|
55
|
+
pharmacokinetics.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Design Philosophy
|
|
60
|
+
|
|
61
|
+
PyStatsBio follows the same principles as PyStatistics:
|
|
62
|
+
|
|
63
|
+
1. **Fail fast, fail loud** — no silent fallbacks or "helpful" defaults
|
|
64
|
+
2. **Explicit over implicit** — require parameters, don't assume intent
|
|
65
|
+
3. **R-level validation** — every function is validated against a named R reference
|
|
66
|
+
|
|
67
|
+
Each function states exactly which R function it replicates and to what tolerance.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# --- Clinical trial power / sample size ---
|
|
75
|
+
from pystatsbio import power
|
|
76
|
+
|
|
77
|
+
# Solve for sample size (two-sample t-test)
|
|
78
|
+
result = power.power_t_test(d=0.5, power=0.80, alpha=0.05, type="two.sample")
|
|
79
|
+
print(result.n) # per-group sample size
|
|
80
|
+
print(result.summary())
|
|
81
|
+
|
|
82
|
+
# Solve for power given n
|
|
83
|
+
result = power.power_t_test(n=64, d=0.5, alpha=0.05, type="two.sample")
|
|
84
|
+
print(result.power)
|
|
85
|
+
|
|
86
|
+
# Paired t-test
|
|
87
|
+
result = power.power_paired_t_test(d=0.3, power=0.80, alpha=0.05)
|
|
88
|
+
print(result.n)
|
|
89
|
+
|
|
90
|
+
# Proportions
|
|
91
|
+
result = power.power_prop_test(p1=0.30, p2=0.50, power=0.80, alpha=0.05)
|
|
92
|
+
print(result.n)
|
|
93
|
+
|
|
94
|
+
# Survival (log-rank)
|
|
95
|
+
result = power.power_logrank(
|
|
96
|
+
p1=0.60, p2=0.40, accrual=12, follow=24, power=0.80, alpha=0.05
|
|
97
|
+
)
|
|
98
|
+
print(result.n_events, result.n_total)
|
|
99
|
+
|
|
100
|
+
# Non-inferiority for means
|
|
101
|
+
result = power.power_noninf_mean(
|
|
102
|
+
delta=0.0, sigma=1.0, margin=0.5, power=0.80, alpha=0.05
|
|
103
|
+
)
|
|
104
|
+
print(result.n)
|
|
105
|
+
|
|
106
|
+
# Crossover bioequivalence (PowerTOST method)
|
|
107
|
+
result = power.power_crossover_be(cv=0.20, power=0.80, alpha=0.05)
|
|
108
|
+
print(result.n) # subjects per sequence
|
|
109
|
+
|
|
110
|
+
# Cluster-randomized trial
|
|
111
|
+
result = power.power_cluster(
|
|
112
|
+
d=0.5, icc=0.05, m=20, power=0.80, alpha=0.05
|
|
113
|
+
)
|
|
114
|
+
print(result.n_clusters)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# --- Dose-response modeling ---
|
|
118
|
+
import numpy as np
|
|
119
|
+
from pystatsbio import doseresponse
|
|
120
|
+
|
|
121
|
+
dose = np.array([0.001, 0.01, 0.1, 1.0, 10.0, 100.0])
|
|
122
|
+
response = np.array([2.1, 3.5, 12.0, 48.0, 87.5, 97.8])
|
|
123
|
+
|
|
124
|
+
# Fit 4PL (LL.4) model
|
|
125
|
+
result = doseresponse.fit_drm(dose, response, model="LL.4")
|
|
126
|
+
print(result.params) # CurveParams(b, c, d, e) — Hill slope, lower, upper, EC50
|
|
127
|
+
print(result.ec50)
|
|
128
|
+
print(result.summary())
|
|
129
|
+
|
|
130
|
+
# Fit 5PL (asymmetric)
|
|
131
|
+
result = doseresponse.fit_drm(dose, response, model="LL.5")
|
|
132
|
+
print(result.params)
|
|
133
|
+
|
|
134
|
+
# Extract EC50 with confidence interval
|
|
135
|
+
ec50_result = doseresponse.ec50(result)
|
|
136
|
+
print(ec50_result.ec50, ec50_result.ci_lower, ec50_result.ci_upper)
|
|
137
|
+
|
|
138
|
+
# Relative potency (reference vs. test compound)
|
|
139
|
+
ref_result = doseresponse.fit_drm(dose, response, model="LL.4")
|
|
140
|
+
test_result = doseresponse.fit_drm(dose * 3, response, model="LL.4")
|
|
141
|
+
rp = doseresponse.relative_potency(ref_result, test_result)
|
|
142
|
+
print(rp.ratio, rp.ci_lower, rp.ci_upper, rp.parallel)
|
|
143
|
+
|
|
144
|
+
# Benchmark dose (BMD/BMDL)
|
|
145
|
+
bmd_result = doseresponse.bmd(result, bmr=0.10)
|
|
146
|
+
print(bmd_result.bmd, bmd_result.bmdl)
|
|
147
|
+
|
|
148
|
+
# Batch fitting (GPU-accelerated for HTS)
|
|
149
|
+
# responses: (n_curves, n_doses) array
|
|
150
|
+
responses = np.random.rand(500, 6) * 100
|
|
151
|
+
batch = doseresponse.fit_drm_batch(dose, responses, model="LL.4", backend="auto")
|
|
152
|
+
print(batch.ec50) # shape (500,)
|
|
153
|
+
print(batch.params) # CurveParams with shape-(500,) arrays
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# --- Diagnostic accuracy ---
|
|
157
|
+
from pystatsbio import diagnostic
|
|
158
|
+
|
|
159
|
+
scores = np.array([0.1, 0.4, 0.35, 0.8, 0.9, 0.15, 0.6, 0.75, 0.55, 0.95])
|
|
160
|
+
labels = np.array([0, 0, 0, 1, 1, 0, 1, 1, 0, 1])
|
|
161
|
+
|
|
162
|
+
# ROC curve + AUC
|
|
163
|
+
roc_result = diagnostic.roc(scores, labels)
|
|
164
|
+
print(roc_result.auc, roc_result.ci_lower, roc_result.ci_upper)
|
|
165
|
+
print(roc_result.sensitivity, roc_result.specificity)
|
|
166
|
+
|
|
167
|
+
# Compare two biomarkers (DeLong test)
|
|
168
|
+
scores2 = np.random.rand(10)
|
|
169
|
+
test_result = diagnostic.roc_test(scores, scores2, labels)
|
|
170
|
+
print(test_result.p_value, test_result.summary())
|
|
171
|
+
|
|
172
|
+
# Full diagnostic accuracy at a threshold
|
|
173
|
+
da = diagnostic.diagnostic_accuracy(scores, labels, threshold=0.5)
|
|
174
|
+
print(da.sensitivity, da.specificity, da.ppv, da.npv, da.lr_pos, da.lr_neg)
|
|
175
|
+
|
|
176
|
+
# Optimal cutoff selection
|
|
177
|
+
cutoff = diagnostic.optimal_cutoff(roc_result, method="youden")
|
|
178
|
+
print(cutoff.threshold, cutoff.sensitivity, cutoff.specificity, cutoff.youden_index)
|
|
179
|
+
|
|
180
|
+
# Batch AUC for biomarker panel screening
|
|
181
|
+
# panel: (n_biomarkers, n_subjects) array
|
|
182
|
+
panel = np.random.rand(200, 100)
|
|
183
|
+
batch_auc = diagnostic.batch_auc(panel, labels[:100] if len(labels) >= 100 else np.random.randint(0, 2, 100))
|
|
184
|
+
print(batch_auc.auc) # shape (200,) — one AUC per biomarker
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# --- Pharmacokinetics (NCA) ---
|
|
188
|
+
from pystatsbio import pk
|
|
189
|
+
|
|
190
|
+
time = np.array([0, 0.5, 1, 2, 4, 6, 8, 12, 24])
|
|
191
|
+
conc = np.array([0, 8.5, 12.1, 10.4, 7.2, 4.9, 3.1, 1.4, 0.3])
|
|
192
|
+
|
|
193
|
+
result = pk.nca(time, conc, route="ev", dose=100.0)
|
|
194
|
+
print(result.cmax, result.tmax)
|
|
195
|
+
print(result.auc_last, result.auc_inf)
|
|
196
|
+
print(result.half_life)
|
|
197
|
+
print(result.cl, result.vd)
|
|
198
|
+
print(result.aumc_last, result.mrt)
|
|
199
|
+
print(result.summary())
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Modules
|
|
205
|
+
|
|
206
|
+
| Module | Status | Description |
|
|
207
|
+
|--------|--------|-------------|
|
|
208
|
+
| `power/` | Complete | Sample size and power for clinical trial designs |
|
|
209
|
+
| `doseresponse/` | Complete | 4PL/5PL curve fitting, EC50, relative potency, BMD, batch HTS |
|
|
210
|
+
| `diagnostic/` | Complete | ROC, AUC, sensitivity/specificity, optimal cutoff, batch biomarker |
|
|
211
|
+
| `pk/` | Complete | Non-compartmental PK analysis (NCA): AUC, Cmax, CL, Vd, MRT |
|
|
212
|
+
|
|
213
|
+
### `power` — Sample Size and Power
|
|
214
|
+
|
|
215
|
+
| Function | R equivalent |
|
|
216
|
+
|----------|--------------|
|
|
217
|
+
| `power_t_test()` | `pwr::pwr.t.test()` |
|
|
218
|
+
| `power_paired_t_test()` | `pwr::pwr.t.test(type="paired")` |
|
|
219
|
+
| `power_prop_test()` | `pwr::pwr.2p.test()` |
|
|
220
|
+
| `power_fisher_test()` | `TrialSize::TwoSampleProportion.Equality()` |
|
|
221
|
+
| `power_logrank()` | `gsDesign::nSurv()` |
|
|
222
|
+
| `power_anova_oneway()` | `pwr::pwr.anova.test()` |
|
|
223
|
+
| `power_anova_factorial()` | `TrialSize::FactorialDesign()` |
|
|
224
|
+
| `power_noninf_mean()` | `TrialSize::TwoSampleMean.NIS()` |
|
|
225
|
+
| `power_noninf_prop()` | `TrialSize::TwoSampleProportion.NIS()` |
|
|
226
|
+
| `power_equiv_mean()` | `TrialSize::TwoSampleMean.Equivalence()` |
|
|
227
|
+
| `power_superiority_mean()` | `TrialSize::TwoSampleMean.Superiority()` |
|
|
228
|
+
| `power_crossover_be()` | `PowerTOST::sampleSize()` |
|
|
229
|
+
| `power_cluster()` | `samplesize::n.twogroup()` with ICC |
|
|
230
|
+
|
|
231
|
+
### `doseresponse` — Dose-Response Modeling
|
|
232
|
+
|
|
233
|
+
| Function | R equivalent |
|
|
234
|
+
|----------|--------------|
|
|
235
|
+
| `fit_drm()` | `drc::drm()` |
|
|
236
|
+
| `fit_drm_batch()` | vectorized `drc::drm()` |
|
|
237
|
+
| `ec50()` | `drc::ED()` |
|
|
238
|
+
| `relative_potency()` | `drc::EDcomp()` with parallelism test |
|
|
239
|
+
| `bmd()` | `drc::bmd()` / `BMDS` |
|
|
240
|
+
|
|
241
|
+
Models: `LL.4` (4PL), `LL.5` (5PL), `W1.4` (Weibull-1), `W2.4` (Weibull-2),
|
|
242
|
+
`BC.4` (Brain-Cousens hormesis).
|
|
243
|
+
|
|
244
|
+
### `diagnostic` — Diagnostic Accuracy
|
|
245
|
+
|
|
246
|
+
| Function | R equivalent |
|
|
247
|
+
|----------|-------------|
|
|
248
|
+
| `roc()` | `pROC::roc()` with DeLong CI |
|
|
249
|
+
| `roc_test()` | `pROC::roc.test()` (DeLong) |
|
|
250
|
+
| `diagnostic_accuracy()` | `epiR::epi.tests()` |
|
|
251
|
+
| `optimal_cutoff()` | `OptimalCutpoints::optimal.cutpoints()` |
|
|
252
|
+
| `batch_auc()` | vectorized `pROC::auc()` |
|
|
253
|
+
|
|
254
|
+
### `pk` — Non-Compartmental Analysis
|
|
255
|
+
|
|
256
|
+
| Function | R equivalent |
|
|
257
|
+
|----------|-------------|
|
|
258
|
+
| `nca()` | `PKNCA::pk.nca()` / `NonCompart::sNCA()` |
|
|
259
|
+
|
|
260
|
+
AUC methods: `linear`, `log`, `linear-up/log-down` (FDA default).
|
|
261
|
+
Routes: `iv` (intravenous), `ev` (extravascular).
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Installation
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
pip install pystatsbio
|
|
269
|
+
|
|
270
|
+
# With GPU support (requires PyTorch)
|
|
271
|
+
pip install pystatsbio[gpu]
|
|
272
|
+
|
|
273
|
+
# Development
|
|
274
|
+
pip install pystatsbio[dev]
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Requires Python 3.11+. Core dependencies: `pystatistics`, `numpy`, `scipy`.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT
|
|
284
|
+
|
|
285
|
+
## Author
|
|
286
|
+
|
|
287
|
+
Hai-Shuo (contact@sgcx.org)
|