epirefine 0.10.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.
- epirefine-0.10.0/.gitignore +13 -0
- epirefine-0.10.0/CHANGELOG.md +328 -0
- epirefine-0.10.0/LICENSE +21 -0
- epirefine-0.10.0/PKG-INFO +177 -0
- epirefine-0.10.0/README.md +142 -0
- epirefine-0.10.0/docs/method.md +1027 -0
- epirefine-0.10.0/docs/scalability.md +268 -0
- epirefine-0.10.0/pyproject.toml +51 -0
- epirefine-0.10.0/src/epirefine/__init__.py +6 -0
- epirefine-0.10.0/src/epirefine/assignability.py +166 -0
- epirefine-0.10.0/src/epirefine/calibrate.py +352 -0
- epirefine-0.10.0/src/epirefine/cli.py +97 -0
- epirefine-0.10.0/src/epirefine/detection.py +254 -0
- epirefine-0.10.0/src/epirefine/grid.py +74 -0
- epirefine-0.10.0/src/epirefine/io.py +125 -0
- epirefine-0.10.0/src/epirefine/model.py +361 -0
- epirefine-0.10.0/src/epirefine/motifs.py +440 -0
- epirefine-0.10.0/src/epirefine/pipeline.py +510 -0
- epirefine-0.10.0/src/epirefine/redundancy.py +73 -0
- epirefine-0.10.0/src/epirefine/refine.py +298 -0
- epirefine-0.10.0/src/epirefine/sites.py +389 -0
- epirefine-0.10.0/tests/test_core.py +104 -0
- epirefine-0.10.0/tests/test_evaluate.py +24 -0
- epirefine-0.10.0/tests/test_group_weights.py +24 -0
- epirefine-0.10.0/tests/test_motif_noise.py +19 -0
- epirefine-0.10.0/tests/test_regional.py +19 -0
- epirefine-0.10.0/tests/test_v2.py +273 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Versions follow semantic versioning (see `CLAUDE.md`). Each entry lists what changed and its measured
|
|
4
|
+
effect on the benchmark suite (`benchmarks/benchmark_suite.py`; history in the dev log).
|
|
5
|
+
|
|
6
|
+
## 0.10.0 (2026-11-02): first public release (PyPI, Bioconda)
|
|
7
|
+
|
|
8
|
+
No change to results.
|
|
9
|
+
- Packaging: MIT licence (`LICENSE`), author and project metadata, classifiers for Python 3.10-3.15, README
|
|
10
|
+
usable on PyPI (absolute links; `pip install epirefine`). The source distribution carries the package, tests,
|
|
11
|
+
docs and changelog.
|
|
12
|
+
- `.github/workflows/release.yml`: on a version tag, checks the tag against the package version, builds and checks
|
|
13
|
+
the sdist and wheel, tests the installed wheel, publishes to TestPyPI and PyPI with trusted publishing, and
|
|
14
|
+
creates a GitHub release.
|
|
15
|
+
- `tests/test_evaluate.py` skips when the repository's `benchmarks/` is not there (as in the sdist).
|
|
16
|
+
- `dev/hooks/install.sh` sets `core.hooksPath` instead of a symlink in `.git/hooks`, which failed in worktrees.
|
|
17
|
+
- README: a known limitation was out of date (sites shared by motif groups count in the most specific group; they
|
|
18
|
+
are not dropped).
|
|
19
|
+
|
|
20
|
+
**Benchmark effect:** none (no package code changed).
|
|
21
|
+
|
|
22
|
+
## 0.9.2 (2026-11-02): tests on GitHub for every supported Python
|
|
23
|
+
|
|
24
|
+
No change to results.
|
|
25
|
+
- `.github/workflows/tests.yml`: the unit tests run on every push, tag and pull request, on Python 3.10, 3.11,
|
|
26
|
+
3.12, 3.13, 3.14 and 3.15, and once with the lowest dependency versions `pyproject.toml` allows.
|
|
27
|
+
- Fix: polars must be >= 1.2 (was >= 1.0). EpiRefine reads pileups with `read_csv(infer_schema=False)`, which
|
|
28
|
+
polars 1.0 and 1.1 don't have; with them pileup parsing failed (2 tests). Found by the lowest-versions job.
|
|
29
|
+
|
|
30
|
+
**Benchmark effect:** none (no code change). All 35 tests pass locally on Python 3.10-3.15 and on
|
|
31
|
+
numpy 1.26.0 / scipy 1.11.1 / polars 1.2.1.
|
|
32
|
+
|
|
33
|
+
## 0.9.1 (2026-11-02): the repository holds only the tool
|
|
34
|
+
|
|
35
|
+
No change to the epirefine package or its outputs.
|
|
36
|
+
- `workflow/`, `hic/` and the benchmarks other than the monocultures (ZymoOral pieces, simulated
|
|
37
|
+
look-alikes, chimeras, simulator, truth tables, Nanomotif comparison, threshold sweeps) moved to a
|
|
38
|
+
separate repository that calls the tool (`epirefine_workflow`; their history up to here stays in this one).
|
|
39
|
+
- `benchmarks/benchmark_suite.py` runs the monoculture benchmark by default and appends a summary per
|
|
40
|
+
version to `benchmarks/results.tsv` in the repository (seeded with the released versions 0.6.1, 0.7.2, 0.9.0).
|
|
41
|
+
|
|
42
|
+
**Benchmark effect:** none; mono-train identical to 0.9.0 (0.948 / 1.000 / 0.893 / 2.0).
|
|
43
|
+
|
|
44
|
+
## 0.9.0 (2026-11-02): renamed to EpiRefine
|
|
45
|
+
|
|
46
|
+
Breaking (name only; no model or result change).
|
|
47
|
+
- The tool is now **EpiRefine** (*epi*genetics + *refine*; formerly pbinner): package `epirefine`
|
|
48
|
+
(`src/epirefine/`), command `epirefine` (`epirefine run`, `epirefine --version`). The `pbinner` command is gone.
|
|
49
|
+
- Output: the `method` column of the Nanomotif-compatible `bin_contamination.tsv` and
|
|
50
|
+
`include_contigs.tsv` now says `epirefine`. Columns and every other value are unchanged.
|
|
51
|
+
- Workflow: the method is called `epirefine` (output folder `<sample>/epirefine/`, config keys `epirefine`,
|
|
52
|
+
`epirefine_extra`); `benchmarks/compare_nanomotif.py --epirefine`. Data and cache folders on disk keep
|
|
53
|
+
their old names.
|
|
54
|
+
|
|
55
|
+
**Benchmark effect:** none; every benchmark identical to 0.8.0 (mono-train 0.948 / 1.000 / 0.893 / 2.0;
|
|
56
|
+
ZymoOral 5-150 kb, 2-30 kb and the simulated look-alikes unchanged). Outputs byte-identical except the `method` value.
|
|
57
|
+
|
|
58
|
+
## 0.8.0 (2026-10-29): regional inclusion off by default
|
|
59
|
+
|
|
60
|
+
Changes default results and adds a CLI flag.
|
|
61
|
+
- Long unbinned contigs are no longer included from their 50 kb windows (the regional model) when
|
|
62
|
+
the whole contig doesn't support the bin. `--regional-inclusion` turns it back on (experimental).
|
|
63
|
+
Regional flagging of long binned contigs ("mixed") is unchanged.
|
|
64
|
+
- Why (dev log D40): no benchmark gains an inclusion from it. On ZymoFecal it added 69 inclusions
|
|
65
|
+
(24.5 Mb); for 58 the whole-contig model's best explanation is "unknown" (P 0.54-1, 42 above 0.99).
|
|
66
|
+
|
|
67
|
+
**Benchmark effect:** none; every scenario is identical to 0.7.2 (mono-train, ZymoOral 5-150 kb and
|
|
68
|
+
2-30 kb, simulated look-alikes, chimeras).
|
|
69
|
+
**ZymoFecal:**
|
|
70
|
+
- inclusions 554 -> 485;
|
|
71
|
+
- strict Hi-C (>= 10x): flags 6/1/2 (TP/FP/FN) unchanged; inclusions 24 right / 2 wrong -> 23 / 1;
|
|
72
|
+
- peak memory 44.1 -> 40.9 GB.
|
|
73
|
+
|
|
74
|
+
## 0.7.2 (2026-10-28): monoculture benchmark sets (benchmarks/ and workflow/ only)
|
|
75
|
+
|
|
76
|
+
No change to the pbinner package.
|
|
77
|
+
- `benchmarks/monoculture_sets.py` builds synthetic communities from 40 isolates sequenced one at a
|
|
78
|
+
time (so every read's origin is known):
|
|
79
|
+
- genus-level split into training (6 communities), test (3) and validation (3);
|
|
80
|
+
- 8 isolates per community;
|
|
81
|
+
- pileups thinned to 3-60x;
|
|
82
|
+
- assemblies cut into 2-100 kb pieces;
|
|
83
|
+
- errors planted with the standard generator.
|
|
84
|
+
- `workflow/monocultures.smk` makes the missing modkit pileups and Nanomotif motifs.
|
|
85
|
+
- `benchmarks/suite_monocultures.json` lists the scenarios. Test and validation communities are held
|
|
86
|
+
out for the final estimate.
|
|
87
|
+
|
|
88
|
+
**Benchmark effect:** none (no package change).
|
|
89
|
+
|
|
90
|
+
## 0.7.1 (2026-10-28): Hi-C membership by contact density (hic/ only)
|
|
91
|
+
|
|
92
|
+
No change to the pbinner package. `hic/density.py` calls contig membership from Hi-C contact density
|
|
93
|
+
relative to the bin's own internal density (ProxiMeta-style ratio):
|
|
94
|
+
- **member:** ratio >= 0.1, >= 5 links, to >= 2 of the bin's other contigs.
|
|
95
|
+
- **foreign:** another eligible bin is clearly preferred.
|
|
96
|
+
- **not linked:** own-bin contacts improbably few for a member: below the 1% quantile of a
|
|
97
|
+
beta-binomial with the bin's pooled own-contact share and an over-dispersion fitted in-sample
|
|
98
|
+
(ZymoFecal rho 0.28).
|
|
99
|
+
- Placed / not placed for unbinned contigs.
|
|
100
|
+
|
|
101
|
+
ZymoFecal, full library (416 M read pairs), binned contigs: 4,062 members, 10 foreign, 178 not
|
|
102
|
+
linked, 2,121 undetermined. The earlier calls had 5,047 members and 230 not linked.
|
|
103
|
+
**Benchmark effect:** none (no package change).
|
|
104
|
+
|
|
105
|
+
## 0.7.0 (2026-10-28): coverage cutoff, motif groups, regional model, and new statuses and columns
|
|
106
|
+
|
|
107
|
+
**Breaking for downstream parsers:** `contig_decisions.tsv` gains statuses `low_coverage`, `too_short`,
|
|
108
|
+
`no_methylation_evidence` and `mixed`, and columns `expected_evidence`, `coverage`,
|
|
109
|
+
`p_regional_foreign`, `p_regional_mixed`, `foreign_segments` and `segment_bins`. A new file,
|
|
110
|
+
`bin_evidence.tsv`, is written. The Nanomotif-compatible files keep their columns.
|
|
111
|
+
|
|
112
|
+
Model and decisions:
|
|
113
|
+
- **No decision below 5x** median coverage at motif sites, counted per strand (status `low_coverage`,
|
|
114
|
+
`--min-contig-coverage`), or below 1 kb (status `too_short`, `--min-contig-length`).
|
|
115
|
+
- **Status `no_methylation_evidence`** for contigs whose sites cannot show where they belong
|
|
116
|
+
(expected log Bayes factor below 3 against a random community genome).
|
|
117
|
+
- **Motif groups:** motifs are merged only when their sites are near-identical (Jaccard >= 0.9), and
|
|
118
|
+
shared positions go to the most specific group. ZymoFecal: 952 motifs, 713 groups.
|
|
119
|
+
- **Noise filter:** groups whose sites show intermediate calls in >= 20% of bins are dropped
|
|
120
|
+
(ZymoFecal: 8 4mC groups on CCNGG-type contexts). `--no-motif-noise-filter` turns it off.
|
|
121
|
+
- **Group weights:** variants of one methyltransferase share one group's weight (ZymoFecal: 81
|
|
122
|
+
groups in 16 clusters). `--no-group-weights` turns them off.
|
|
123
|
+
- **Unknown-genome model:** community-only unless relatives fit significantly better (paired
|
|
124
|
+
bootstrap); the per-group swap probability is capped at 0.05.
|
|
125
|
+
- **Thresholds:** flags need a methylome difference >= 0.4, and inclusion needs P >= 0.9, or >= 0.95
|
|
126
|
+
for bins with no detected methylation (`--include-threshold-unmethylated`).
|
|
127
|
+
- **Regional model:** long binned contigs are judged along their length (member / foreign / mixed,
|
|
128
|
+
a two-state chain over 50 kb chunks). Flags better explained as mixed are withdrawn, and the
|
|
129
|
+
contig is reported with its foreign segments and a segment map. Long unbinned contigs are scored
|
|
130
|
+
in windows, with inclusion tied to the model's share of genomes without a bin.
|
|
131
|
+
`--no-regional-model` turns it off.
|
|
132
|
+
- Evidence pass: cache parts run serially, and the expectation table is shared (fixes a heap
|
|
133
|
+
corruption from oversubscribed OpenBLAS threads on ZymoFecal).
|
|
134
|
+
- Benchmarks leave undecided contigs out of the metrics and count them separately. New chimera
|
|
135
|
+
benchmark: `benchmarks/chimera_sets.py`, `benchmarks/evaluate_regional.py`.
|
|
136
|
+
|
|
137
|
+
**Benchmark effect** (39ab5ae, decided contigs; flag precision 1.00 on every benchmark):
|
|
138
|
+
- Monocultures: contaminants found 0.948, put back 0.893, 2.0 wrong placements per scenario.
|
|
139
|
+
- ZymoOral 5-150 kb: 1.000 / 0.927. ZymoOral 2-30 kb: 0.996 / 0.876.
|
|
140
|
+
- Simulated look-alikes: 0.967 found; nothing included.
|
|
141
|
+
- Chimeras: 33 of 40 reported as mixed instead of flagged; 20 of 20 long contaminants flagged.
|
|
142
|
+
|
|
143
|
+
**ZymoFecal against the strict full-library Hi-C (contigs >= 10x):**
|
|
144
|
+
- Flag precision 0.40 → 0.86 and recall 0.75 → 0.75 (TP/FP/FN 6/9/2 → 6/1/2) against the 0.6.1 candidate settings.
|
|
145
|
+
- Inclusions in the Hi-C bin: 24 of 26.
|
|
146
|
+
- Run: 86 min, 44 GB, 4 workers.
|
|
147
|
+
|
|
148
|
+
## 0.6.1 (2026-10-26): Hi-C validation of refinement (workflow and hic/ only)
|
|
149
|
+
|
|
150
|
+
No change to the pbinner package. Workflow and `hic/`:
|
|
151
|
+
- **Strict Hi-C calls.** Pairs need MAPQ >= 20 on both reads (`hic_mapq`; chromap's Hi-C preset
|
|
152
|
+
used 1) and the `.pairs` file is kept. A contig's Hi-C bin is the bin with most contacts; a call
|
|
153
|
+
is confident only with >= 20 contacts, >= 80% of the contig's contacts to bins, >= 3 distinct
|
|
154
|
+
partner contigs, >= 10 contacts without its strongest partner, and P(random ligation) <= 1e-6.
|
|
155
|
+
On ZymoFecal contigs of CheckM2 high-quality bins: 0 of 139 called to another bin (the earlier
|
|
156
|
+
rule: 33 of 343, nearly all resting on a single partner contig).
|
|
157
|
+
- `contacts.py` counting and calling are separate steps (rules `hic_pairs`, `hic_contacts`).
|
|
158
|
+
- New analyses: `refine.py` (Hi-C-only refinement as a CheckM2 reference), `nonredundant.py`,
|
|
159
|
+
`bin_quality.py`, `marker_visibility.py` (can CheckM2 see Hi-C-foreign contigs?),
|
|
160
|
+
`within_vs_outside.py` (contacts inside vs outside the bin), `probability_vs_hic.py` (Hi-C
|
|
161
|
+
posterior per contig), `calibrate_with_hic.py` (pbinner P(contaminant) -> Hi-C-foreign rate),
|
|
162
|
+
with R plots and workflow rules.
|
|
163
|
+
- ZymoFecal sample in the config (original, pbinner and Hi-C bin sets; Nanomotif left out).
|
|
164
|
+
|
|
165
|
+
**Findings (ZymoFecal):** CheckM2 can't see most Hi-C-foreign sequence (78% carries no duplicated
|
|
166
|
+
single-copy marker); pbinner flags are Hi-C-foreign 36-44% of the time against a base rate of 11%.
|
|
167
|
+
**Benchmark effect:** none (no package change).
|
|
168
|
+
|
|
169
|
+
## 0.6.0 (2026-10-26): calibration leaves the whole contig out
|
|
170
|
+
|
|
171
|
+
- Calibration scores each pseudo-contig against its bin's profile with *every* chunk of its source
|
|
172
|
+
contig removed, as contig decisions already did. Before, only the chunks the fragment overlapped
|
|
173
|
+
were removed, so the rest of its own contig (a median 33-52% of the profile) stayed in, and
|
|
174
|
+
calibration measured agreement within a contig. Fragments whose contig is its whole bin are skipped.
|
|
175
|
+
|
|
176
|
+
**Benchmark effect:** ZymoOral and simulated benchmarks identical (mock genomes have no
|
|
177
|
+
between-contig variation). ZymoFecal (real gut community, checked against Hi-C, MAPQ >= 20 and strict
|
|
178
|
+
calls): the unknown-genome model no longer absorbs everything (share of unbinned contigs from bin-less
|
|
179
|
+
genomes 0.995 -> 0.822); inclusions 5 -> 495; of the 380 unbinned contigs Hi-C places confidently, 42
|
|
180
|
+
included, all in Hi-C's bin (was 1); flags 460 -> 431, Hi-C precision of flags 31% -> 36%.
|
|
181
|
+
human_gut: CheckM2 203 vs 204 high-quality bins, median completeness 95.0 vs 95.1%, contamination 1.00%
|
|
182
|
+
(unchanged within noise); run time 25 vs 36 min.
|
|
183
|
+
|
|
184
|
+
## 0.5.0 (2026-10-23): bin refinement only; Hi-C validation in the workflow
|
|
185
|
+
|
|
186
|
+
**Breaking.**
|
|
187
|
+
- The package does bin refinement only (D22). Removed: plasmid-host assignment (`--plasmids`,
|
|
188
|
+
`--mtases`, `assignments.tsv`, `posteriors.tsv`, `plasmid_param_fit.tsv`), the own-MTase model,
|
|
189
|
+
detectability, and the `simulate`, `evaluate` and `report` subcommands. `pbinner run` is the only
|
|
190
|
+
command.
|
|
191
|
+
- Repository layout: `src/pbinner/` (package), `workflow/`, `hic/`, `benchmarks/` (was
|
|
192
|
+
`scripts/`), `dev/` (hooks, prototypes), `docs/`.
|
|
193
|
+
|
|
194
|
+
**New.**
|
|
195
|
+
- `--variation logit`: a logit-normal kernel for how a contig's methylated fraction varies around
|
|
196
|
+
its bin's (default stays `beta`); `calibration/temperature_by_length.tsv` diagnostic.
|
|
197
|
+
- `hic/`: Hi-C contacts per contig and bin, a Hi-C bin call per contig, and scoring of each
|
|
198
|
+
method's flags and inclusions against it. The workflow runs it (rules `hic_*`) for samples with
|
|
199
|
+
`hic_r1`/`hic_r2`.
|
|
200
|
+
|
|
201
|
+
**Benchmark effect:** all 9 scenarios give the same status and bin for every contig as 0.4.0;
|
|
202
|
+
metrics equal to 1e-4. One ZymoOral contig (142 kb) changed P(foreign) 0.38 → 0.97 without a
|
|
203
|
+
status change (the own-MTase term removed; the methylome-difference gate keeps it in its bin).
|
|
204
|
+
The `--variation logit` option gives identical decisions on ZymoOral 2–30 kb; its human_gut test
|
|
205
|
+
is pending.
|
|
206
|
+
|
|
207
|
+
## 0.4.1 (2026-10-23): faster iteration
|
|
208
|
+
|
|
209
|
+
- Detectability and the redundancy check run threaded.
|
|
210
|
+
- `benchmarks/benchmark_suite.py --jobs N` runs scenarios in parallel (timings of such runs are
|
|
211
|
+
marked and not compared with single-job runs).
|
|
212
|
+
|
|
213
|
+
**Benchmark effect:** results identical (same decisions on all scenarios). The suite takes about a
|
|
214
|
+
third of the wall time with `--jobs 3`; the human_gut timing of the threaded steps is reported with
|
|
215
|
+
the next human_gut run.
|
|
216
|
+
|
|
217
|
+
## 0.4.0 (2026-10-23): refinement that holds up on a real gut community
|
|
218
|
+
|
|
219
|
+
- **Caller model fitted with the bins as labels** (D19). The pooled two-component fit collapsed in
|
|
220
|
+
365 of 367 motif groups on human_gut (a motif methylated in a few bins looks like noise in the
|
|
221
|
+
pool); each group is now fitted with bin membership as labels.
|
|
222
|
+
- **A contamination call needs a methylome difference** (D20): besides P(foreign) ≥ 0.5, the
|
|
223
|
+
contig's 95% credible interval for some motif group's methylated fraction must lie at least
|
|
224
|
+
`--min-methylome-difference` (0.3) from its bin's level. New column `methylome_difference`.
|
|
225
|
+
- **No inclusion of sequence the bin already holds** (D21): FracMinHash containment of the contig in
|
|
226
|
+
the bin ≥ `--max-redundancy` (0.3) gives status `redundant` instead of `included`. New column
|
|
227
|
+
`redundancy_with_bin`.
|
|
228
|
+
- `contig_decisions.tsv` gains `credible_set_95`; `--motif-fit` writes `motif_fit.tsv`.
|
|
229
|
+
|
|
230
|
+
**Benchmark effect** (vs 0.2.0 code, the last recorded baseline; 3 scenarios each):
|
|
231
|
+
|
|
232
|
+
| Benchmark | Contaminants found | Flag precision | Put back | Wall time |
|
|
233
|
+
|---|---|---|---|---|
|
|
234
|
+
| ZymoOral 5–150 kb | 100 → 100% | 96.5 → 100% | 92.6 → 86.2% | 55 → 37 s (−32%) |
|
|
235
|
+
| ZymoOral 2–30 kb | 100 → 99.7% | 99.1 → 99.7% | 89.9 → 82.8% | 57 → 37 s (−34%) |
|
|
236
|
+
| Simulated look-alikes | 97.0 → 96.4% | 100 → 100% | 0 → 0% | 145 → 122 s (−16%) |
|
|
237
|
+
|
|
238
|
+
Fewer contigs are put back (the redundancy filter refuses assembly duplicates that the benchmark
|
|
239
|
+
truth counts as members). human_gut, CheckM2 (369 bins):
|
|
240
|
+
|
|
241
|
+
| | Original | 0.3.0 | 0.4.0 |
|
|
242
|
+
|---|---|---|---|
|
|
243
|
+
| High-quality bins | 198 | 157 | 204 |
|
|
244
|
+
| Low-quality bins | 1 | 46 | 3 |
|
|
245
|
+
| Median completeness | 93.7% | 92.1% | 95.1% |
|
|
246
|
+
| Median contamination | 0.93% | 1.06% | 1.00% |
|
|
247
|
+
|
|
248
|
+
## 0.3.0 (2026-10-21): refinement workflow with CheckM2
|
|
249
|
+
|
|
250
|
+
- `workflow/Snakefile`: per sample, runs Nanomotif 1.1.2 (`detect_contamination` +
|
|
251
|
+
`include_contigs`) and pbinner on the same inputs, writes the original and refined bins as FASTA,
|
|
252
|
+
and scores them with CheckM2 1.1.0 (database downloaded by the workflow). Conda environments are
|
|
253
|
+
in `workflow/envs/`; per-sample `methods` chooses which bin sets to compare.
|
|
254
|
+
- `workflow/scripts/compare_bins.R`: per-bin and per-method CheckM2 comparison (quality tiers,
|
|
255
|
+
completeness and contamination change per bin, sequence moved), as tables and a figure page.
|
|
256
|
+
|
|
257
|
+
**Benchmark effect:** none on pbinner (no pbinner code changed). Workflow results (CheckM2):
|
|
258
|
+
|
|
259
|
+
| Sample | Bins | High-quality: original → Nanomotif / pbinner | Median completeness | Median contamination |
|
|
260
|
+
|---|---|---|---|---|
|
|
261
|
+
| ZymoOral with planted errors | 10 | 0 → 4 / 9 | 71.4 → 91.8 / 99.8% | 5.7 → 5.7 / 1.7% |
|
|
262
|
+
| ZymoOral as binned | 12 | 11 → 11 / 11 | 100 → 100 / 100% | 0.14 → 0.14 / 0.31% |
|
|
263
|
+
| human_gut (Nanomotif skipped) | 369 | 198 → – / 157 | 93.7 → – / 92.1% | 0.93 → – / 1.06% |
|
|
264
|
+
|
|
265
|
+
On human_gut pbinner's refinement makes bins worse: its contamination flags mostly hit genuine
|
|
266
|
+
members (see the dev log, D18).
|
|
267
|
+
|
|
268
|
+
## 0.2.1 (2026-10-21): project framework
|
|
269
|
+
|
|
270
|
+
- `CLAUDE.md`: project rules (branches only, commit every change, merge with version bump,
|
|
271
|
+
test gate, benchmark and report the effect of every change, dev log outside git).
|
|
272
|
+
- Pre-commit hook (`scripts/hooks/`): refuses commits on `main` and failing tests.
|
|
273
|
+
- `pbinner --version`.
|
|
274
|
+
- `scripts/benchmark_suite.py` with `benchmarks/suite.json`: records accuracy, wall time and peak
|
|
275
|
+
memory per version in the dev log.
|
|
276
|
+
|
|
277
|
+
**Benchmark effect:** none; no code that changes results. First recorded baseline (0.2.0 code):
|
|
278
|
+
|
|
279
|
+
| Benchmark | Contaminants found | Flag precision | Put back | Wrongly placed / scenario | Completeness after | Wall time | Peak RAM |
|
|
280
|
+
|---|---|---|---|---|---|---|---|
|
|
281
|
+
| ZymoOral 5–150 kb | 100% | 96.5% | 92.6% | 1.0 | 98.7% | 55 s | 1.0 GB |
|
|
282
|
+
| ZymoOral 2–30 kb | 100% | 99.1% | 89.9% | 0.3 | 98.2% | 57 s | 1.1 GB |
|
|
283
|
+
| Simulated look-alikes | 97.0% | 100% | 0% | 0.7 | 76.1% | 145 s | 2.7 GB |
|
|
284
|
+
|
|
285
|
+
## 0.2.0 (2026-10-21): baseline under version control
|
|
286
|
+
|
|
287
|
+
Everything before this version was developed without version control; the reasoning is in the dev
|
|
288
|
+
log (`sessions/`, `DECISIONS.md`).
|
|
289
|
+
|
|
290
|
+
**Bin refinement (new focus).**
|
|
291
|
+
- Every contig is scored: binned contigs leave-one-out against their own bin, unbinned contigs
|
|
292
|
+
against every bin. `refine.py` turns the posteriors into decisions:
|
|
293
|
+
- contamination: P(foreign) from a mixture whose contamination rate is estimated from the data;
|
|
294
|
+
- inclusion: a contig goes into a bin at posterior ≥ 0.95 (`--include-threshold`); the share of
|
|
295
|
+
unbinned contigs from unbinned genomes is estimated from the data (`--p-unknown` overrides).
|
|
296
|
+
- Nanomotif-compatible outputs: `bin_contamination.tsv`, `decontaminated_contig_bin.tsv`,
|
|
297
|
+
`include_contigs.tsv`, `new_contig_bin.tsv`; per-contig decisions in `contig_decisions.tsv`.
|
|
298
|
+
- Plasmid-specific parameters (δ, τ_p, own-MTase ρ) apply only to `--plasmids` candidates.
|
|
299
|
+
- Unbinned genomes are modelled as a community draw or a relative of a bin (switch probability η,
|
|
300
|
+
weight λ, fitted in calibration with the temperature).
|
|
301
|
+
- τ (between-chunk variation) is chosen per motif group.
|
|
302
|
+
- Calibration's host-absent test uses only fragments that can tell their bin from its nearest
|
|
303
|
+
competitor.
|
|
304
|
+
|
|
305
|
+
**Speed and memory (`docs/scalability.md`).**
|
|
306
|
+
- Shape-based motif matcher (identical records, 58x faster than regex).
|
|
307
|
+
- Parallel streaming pileup pass with a reusable site cache; no in-memory motif records.
|
|
308
|
+
- Lookup-table site likelihoods, float32 tables, vectorised NPMLE, summed scores without
|
|
309
|
+
[units × bins × groups] tensors, threaded robust-profile step, fixed calibration budget.
|
|
310
|
+
- human_gut (1.73 Gb, 369 bins, 235 GB pileup): about 56 min from scratch, 45 min with the cache,
|
|
311
|
+
against a projected 2 days for 0.1.0.
|
|
312
|
+
|
|
313
|
+
**Fixes.**
|
|
314
|
+
- Caller-model fit could stop at a degenerate optimum depending on row order.
|
|
315
|
+
- Motif-group numbering and fragment selection depended on polars group order.
|
|
316
|
+
- Fragment sampling assumed sorted input.
|
|
317
|
+
- Site-cache paths were relative to the directory the cache was made from.
|
|
318
|
+
|
|
319
|
+
**Benchmark (vs Nanomotif 1.1.2 include_contigs, same inputs).** ZymoOral, 6 scenarios:
|
|
320
|
+
contaminants found 100% (Nanomotif 15–42%), contigs put back 85–97% (46–81%), 0–1 contigs of
|
|
321
|
+
unbinned genomes placed in a bin (6–69). Simulated look-alike community: contaminants found
|
|
322
|
+
96–98%, almost nothing put back at the 0.95 threshold (methylation cannot separate sibling strains).
|
|
323
|
+
|
|
324
|
+
## 0.1.0 (2026-10-19)
|
|
325
|
+
|
|
326
|
+
Plasmid-host assignment from methylation: hierarchical Bayesian model (caller model, discrete
|
|
327
|
+
methylation grid, population prior, robust host profiles), pseudo-plasmid calibration,
|
|
328
|
+
MTase-aware scoring, assignability, HTML report.
|
epirefine-0.10.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Søren Heidelbach
|
|
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,177 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: epirefine
|
|
3
|
+
Version: 0.10.0
|
|
4
|
+
Summary: Refine metagenome bins from nanopore methylation: flag contaminants and place unbinned contigs, with calibrated probabilities
|
|
5
|
+
Project-URL: Homepage, https://github.com/SorenHeidelbach/epirefine
|
|
6
|
+
Project-URL: Repository, https://github.com/SorenHeidelbach/epirefine
|
|
7
|
+
Project-URL: Issues, https://github.com/SorenHeidelbach/epirefine/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/SorenHeidelbach/epirefine/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Søren Heidelbach <shei@bio.aau.dk>
|
|
10
|
+
Maintainer-email: Søren Heidelbach <shei@bio.aau.dk>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: DNA methylation,MAG,Nanomotif,bin refinement,binning,metagenomics,nanopore
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
27
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Requires-Dist: numpy>=1.26
|
|
30
|
+
Requires-Dist: polars>=1.2
|
|
31
|
+
Requires-Dist: scipy>=1.11
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# EpiRefine
|
|
37
|
+
|
|
38
|
+
[](https://github.com/SorenHeidelbach/epirefine/actions/workflows/tests.yml)
|
|
39
|
+
|
|
40
|
+
*Epi*genetics + *refine*: it refines metagenome bins using the methylation every genome carries. Formerly `pbinner`.
|
|
41
|
+
|
|
42
|
+
Bin refinement from nanopore methylation, built on Nanomotif output:
|
|
43
|
+
|
|
44
|
+
- **contamination detection:** flags binned contigs whose methylation doesn't fit their bin;
|
|
45
|
+
- **contig inclusion:** places unbinned contigs into the bin they belong to.
|
|
46
|
+
|
|
47
|
+
Every decision comes with a calibrated posterior, including an explicit "unbinned genome" class, so
|
|
48
|
+
contigs of genomes that have no bin stay out of bins. Outputs include Nanomotif-compatible files
|
|
49
|
+
(`bin_contamination.tsv`, `include_contigs.tsv`, `new_contig_bin.tsv`,
|
|
50
|
+
`decontaminated_contig_bin.tsv`). Calibration is estimated inside each sample from pseudo-contigs
|
|
51
|
+
cut from binned contigs.
|
|
52
|
+
|
|
53
|
+
- Method: [docs/method.md](https://github.com/SorenHeidelbach/epirefine/blob/main/docs/method.md).
|
|
54
|
+
- Performance work: [docs/scalability.md](https://github.com/SorenHeidelbach/epirefine/blob/main/docs/scalability.md).
|
|
55
|
+
- Changes per version: [CHANGELOG.md](https://github.com/SorenHeidelbach/epirefine/blob/main/CHANGELOG.md).
|
|
56
|
+
- Development rules: [CLAUDE.md](https://github.com/SorenHeidelbach/epirefine/blob/main/CLAUDE.md).
|
|
57
|
+
|
|
58
|
+
## Repository layout
|
|
59
|
+
|
|
60
|
+
| Folder | Content |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `src/epirefine/` | The package: methylation-based contamination detection and contig inclusion, nothing else |
|
|
63
|
+
| `tests/` | Unit tests (`.venv/bin/python -m pytest -q tests`) |
|
|
64
|
+
| `benchmarks/` | Monoculture benchmark: suite runner, the scripts that build the sets, the evaluator; results of every version in [`benchmarks/results.tsv`](https://github.com/SorenHeidelbach/epirefine/blob/main/benchmarks/results.tsv) |
|
|
65
|
+
| `dev/` | Development tooling: git hooks, speed prototypes |
|
|
66
|
+
| `docs/` | Method and performance notes |
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install epirefine # from PyPI
|
|
72
|
+
conda install -c bioconda epirefine # from Bioconda (once the recipe is accepted)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
From source, for development:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/SorenHeidelbach/epirefine.git && cd epirefine
|
|
79
|
+
uv venv && uv pip install -e ".[dev]"
|
|
80
|
+
.venv/bin/pytest -q
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Python 3.10-3.15 (tested on every push, together with the lowest dependency versions allowed:
|
|
84
|
+
numpy 1.26, scipy 1.11, polars 1.2).
|
|
85
|
+
|
|
86
|
+
## Running EpiRefine
|
|
87
|
+
|
|
88
|
+
| Flag | File | Notes |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `--assembly` | assembly FASTA | |
|
|
91
|
+
| `--pileup` | `modkit pileup` bedMethyl (`.bed`/`.bed.gz`) | Space-separated (older modkit) or all-tab formats. Only rows at motif sites are kept while streaming. |
|
|
92
|
+
| `--contig-bin` | `contig<TAB>bin`, no header | The bins to refine |
|
|
93
|
+
| `--motifs` | Nanomotif `bin-motifs.tsv` | Only the **union** of motifs is used (+ `motif_complement`). Which bin carries which motif is re-estimated from the data. |
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
epirefine run --assembly asm.fasta --pileup pileup.bed --contig-bin contig_bin.tsv \
|
|
97
|
+
--motifs nanomotif/bin-motifs.tsv --out epirefine_out --workers 16
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Decision thresholds:**
|
|
101
|
+
- `--contamination-threshold` (0.5): flag a binned contig when P(foreign) is at least this,
|
|
102
|
+
- `--min-methylome-difference` (0.3): ... and its 95% credible interval for some motif group's
|
|
103
|
+
methylated fraction lies at least this far from its bin's level (an effect size, so that many
|
|
104
|
+
weak site-level differences don't add up to a flag);
|
|
105
|
+
- `--include-threshold` (0.95): include an unbinned contig when one bin's posterior is at least this;
|
|
106
|
+
- `--max-redundancy` (0.3): don't include a contig when this share of its k-mers is already in the
|
|
107
|
+
bin (assembly duplicates; status `redundant`).
|
|
108
|
+
|
|
109
|
+
**Model options:** `--variation beta|logit` (how a contig's methylation varies around its bin's;
|
|
110
|
+
`--tau` overrides the calibrated value), `--temperature`, `--allow-sharpening` (default: the
|
|
111
|
+
temperature is clipped at ≥ 1), `--p-unknown`, `--contam-eps`, `--chunk-size`.
|
|
112
|
+
`--regional-inclusion` (experimental, off by default) also includes long unbinned contigs whose
|
|
113
|
+
50 kb windows match a bin when the whole contig doesn't.
|
|
114
|
+
|
|
115
|
+
**Running:**
|
|
116
|
+
- `--workers N` sets the processes for the pileup and table passes (default: CPUs, at most 16).
|
|
117
|
+
- `--cache-dir` holds the site cache (default `<out>/cache`). A rerun with the same assembly, pileup
|
|
118
|
+
and motifs reuses it and skips the pileup pass.
|
|
119
|
+
- `--frag-budget` (default 6000) is the total number of calibration fragments, spread over bins and
|
|
120
|
+
lengths with at most `--frags-per-bin` each.
|
|
121
|
+
- `--motif-fit` also writes each contig's methylated fraction per motif group next to its bin's.
|
|
122
|
+
|
|
123
|
+
The pileup must have each contig's rows together (as modkit writes it). Uncompressed pileups are read
|
|
124
|
+
in parallel byte ranges; gzipped ones by a single worker.
|
|
125
|
+
|
|
126
|
+
## Outputs
|
|
127
|
+
|
|
128
|
+
| File | Content |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `contig_decisions.tsv` | Per contig: `status`, given and new bin, `p_contaminant`, `methylome_difference`, `redundancy_with_bin`, best alternative bin and its posterior, MAP, `p_unknown`, 95% credible set |
|
|
131
|
+
| `bin_contamination.tsv`, `include_contigs.tsv`, `new_contig_bin.tsv`, `decontaminated_contig_bin.tsv` | Nanomotif-compatible results |
|
|
132
|
+
| `host_profiles.tsv` | Per bin × motif group: methylated fraction (posterior mean, 95% interval) |
|
|
133
|
+
| `host_chunk_outliers.tsv` | P(chunk is foreign to its bin) for every 50 kb chunk of a binned contig |
|
|
134
|
+
| `detection_model.tsv` | Learned caller behaviour per motif group (μ0, μ1, κ0, κ1) |
|
|
135
|
+
| `motif_groups.tsv` | Which motifs were merged into which feature, with site and read counts |
|
|
136
|
+
| `motif_fit.tsv` | With `--motif-fit`: per contig × motif group, observed methylated fraction (95% CI) and the bin's |
|
|
137
|
+
| `calibration/` | Pseudo-contig calibration: accuracy, ECE, credible-set coverage per length, temperature and τ fits |
|
|
138
|
+
| `params.json` | Fitted parameters and settings |
|
|
139
|
+
|
|
140
|
+
**Status** in `contig_decisions.tsv`:
|
|
141
|
+
|
|
142
|
+
| Status | Meaning |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `kept` | Binned contig that fits its bin |
|
|
145
|
+
| `contaminant` | Binned contig flagged as foreign; removed from its bin |
|
|
146
|
+
| `moved` | Flagged contaminant that confidently belongs to another bin; moved there |
|
|
147
|
+
| `sole_contig_of_bin` | Only contig of its bin: no leave-one-out profile, not judged |
|
|
148
|
+
| `included` | Unbinned contig placed into a bin |
|
|
149
|
+
| `redundant` | Would be included, but its sequence is already in the bin |
|
|
150
|
+
| `unbinned` | Unbinned contig left out (uncertain, or its genome has no bin) |
|
|
151
|
+
|
|
152
|
+
## Workflow and evaluation
|
|
153
|
+
|
|
154
|
+
The Snakemake workflow (Nanomotif, EpiRefine, CheckM2), the Hi-C validation and the benchmarks beyond the
|
|
155
|
+
monocultures (ZymoOral pieces, simulated look-alikes, chimeras) live in a separate repository that calls this
|
|
156
|
+
tool (`epirefine_workflow`; moved out in 0.9.1).
|
|
157
|
+
|
|
158
|
+
## Benchmarks
|
|
159
|
+
|
|
160
|
+
`benchmarks/benchmark_suite.py --note "<change>"` runs the monoculture benchmark (`benchmarks/suite_monocultures.json`):
|
|
161
|
+
communities built from isolates sequenced alone, so the origin of every contig is known, with planted errors
|
|
162
|
+
(contaminants, removed members, genomes without a bin). By default it runs the six training communities
|
|
163
|
+
(about 5 minutes); `--all` adds the held-out test and validation communities, for final evaluation only. Every
|
|
164
|
+
run appends one row per benchmark group and version to [`benchmarks/results.tsv`](https://github.com/SorenHeidelbach/epirefine/blob/main/benchmarks/results.tsv):
|
|
165
|
+
contaminants found, flag precision, contigs put back, contigs of bin-less genomes placed, bin completeness and
|
|
166
|
+
contamination after refinement, wall time and peak memory. `benchmarks/monoculture_sets.py` rebuilds the sets.
|
|
167
|
+
|
|
168
|
+
## Known limitations
|
|
169
|
+
|
|
170
|
+
- **One bin per contig.** A contig assembled across two genomes is not split.
|
|
171
|
+
- **Robust profiles assume a contaminant looks like a "random community genome".** A bin that is a
|
|
172
|
+
50/50 merge of two genomes is not resolved.
|
|
173
|
+
- **Motif groups are treated as independent.** A site matched by several groups counts in the most specific
|
|
174
|
+
one, so one methyltransferase can show up in several partly methylated groups and count more than once.
|
|
175
|
+
- **No coverage, co-abundance or taxonomy evidence**: methylation only.
|
|
176
|
+
- **Dropped constant:** the binomial coefficient is omitted from likelihoods, so reported
|
|
177
|
+
log-likelihoods are relative. Bayes factors and posteriors are unaffected.
|