pyedger 0.1.2__tar.gz → 0.2.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.
- {pyedger-0.1.2 → pyedger-0.2.0}/PKG-INFO +23 -19
- {pyedger-0.1.2 → pyedger-0.2.0}/README.md +22 -18
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger/__init__.py +2 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger/_glm_batch.py +2 -2
- pyedger-0.2.0/pyedger/_limma.py +714 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger/_ql_adjust.py +117 -76
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger/core.py +93 -147
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger.egg-info/PKG-INFO +23 -19
- {pyedger-0.1.2 → pyedger-0.2.0}/pyproject.toml +1 -1
- pyedger-0.2.0/tests/test_r_parity.py +236 -0
- pyedger-0.1.2/pyedger/_limma.py +0 -201
- pyedger-0.1.2/tests/test_r_parity.py +0 -122
- {pyedger-0.1.2 → pyedger-0.2.0}/LICENSE +0 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger/_locfit.py +0 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger.egg-info/SOURCES.txt +0 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger.egg-info/dependency_links.txt +0 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger.egg-info/requires.txt +0 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/pyedger.egg-info/top_level.txt +0 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/setup.cfg +0 -0
- {pyedger-0.1.2 → pyedger-0.2.0}/tests/test_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyedger
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Pure-Python port of Bioconductor edgeR — negative-binomial models for differential expression of count data (estimateDisp, glmFit, glmLRT, glmQLFTest, exactTest).
|
|
5
5
|
Author-email: Zehua Zeng <starlitnightly@163.com>
|
|
6
6
|
License: LGPL-3.0-or-later
|
|
@@ -33,7 +33,7 @@ Dynamic: license-file
|
|
|
33
33
|
A **pure-Python port of [Bioconductor edgeR](https://bioconductor.org/packages/release/bioc/html/edgeR.html)** (Robinson, McCarthy & Smyth, *Bioinformatics* 2010) — negative-binomial models for differential expression of count data.
|
|
34
34
|
|
|
35
35
|
- **No `rpy2`**, no R install — the edgeR negative-binomial GLM workflow reimplemented in NumPy / SciPy, vectorised over genes
|
|
36
|
-
- Matches Bioconductor edgeR 4.
|
|
36
|
+
- Matches Bioconductor edgeR 4.10 numerically (see [Numerical parity](#numerical-parity-with-edger))
|
|
37
37
|
- The canonical pipelines: `DGEList → calcNormFactors → estimateDisp → glmQLFit → glmQLFTest` and the classic `exactTest`
|
|
38
38
|
- TMM normalization, common / trended / tagwise dispersion, GLM and quasi-likelihood F-tests, `filterByExpr`, `cpm` / `aveLogCPM`
|
|
39
39
|
- Both Python-style (`glm_fit`, `estimate_disp`, `top_tags`) and R-style (`glmFit`, `estimateDisp`, `topTags`) names exported
|
|
@@ -93,27 +93,31 @@ pyedger.topTags(et)
|
|
|
93
93
|
|
|
94
94
|
## Numerical parity with edgeR
|
|
95
95
|
|
|
96
|
-
`tests/test_r_parity.py` checks pyedger against edgeR 4.
|
|
97
|
-
locfit 1.5-9) on a paired design with zero-count library pairs
|
|
98
|
-
|
|
99
|
-
kernels follow edgeR's compiled code step
|
|
100
|
-
genes:
|
|
96
|
+
`tests/test_r_parity.py` checks pyedger against edgeR 4.10.5 (R 4.5.3, limma 3.68.5,
|
|
97
|
+
locfit 1.5-9.12) on a paired design with zero-count library pairs, a Milo-style
|
|
98
|
+
design and limma hyperparameter cases; the reference values are regenerated by
|
|
99
|
+
`tests/data/make_reference.R`. The GLM kernels follow edgeR's compiled code step
|
|
100
|
+
by step and run vectorised over all genes:
|
|
101
101
|
|
|
102
102
|
| Step | Ported from | Agreement with R |
|
|
103
103
|
|---|---|---|
|
|
104
|
-
| `glmFit` (Levenberg, one-way shortcut, start values) | `
|
|
105
|
-
| `estimateDisp` with a design (Cox-Reid APL, zero-fit groups) and classic mode | `
|
|
104
|
+
| `glmFit` (Levenberg, one-way shortcut, start values) | `glm.c` | ~1e-9 |
|
|
105
|
+
| `estimateDisp` with a design (Cox-Reid APL, zero-fit groups) and classic mode | `compute_apl.c`, `q2qnbinom` | ~1e-10 |
|
|
106
106
|
| Trend: `locfitByCol` | locfit tree evaluation + interpolation | bit-identical |
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `glmQLFit(legacy=
|
|
110
|
-
|
|
111
|
-
`glmQLFit(legacy=False)` and `estimateGLMCommonDisp`
|
|
112
|
-
`optimize(tol=1e-5)` over dispersion^(1/4)
|
|
113
|
-
~4e-5 relative
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
| `squeezeVar` (`fitFDist`, `fitFDistUnequalDF1`, `fitFDistRobustly`) | limma 3.68 | ~1e-11 |
|
|
108
|
+
| `glmQLFit(legacy=False)` (new QL) given a dispersion | `ql_glm.c` | ~1e-9 |
|
|
109
|
+
| `glmQLFit(legacy=True)`, `exactTest`, `glmTreat` | | ~1e-10 |
|
|
110
|
+
|
|
111
|
+
`glmQLFit(legacy=False)` without a dispersion and `estimateGLMCommonDisp` estimate
|
|
112
|
+
the NB dispersion with edgeR's `optimize(tol=1e-5)` over dispersion^(1/4); R's own
|
|
113
|
+
value is only defined to ~4e-5 relative, and agreement there is at that level
|
|
114
|
+
(p-values ~1e-4 relative on the test data). On real data the agreement is usually
|
|
115
|
+
much closer: on four GSE274113 pseudobulk ATAC comparisons (~170k peaks x 26
|
|
116
|
+
samples, `~ library + condition`, default new QL) p-values agreed to < 1e-7
|
|
117
|
+
relative with identical FDR < 0.1 calls.
|
|
118
|
+
|
|
119
|
+
Defaults follow edgeR 4.10: `glmQLFit(legacy=False, abundance_trend=True)`,
|
|
120
|
+
`estimateDisp(prior_df=None)` with the `chooseLowessSpan` span.
|
|
117
121
|
|
|
118
122
|
## Citation
|
|
119
123
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
A **pure-Python port of [Bioconductor edgeR](https://bioconductor.org/packages/release/bioc/html/edgeR.html)** (Robinson, McCarthy & Smyth, *Bioinformatics* 2010) — negative-binomial models for differential expression of count data.
|
|
4
4
|
|
|
5
5
|
- **No `rpy2`**, no R install — the edgeR negative-binomial GLM workflow reimplemented in NumPy / SciPy, vectorised over genes
|
|
6
|
-
- Matches Bioconductor edgeR 4.
|
|
6
|
+
- Matches Bioconductor edgeR 4.10 numerically (see [Numerical parity](#numerical-parity-with-edger))
|
|
7
7
|
- The canonical pipelines: `DGEList → calcNormFactors → estimateDisp → glmQLFit → glmQLFTest` and the classic `exactTest`
|
|
8
8
|
- TMM normalization, common / trended / tagwise dispersion, GLM and quasi-likelihood F-tests, `filterByExpr`, `cpm` / `aveLogCPM`
|
|
9
9
|
- Both Python-style (`glm_fit`, `estimate_disp`, `top_tags`) and R-style (`glmFit`, `estimateDisp`, `topTags`) names exported
|
|
@@ -63,27 +63,31 @@ pyedger.topTags(et)
|
|
|
63
63
|
|
|
64
64
|
## Numerical parity with edgeR
|
|
65
65
|
|
|
66
|
-
`tests/test_r_parity.py` checks pyedger against edgeR 4.
|
|
67
|
-
locfit 1.5-9) on a paired design with zero-count library pairs
|
|
68
|
-
|
|
69
|
-
kernels follow edgeR's compiled code step
|
|
70
|
-
genes:
|
|
66
|
+
`tests/test_r_parity.py` checks pyedger against edgeR 4.10.5 (R 4.5.3, limma 3.68.5,
|
|
67
|
+
locfit 1.5-9.12) on a paired design with zero-count library pairs, a Milo-style
|
|
68
|
+
design and limma hyperparameter cases; the reference values are regenerated by
|
|
69
|
+
`tests/data/make_reference.R`. The GLM kernels follow edgeR's compiled code step
|
|
70
|
+
by step and run vectorised over all genes:
|
|
71
71
|
|
|
72
72
|
| Step | Ported from | Agreement with R |
|
|
73
73
|
|---|---|---|
|
|
74
|
-
| `glmFit` (Levenberg, one-way shortcut, start values) | `
|
|
75
|
-
| `estimateDisp` with a design (Cox-Reid APL, zero-fit groups) and classic mode | `
|
|
74
|
+
| `glmFit` (Levenberg, one-way shortcut, start values) | `glm.c` | ~1e-9 |
|
|
75
|
+
| `estimateDisp` with a design (Cox-Reid APL, zero-fit groups) and classic mode | `compute_apl.c`, `q2qnbinom` | ~1e-10 |
|
|
76
76
|
| Trend: `locfitByCol` | locfit tree evaluation + interpolation | bit-identical |
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
79
|
-
| `glmQLFit(legacy=
|
|
80
|
-
|
|
81
|
-
`glmQLFit(legacy=False)` and `estimateGLMCommonDisp`
|
|
82
|
-
`optimize(tol=1e-5)` over dispersion^(1/4)
|
|
83
|
-
~4e-5 relative
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
| `squeezeVar` (`fitFDist`, `fitFDistUnequalDF1`, `fitFDistRobustly`) | limma 3.68 | ~1e-11 |
|
|
78
|
+
| `glmQLFit(legacy=False)` (new QL) given a dispersion | `ql_glm.c` | ~1e-9 |
|
|
79
|
+
| `glmQLFit(legacy=True)`, `exactTest`, `glmTreat` | | ~1e-10 |
|
|
80
|
+
|
|
81
|
+
`glmQLFit(legacy=False)` without a dispersion and `estimateGLMCommonDisp` estimate
|
|
82
|
+
the NB dispersion with edgeR's `optimize(tol=1e-5)` over dispersion^(1/4); R's own
|
|
83
|
+
value is only defined to ~4e-5 relative, and agreement there is at that level
|
|
84
|
+
(p-values ~1e-4 relative on the test data). On real data the agreement is usually
|
|
85
|
+
much closer: on four GSE274113 pseudobulk ATAC comparisons (~170k peaks x 26
|
|
86
|
+
samples, `~ library + condition`, default new QL) p-values agreed to < 1e-7
|
|
87
|
+
relative with identical FDR < 0.1 calls.
|
|
88
|
+
|
|
89
|
+
Defaults follow edgeR 4.10: `glmQLFit(legacy=False, abundance_trend=True)`,
|
|
90
|
+
`estimateDisp(prior_df=None)` with the `chooseLowessSpan` span.
|
|
87
91
|
|
|
88
92
|
## Citation
|
|
89
93
|
|
|
@@ -31,6 +31,7 @@ from .core import (
|
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
calcNormFactors = calc_norm_factors
|
|
34
|
+
normLibSizes = calc_norm_factors # edgeR >= 4.0 name for calcNormFactors
|
|
34
35
|
effectiveLibSizes = effective_lib_sizes
|
|
35
36
|
equalizeLibSizes = equalize_lib_sizes
|
|
36
37
|
estimateCommonDisp = estimate_common_disp
|
|
@@ -65,6 +66,7 @@ __all__ = [
|
|
|
65
66
|
"aveLogCPM",
|
|
66
67
|
"calc_norm_factors",
|
|
67
68
|
"calcNormFactors",
|
|
69
|
+
"normLibSizes",
|
|
68
70
|
"effective_lib_sizes",
|
|
69
71
|
"effectiveLibSizes",
|
|
70
72
|
"equalize_lib_sizes",
|
|
@@ -20,7 +20,7 @@ LOG_LOW_VALUE = np.log(LOW_VALUE)
|
|
|
20
20
|
|
|
21
21
|
# --------------------------------------------------------------------------- deviance
|
|
22
22
|
def unit_nb_deviance(y, mu, phi):
|
|
23
|
-
"""
|
|
23
|
+
"""compute_nbdev.c compute_unit_nb_deviance (edgeR 4.10), elementwise with broadcasting."""
|
|
24
24
|
y = np.asarray(y, dtype=float) + 1e-8
|
|
25
25
|
mu = np.asarray(mu, dtype=float) + 1e-8
|
|
26
26
|
y, mu, phi = np.broadcast_arrays(y, mu, np.asarray(phi, dtype=float))
|
|
@@ -38,7 +38,7 @@ def unit_nb_deviance(y, mu, phi):
|
|
|
38
38
|
nb = rest & ~(prod > 1e6)
|
|
39
39
|
inv = 1.0 / phi[nb]
|
|
40
40
|
out[nb] = 2 * (y[nb] * np.log(y[nb] / mu[nb]) + (y[nb] + inv) * np.log((mu[nb] + inv) / (y[nb] + inv)))
|
|
41
|
-
return out
|
|
41
|
+
return np.maximum(out, 0.0) # edgeR >= 4.4 floors unit deviances at 0
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def nb_deviance(Y, Mu, Phi, W):
|