bam-dedup 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Caleb Lareau
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,21 @@
1
+ # Ship Cython sources (and generated C, if present) so sdists build cleanly
2
+ recursive-include dedup *.pyx *.pxd *.c
3
+
4
+ # Test + benchmark code and small BAMs used by the suite
5
+ recursive-include tests *.py *.md
6
+ recursive-include tests/data *.bam *.bai
7
+
8
+ # Misc
9
+ include README.md
10
+ include LICENSE.txt
11
+ include pyproject.toml
12
+
13
+ # Prune build cruft
14
+ prune *.egg-info
15
+ global-exclude *.so
16
+ global-exclude *.DS_Store
17
+ global-exclude __pycache__
18
+
19
+ # Keep the 15 MB Picard jar out of the distributable (repo-only, for local
20
+ # benchmarking). See tests/benchmark/README.md.
21
+ global-exclude *.jar
@@ -0,0 +1,40 @@
1
+ Metadata-Version: 2.4
2
+ Name: bam-dedup
3
+ Version: 0.2.0
4
+ Summary: Fast, JVM-free BAM deduplication: PCR/optical (Picard-like) and molecular/UMI consensus (fgbio-like).
5
+ Home-page: https://github.com/caleblareau/bam-dedup
6
+ Author: Caleb Lareau
7
+ Author-email: caleb.lareau@gmail.com
8
+ License: MIT
9
+ Platform: any
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: POSIX
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: Unix
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Cython
20
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
21
+ Requires-Python: >=3.8
22
+ License-File: LICENSE.txt
23
+ Requires-Dist: pysam
24
+ Provides-Extra: test
25
+ Requires-Dist: pytest; extra == "test"
26
+ Dynamic: author
27
+ Dynamic: author-email
28
+ Dynamic: classifier
29
+ Dynamic: description
30
+ Dynamic: home-page
31
+ Dynamic: license
32
+ Dynamic: license-file
33
+ Dynamic: platform
34
+ Dynamic: provides-extra
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+
40
+ bam-dedup: a fast, JVM-free reimplementation of familiar tools.
@@ -0,0 +1,199 @@
1
+ # bam-dedup
2
+
3
+ A fast, **JVM-free** toolkit for removing duplicate reads from BAM/CRAM. It
4
+ handles the **two major kinds of duplication event** in sequencing data, each a
5
+ faithful, independent port of the standard reference tool, with the
6
+ performance-critical inner loops accelerated in Cython. All BAM/CRAM I/O goes
7
+ through [pysam](https://github.com/pysam-developers/pysam) (htslib) — no Java
8
+ required.
9
+
10
+ | Duplication kind | Module | Reproduces | What it does |
11
+ |---|---|---|---|
12
+ | **PCR / optical duplicates** | `dedup.picardlike` | [Picard MarkDuplicates](https://broadinstitute.github.io/picard/) | flags or removes duplicate *records* by 5′ position + orientation |
13
+ | **Molecular / UMI duplicates** | `dedup.fgbiolike` | [fgbio](https://github.com/fulcrumgenomics/fgbio) GroupReadsByUmi + CallMolecularConsensusReads | collapses reads sharing a UMI into one error-corrected *consensus* read |
14
+
15
+ Both are validated against their reference tool:
16
+
17
+ * `picardlike` is **bit-identical to Picard MarkDuplicates 2.18.21** for the
18
+ duplicate flag *and* the optical/sequencing-duplicate classification.
19
+ * `fgbiolike` is **bit-identical to fgbio 4.1.0** (`GroupReadsByUmi -s Identity`
20
+ and `CallMolecularConsensusReads`) for the grouped `MI` assignment and every
21
+ consensus base, quality, and `cD`/`cM`/`cE`/`cd`/`ce` tag — verified
22
+ record-for-record on both a bundled test BAM and a 176k-read MAESTER file.
23
+
24
+ Both run faster than the corresponding Java tool.
25
+
26
+ > The importable modules `dedup.picardlike` / `dedup.fgbiolike` are independent
27
+ > reimplementations, **not** affiliated with or derived from the Picard or fgbio
28
+ > source code; those names are referenced only to describe the behavior
29
+ > reproduced.
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ### From PyPI (once published)
36
+
37
+ ```bash
38
+ pip install bam-dedup
39
+ ```
40
+
41
+ ### From source
42
+
43
+ Requires a C compiler (clang/gcc) and Python ≥ 3.8. Cython is pulled in
44
+ automatically as a build dependency via `pyproject.toml`; the only *runtime*
45
+ dependency is `pysam`.
46
+
47
+ ```bash
48
+ git clone https://github.com/caleblareau/bam-dedup.git
49
+ cd bam-dedup
50
+ pip install .
51
+ ```
52
+
53
+ ### For development (editable install + tests)
54
+
55
+ An editable install compiles the Cython extension (`dedup._fast`) in place, so
56
+ `pytest` can find it:
57
+
58
+ ```bash
59
+ pip install -e ".[test]"
60
+ pytest
61
+ ```
62
+
63
+ Verify the compiled acceleration is active:
64
+
65
+ ```python
66
+ import dedup
67
+ print(dedup.HAVE_CYTHON) # True when the Cython extension is built
68
+ ```
69
+
70
+ If `HAVE_CYTHON` is `False`, the package still runs correctly using a pure-Python
71
+ fallback that produces bit-identical results (just slower).
72
+
73
+ ---
74
+
75
+ ## Usage
76
+
77
+ pysam-like: point at an input BAM, call one function, get an output BAM.
78
+
79
+ ### PCR / optical duplicates (`picardlike`)
80
+
81
+ Input must be **coordinate-sorted** (e.g. `samtools sort`).
82
+
83
+ ```python
84
+ from dedup import picardlike
85
+
86
+ # Picard-style: flag duplicates in place (records kept, 0x400 flag set)
87
+ picardlike.mark_duplicates("input.sorted.bam", "marked.bam")
88
+
89
+ # Produce a deduplicated BAM (duplicate records removed)
90
+ picardlike.deduplicate("input.sorted.bam", "dedup.bam")
91
+ ```
92
+
93
+ `mark_duplicates(input_bam, output_bam, **options)`:
94
+
95
+ | option | default | meaning |
96
+ |---|---|---|
97
+ | `remove_duplicates` | `False` | drop duplicate records instead of only flagging them |
98
+ | `remove_sequencing_duplicates` | `False` | drop optical/sequencing duplicates |
99
+ | `read_name_regex_enabled` | `True` | parse tile/x/y from read names for optical detection |
100
+ | `optical_pixel_distance` | `100` | optical duplicate pixel distance |
101
+
102
+ `deduplicate(...)` is a convenience wrapper equal to
103
+ `mark_duplicates(..., remove_duplicates=True)`.
104
+
105
+ ### Molecular / UMI duplicates (`fgbiolike`)
106
+
107
+ Reads must carry a UMI tag (default `UB`); a cell-barcode tag (default `CB`) is
108
+ used when present. The one-shot `consensus()` runs the two fgbio steps —
109
+ grouping reads into molecules, then collapsing each molecule into a consensus
110
+ read:
111
+
112
+ ```python
113
+ from dedup import fgbiolike
114
+
115
+ # group by UMI (+ cell barcode) and emit error-corrected consensus reads
116
+ fgbiolike.consensus("input.bam", "consensus.bam", umi_tag="UB", min_reads=3)
117
+ ```
118
+
119
+ Or run the two stages explicitly:
120
+
121
+ ```python
122
+ fgbiolike.group_reads_by_umi("input.bam", "grouped.bam", raw_tag="UB")
123
+ fgbiolike.call_molecular_consensus_reads("grouped.bam", "consensus.bam",
124
+ tag="MI", min_reads=3)
125
+ ```
126
+
127
+ `consensus(...)` / `call_molecular_consensus_reads(...)` options:
128
+
129
+ | option | default | meaning |
130
+ |---|---|---|
131
+ | `min_reads` | `3` | minimum reads in a molecule to emit a consensus |
132
+ | `umi_tag` | `"UB"` | raw UMI tag used for grouping |
133
+ | `cell_tag` | `"CB"` | cell-barcode tag; consensus is called per cell |
134
+ | `consensus_tag` | `"MI"` | tag grouped on in the consensus step (see note) |
135
+ | `on_cell_collision` | `"split"` | if a molecule spans >1 cell: `split`, `merge`, or `error` |
136
+
137
+ > **Grouping key.** The consensus step groups by the assigned molecular id `MI`
138
+ > (per position × cell × UMI). This is the correct, crash-free equivalent of
139
+ > fgbio's `-t UB`: fgbio *aborts* if a raw-UMI group ever spans two cell
140
+ > barcodes, whereas `on_cell_collision="split"` (the default) simply calls one
141
+ > consensus per cell.
142
+
143
+ ### Command line
144
+
145
+ Installing the package provides two console commands:
146
+
147
+ ```bash
148
+ # PCR/optical duplicate marking
149
+ bam-dedup -i input.sorted.bam -o dedup.bam --remove-duplicates
150
+
151
+ # UMI molecular-consensus deduplication (group + call in one step)
152
+ bam-consensus consensus -i input.bam -o consensus.bam -M 3
153
+ # or the individual stages:
154
+ bam-consensus group -i input.bam -o grouped.bam -t UB
155
+ bam-consensus call -i grouped.bam -o consensus.bam -t MI -M 3
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Scope
161
+
162
+ **`picardlike`** — faithful to Picard for coordinate-sorted input, the default
163
+ `SUM_OF_BASE_QUALITIES` scoring strategy, standard Illumina read names, and
164
+ single/multiple libraries. *Not yet handled:* queryname-sorted input, flow-based
165
+ mode, and the non-default scoring strategies (`TOTAL_MAPPED_REFERENCE_LENGTH`,
166
+ `RANDOM`).
167
+
168
+ **`fgbiolike`** — faithful to fgbio for the `Identity` grouping strategy
169
+ (`edits=0`) and the single-end / fragment consensus path (the MAESTER-style
170
+ input), with fgbio's default error model (pre=45, post=40),
171
+ `min-input-base-quality=10`, and per-base tags. *Not yet handled:* the
172
+ Adjacency/Edit/Paired strategies (`edits > 0`) and paired-end / duplex /
173
+ overlapping-base consensus.
174
+
175
+ ---
176
+
177
+ ## Tests
178
+
179
+ ```bash
180
+ pip install -e ".[test]"
181
+ pytest
182
+ ```
183
+
184
+ `tests/test_dedup.py` validates duplicate and optical-duplicate counts against
185
+ Picard's reference output on the bundled BAMs; `tests/test_fgbiolike.py`
186
+ validates UMI grouping and consensus reads against **fgbio golden outputs**
187
+ (`tests/data/umi_consensus.*.golden.bam`). Both suites also check that the Cython
188
+ and pure-Python paths agree.
189
+
190
+ ## Benchmarking against Picard
191
+
192
+ Reproducible head-to-head scripts (and a bundled `picard.jar`) live in
193
+ [`tests/benchmark/`](tests/benchmark/). See
194
+ [`tests/benchmark/README.md`](tests/benchmark/README.md) for the concordance and
195
+ timing harness.
196
+
197
+ ## License
198
+
199
+ MIT © Caleb Lareau
@@ -0,0 +1,25 @@
1
+ LICENSE.txt
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.py
6
+ dedup/__init__.py
7
+ dedup/_fast.c
8
+ dedup/_fast.pyx
9
+ dedup/cli.py
10
+ dedup/fgbiolike.py
11
+ dedup/picardlike.py
12
+ tests/__init__.py
13
+ tests/test_dedup.py
14
+ tests/test_fgbiolike.py
15
+ tests/benchmark/README.md
16
+ tests/benchmark/compare_flags.py
17
+ tests/benchmark/make_stress_bam.py
18
+ tests/benchmark/run_benchmark.py
19
+ tests/data/NA12891.bam
20
+ tests/data/NA12892.bam
21
+ tests/data/make_umi_bam.py
22
+ tests/data/synthetic_optical.bam
23
+ tests/data/umi_consensus.bam
24
+ tests/data/umi_consensus.consensus.golden.bam
25
+ tests/data/umi_consensus.grouped.golden.bam
@@ -0,0 +1,46 @@
1
+ """
2
+ bam-dedup: a fast, JVM-free toolkit for removing duplicate reads from BAM/CRAM.
3
+
4
+ It handles the two major classes of duplicate with faithful, independent ports of
5
+ the reference tools, both accelerated in Cython (:mod:`dedup._fast`) and doing all
6
+ I/O through pysam (htslib) -- no Java dependency:
7
+
8
+ * **PCR / optical duplicates** -- :mod:`dedup.picardlike`, a port of Picard/htsjdk
9
+ MarkDuplicates (coordinate-sorted, Illumina paired-end, default options). Marks
10
+ or removes duplicate *records*.
11
+
12
+ * **Molecular (UMI) duplicates** -- :mod:`dedup.fgbiolike`, a port of fgbio
13
+ GroupReadsByUmi + CallMolecularConsensusReads (Identity strategy, fragment
14
+ path). Collapses reads sharing a UMI into one error-corrected *consensus* read.
15
+
16
+ Typical use::
17
+
18
+ from dedup import picardlike, fgbiolike
19
+
20
+ # PCR/optical: flag duplicates (records kept, 0x400 flag set)
21
+ picardlike.mark_duplicates("input.bam", "marked.bam")
22
+
23
+ # molecular/UMI: group by UMI and call consensus reads
24
+ fgbiolike.consensus("input.bam", "consensus.bam", umi_tag="UB", min_reads=3)
25
+ """
26
+
27
+ from dedup.picardlike import mark_duplicates, deduplicate
28
+ from dedup.fgbiolike import (
29
+ group_reads_by_umi,
30
+ call_molecular_consensus_reads,
31
+ consensus,
32
+ )
33
+
34
+ try:
35
+ from dedup import _fast # noqa: F401
36
+ HAVE_CYTHON = True
37
+ except ImportError: # pragma: no cover
38
+ HAVE_CYTHON = False
39
+
40
+ __version__ = "0.2.0"
41
+ __all__ = [
42
+ "picardlike", "fgbiolike",
43
+ "mark_duplicates", "deduplicate",
44
+ "group_reads_by_umi", "call_molecular_consensus_reads", "consensus",
45
+ "HAVE_CYTHON", "__version__",
46
+ ]