denseclass 0.1.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.
- denseclass-0.1.0/LICENCE.txt +21 -0
- denseclass-0.1.0/MANIFEST.in +2 -0
- denseclass-0.1.0/PKG-INFO +246 -0
- denseclass-0.1.0/README.md +207 -0
- denseclass-0.1.0/denseclass/__init__.py +12 -0
- denseclass-0.1.0/denseclass/__main__.py +4 -0
- denseclass-0.1.0/denseclass/base_util.py +41 -0
- denseclass-0.1.0/denseclass/cli.py +76 -0
- denseclass-0.1.0/denseclass/comparison.py +1220 -0
- denseclass-0.1.0/denseclass/core.py +719 -0
- denseclass-0.1.0/denseclass/evaluation.py +775 -0
- denseclass-0.1.0/denseclass/gendata.py +238 -0
- denseclass-0.1.0/denseclass/genome_sampling.py +186 -0
- denseclass-0.1.0/denseclass/label.py +78 -0
- denseclass-0.1.0/denseclass/model.py +142 -0
- denseclass-0.1.0/denseclass/precision.py +234 -0
- denseclass-0.1.0/denseclass/predict.py +265 -0
- denseclass-0.1.0/denseclass/split.py +79 -0
- denseclass-0.1.0/denseclass/train.py +407 -0
- denseclass-0.1.0/denseclass/utils.py +457 -0
- denseclass-0.1.0/denseclass.egg-info/PKG-INFO +246 -0
- denseclass-0.1.0/denseclass.egg-info/SOURCES.txt +29 -0
- denseclass-0.1.0/denseclass.egg-info/dependency_links.txt +1 -0
- denseclass-0.1.0/denseclass.egg-info/entry_points.txt +2 -0
- denseclass-0.1.0/denseclass.egg-info/not-zip-safe +1 -0
- denseclass-0.1.0/denseclass.egg-info/requires.txt +15 -0
- denseclass-0.1.0/denseclass.egg-info/top_level.txt +1 -0
- denseclass-0.1.0/requirements.txt +20 -0
- denseclass-0.1.0/setup.cfg +4 -0
- denseclass-0.1.0/setup.py +58 -0
- denseclass-0.1.0/tests/test_structure.py +308 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DenseCall2 contributors
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: denseclass
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Author: linlian
|
|
5
|
+
Author-email: 21620151153308@stu.xmu.edu.cn
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENCE.txt
|
|
15
|
+
Requires-Dist: numpy>=1.23
|
|
16
|
+
Requires-Dist: scipy>=1.9
|
|
17
|
+
Requires-Dist: pandas>=1.5
|
|
18
|
+
Requires-Dist: h5py>=3.7
|
|
19
|
+
Requires-Dist: torch>=2.0
|
|
20
|
+
Requires-Dist: tqdm>=4.64
|
|
21
|
+
Requires-Dist: toml>=0.10
|
|
22
|
+
Requires-Dist: scikit-learn>=1.1
|
|
23
|
+
Requires-Dist: matplotlib>=3.6
|
|
24
|
+
Requires-Dist: seaborn>=0.12
|
|
25
|
+
Requires-Dist: biopython>=1.80
|
|
26
|
+
Requires-Dist: numba>=0.57
|
|
27
|
+
Requires-Dist: plotnine>=0.12
|
|
28
|
+
Requires-Dist: ont-fast5-api>=4.1
|
|
29
|
+
Requires-Dist: ont-bonito>=0.7
|
|
30
|
+
Dynamic: author
|
|
31
|
+
Dynamic: author-email
|
|
32
|
+
Dynamic: classifier
|
|
33
|
+
Dynamic: description
|
|
34
|
+
Dynamic: description-content-type
|
|
35
|
+
Dynamic: license
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
|
|
40
|
+
<div align="center">
|
|
41
|
+
|
|
42
|
+
# 🦠**denseclass**
|
|
43
|
+
|
|
44
|
+
**Nanopore signal classification** — label every raw read by species, barcode or taxon,
|
|
45
|
+
straight from the current, with no basecalling step.
|
|
46
|
+
|
|
47
|
+
[](https://www.python.org/)
|
|
48
|
+
[](LICENCE.txt)
|
|
49
|
+
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 📖 Table of Contents
|
|
55
|
+
|
|
56
|
+
- [Overview](#-overview)
|
|
57
|
+
- [Installation](#-installation)
|
|
58
|
+
- [Quick start](#-quick-start)
|
|
59
|
+
- [Subcommands](#-subcommands)
|
|
60
|
+
- [Training modes](#-training-modes)
|
|
61
|
+
- [Examples](#-examples)
|
|
62
|
+
- [Relationship to densecall](#-relationship-to-densecall)
|
|
63
|
+
- [Development](#-development)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 🧬 Overview
|
|
68
|
+
|
|
69
|
+
Reads are trimmed to their informative region (adapter/polyA detection for DNA,
|
|
70
|
+
poly(A)-tail trimming for RNA), encoded, and passed through a small 1-D CNN that
|
|
71
|
+
predicts one class per read. Training works either on **real** labelled reads or on
|
|
72
|
+
**signal simulated** from reference genomes, so you can build a classifier without
|
|
73
|
+
per-species reads for every class.
|
|
74
|
+
|
|
75
|
+
This package was split out of [DenseCall](https://github.com/n-damo/densecall) and
|
|
76
|
+
now stands alone — see [Relationship to densecall](#-relationship-to-densecall).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 📦 Installation
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone <this-repo>
|
|
84
|
+
cd denseclass
|
|
85
|
+
pip install -e .
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`torch` is deliberately unpinned so the resolver picks a build matching your local
|
|
89
|
+
CUDA toolkit. For GPU basecalling-adjacent work, install the CUDA toolkit first and
|
|
90
|
+
verify `nvcc` is on `$PATH`:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
conda install -c nvidia cuda-toolkit=12.8
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 🚀 Quick start
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
denseclass --help
|
|
102
|
+
denseclass label --help
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🎯 Subcommands
|
|
108
|
+
|
|
109
|
+
| Subcommand | Purpose |
|
|
110
|
+
|:--|:--|
|
|
111
|
+
| `label` | Build `label_map.json` / `inverse_label_map.json` from a FASTA whose headers are the class names |
|
|
112
|
+
| `split` | Split a read→label pairs file into train / test sets |
|
|
113
|
+
| `gendata` | Convert POD5/FAST5 + labels into `train.hdf5` / `valid.hdf5` |
|
|
114
|
+
| `train` | Train the classification CNN (real or simulated data) |
|
|
115
|
+
| `predict` | Classify reads with a trained model |
|
|
116
|
+
| `precision` | One-class precision / recall / F1 / ROC evaluation |
|
|
117
|
+
| `evaluation` | Full multiclass panel (confusion matrix, PR/ROC, ...) |
|
|
118
|
+
| `comparison` | Side-by-side comparison of two models |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 🧪 Training modes
|
|
123
|
+
|
|
124
|
+
| Mode | Description |
|
|
125
|
+
|:--|:--|
|
|
126
|
+
| **Real-data** | `gendata` extracts signal chunks from reads already labelled by species, then `train` (no `--simu`) fits the CNN directly. |
|
|
127
|
+
| **Simulation** | `train --simu <refs.fa> --preset <name>` synthesises chunks on the fly from a FASTA of reference genomes. Presets: `dna-r9-min`, `dna-r10-min`, `rna-r9-min`, `rna004-min`, ... |
|
|
128
|
+
|
|
129
|
+
The CNN architecture is set by a small TOML (`dna.toml` with `strides=[2,2,2,2,2]` for
|
|
130
|
+
DNA, `rna.toml` with `strides=[5,2,2,2,2]` for RNA) passed via `-config`. The
|
|
131
|
+
`label_map.json` / `inverse_label_map.json` pair must be produced first with
|
|
132
|
+
`denseclass label`.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 🧪 Examples
|
|
137
|
+
|
|
138
|
+
### Barcode / species classification on real reads
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# 1. build the label map
|
|
142
|
+
denseclass label species.fa
|
|
143
|
+
|
|
144
|
+
# 2. split read→label pairs
|
|
145
|
+
denseclass split readid_species.txt --train_file train_labels.txt --test_file test_labels.txt
|
|
146
|
+
|
|
147
|
+
# 3. extract training chunks
|
|
148
|
+
denseclass gendata train.pod5 \
|
|
149
|
+
--label_path train_labels.txt --save_path ./ --seqlen 6000 --do-trim
|
|
150
|
+
|
|
151
|
+
# 4. train
|
|
152
|
+
denseclass train model_real \
|
|
153
|
+
-config dna.toml --directory ./ --label-map label_map.json \
|
|
154
|
+
--chunksize 6000 -f --new -y
|
|
155
|
+
|
|
156
|
+
# 5. predict
|
|
157
|
+
denseclass predict model_real test.pod5 inverse_label_map.json \
|
|
158
|
+
--chunksize 6000 > result_real.txt
|
|
159
|
+
|
|
160
|
+
# 6. evaluate
|
|
161
|
+
denseclass evaluation result_real.txt test_labels.txt \
|
|
162
|
+
-m label_map.json -o ./real
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Simulation-based training (no real per-species data)
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
denseclass label species.fa
|
|
169
|
+
|
|
170
|
+
denseclass train model_simu \
|
|
171
|
+
-config dna.toml --directory ./ --label-map label_map.json \
|
|
172
|
+
--chunksize 25000 -f --new --simu species.fa --preset dna-r10-min -y
|
|
173
|
+
|
|
174
|
+
denseclass predict model_simu reads_dir/ inverse_label_map.json \
|
|
175
|
+
--chunksize 25000 > result_simu.txt
|
|
176
|
+
|
|
177
|
+
denseclass precision result_simu.txt test_labels.txt -m label_map.json
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### RNA direct-read classification
|
|
181
|
+
|
|
182
|
+
RNA reads are reverse-complement mapped and sequenced after the poly(A) tail, so
|
|
183
|
+
`gendata` / `predict` take `--rna --tail`, and RNA models use `rna.toml`:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
denseclass gendata train.pod5 \
|
|
187
|
+
--label_path train_labels.txt --save_path ./ --rna --seqlen 50000 --tail
|
|
188
|
+
|
|
189
|
+
denseclass train model \
|
|
190
|
+
-config rna.toml --directory ./ --label-map label_map.json \
|
|
191
|
+
-f --new -y --chunksize 50000 --rna
|
|
192
|
+
|
|
193
|
+
denseclass predict model test.pod5 inverse_label_map.json \
|
|
194
|
+
--rna --tail --chunksize 50000 > result_real.txt
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 🔗 Relationship to densecall
|
|
200
|
+
|
|
201
|
+
This package was previously `densecall classify`, wrapping `densecall/microbiome/`.
|
|
202
|
+
It is now standalone:
|
|
203
|
+
|
|
204
|
+
- **No `densecall` import anywhere.** The one helper that used to be shared
|
|
205
|
+
(`convert_base_name`) is vendored in `denseclass/base_util.py`.
|
|
206
|
+
- **Separate dependency set and release cycle.** Install and upgrade it on its own.
|
|
207
|
+
- **The trained `model/` directory is a normal model directory** and can be used with
|
|
208
|
+
the DenseCall basecalling `train` / `predict` tooling where applicable.
|
|
209
|
+
|
|
210
|
+
Migration is a command rename:
|
|
211
|
+
|
|
212
|
+
| Before | After |
|
|
213
|
+
|:--|:--|
|
|
214
|
+
| `densecall classify label` | `denseclass label` |
|
|
215
|
+
| `densecall classify gendata` | `denseclass gendata` |
|
|
216
|
+
| `densecall classify train` | `denseclass train` |
|
|
217
|
+
| `densecall classify predict` | `denseclass predict` |
|
|
218
|
+
|
|
219
|
+
All flags are unchanged.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 🛠Development
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
pytest tests/ # structural checks: imports, CLI tree, packaging
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The test suite is purely structural — it verifies that every module imports, the
|
|
230
|
+
command tree builds and formats, and the packaging metadata is consistent. It needs
|
|
231
|
+
no signal data or checkpoints. End-to-end behaviour is exercised through the examples
|
|
232
|
+
above.
|
|
233
|
+
|
|
234
|
+
### Publishing
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# 1. bump __version__ in denseclass/cli.py
|
|
238
|
+
python -m build
|
|
239
|
+
twine check dist/* && twine upload dist/*
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 📄 License
|
|
245
|
+
|
|
246
|
+
MIT — see [LICENCE.txt](LICENCE.txt).
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🦠**denseclass**
|
|
4
|
+
|
|
5
|
+
**Nanopore signal classification** — label every raw read by species, barcode or taxon,
|
|
6
|
+
straight from the current, with no basecalling step.
|
|
7
|
+
|
|
8
|
+
[](https://www.python.org/)
|
|
9
|
+
[](LICENCE.txt)
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 📖 Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Overview](#-overview)
|
|
18
|
+
- [Installation](#-installation)
|
|
19
|
+
- [Quick start](#-quick-start)
|
|
20
|
+
- [Subcommands](#-subcommands)
|
|
21
|
+
- [Training modes](#-training-modes)
|
|
22
|
+
- [Examples](#-examples)
|
|
23
|
+
- [Relationship to densecall](#-relationship-to-densecall)
|
|
24
|
+
- [Development](#-development)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🧬 Overview
|
|
29
|
+
|
|
30
|
+
Reads are trimmed to their informative region (adapter/polyA detection for DNA,
|
|
31
|
+
poly(A)-tail trimming for RNA), encoded, and passed through a small 1-D CNN that
|
|
32
|
+
predicts one class per read. Training works either on **real** labelled reads or on
|
|
33
|
+
**signal simulated** from reference genomes, so you can build a classifier without
|
|
34
|
+
per-species reads for every class.
|
|
35
|
+
|
|
36
|
+
This package was split out of [DenseCall](https://github.com/n-damo/densecall) and
|
|
37
|
+
now stands alone — see [Relationship to densecall](#-relationship-to-densecall).
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📦 Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone <this-repo>
|
|
45
|
+
cd denseclass
|
|
46
|
+
pip install -e .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`torch` is deliberately unpinned so the resolver picks a build matching your local
|
|
50
|
+
CUDA toolkit. For GPU basecalling-adjacent work, install the CUDA toolkit first and
|
|
51
|
+
verify `nvcc` is on `$PATH`:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
conda install -c nvidia cuda-toolkit=12.8
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 🚀 Quick start
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
denseclass --help
|
|
63
|
+
denseclass label --help
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 🎯 Subcommands
|
|
69
|
+
|
|
70
|
+
| Subcommand | Purpose |
|
|
71
|
+
|:--|:--|
|
|
72
|
+
| `label` | Build `label_map.json` / `inverse_label_map.json` from a FASTA whose headers are the class names |
|
|
73
|
+
| `split` | Split a read→label pairs file into train / test sets |
|
|
74
|
+
| `gendata` | Convert POD5/FAST5 + labels into `train.hdf5` / `valid.hdf5` |
|
|
75
|
+
| `train` | Train the classification CNN (real or simulated data) |
|
|
76
|
+
| `predict` | Classify reads with a trained model |
|
|
77
|
+
| `precision` | One-class precision / recall / F1 / ROC evaluation |
|
|
78
|
+
| `evaluation` | Full multiclass panel (confusion matrix, PR/ROC, ...) |
|
|
79
|
+
| `comparison` | Side-by-side comparison of two models |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 🧪 Training modes
|
|
84
|
+
|
|
85
|
+
| Mode | Description |
|
|
86
|
+
|:--|:--|
|
|
87
|
+
| **Real-data** | `gendata` extracts signal chunks from reads already labelled by species, then `train` (no `--simu`) fits the CNN directly. |
|
|
88
|
+
| **Simulation** | `train --simu <refs.fa> --preset <name>` synthesises chunks on the fly from a FASTA of reference genomes. Presets: `dna-r9-min`, `dna-r10-min`, `rna-r9-min`, `rna004-min`, ... |
|
|
89
|
+
|
|
90
|
+
The CNN architecture is set by a small TOML (`dna.toml` with `strides=[2,2,2,2,2]` for
|
|
91
|
+
DNA, `rna.toml` with `strides=[5,2,2,2,2]` for RNA) passed via `-config`. The
|
|
92
|
+
`label_map.json` / `inverse_label_map.json` pair must be produced first with
|
|
93
|
+
`denseclass label`.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🧪 Examples
|
|
98
|
+
|
|
99
|
+
### Barcode / species classification on real reads
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# 1. build the label map
|
|
103
|
+
denseclass label species.fa
|
|
104
|
+
|
|
105
|
+
# 2. split read→label pairs
|
|
106
|
+
denseclass split readid_species.txt --train_file train_labels.txt --test_file test_labels.txt
|
|
107
|
+
|
|
108
|
+
# 3. extract training chunks
|
|
109
|
+
denseclass gendata train.pod5 \
|
|
110
|
+
--label_path train_labels.txt --save_path ./ --seqlen 6000 --do-trim
|
|
111
|
+
|
|
112
|
+
# 4. train
|
|
113
|
+
denseclass train model_real \
|
|
114
|
+
-config dna.toml --directory ./ --label-map label_map.json \
|
|
115
|
+
--chunksize 6000 -f --new -y
|
|
116
|
+
|
|
117
|
+
# 5. predict
|
|
118
|
+
denseclass predict model_real test.pod5 inverse_label_map.json \
|
|
119
|
+
--chunksize 6000 > result_real.txt
|
|
120
|
+
|
|
121
|
+
# 6. evaluate
|
|
122
|
+
denseclass evaluation result_real.txt test_labels.txt \
|
|
123
|
+
-m label_map.json -o ./real
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Simulation-based training (no real per-species data)
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
denseclass label species.fa
|
|
130
|
+
|
|
131
|
+
denseclass train model_simu \
|
|
132
|
+
-config dna.toml --directory ./ --label-map label_map.json \
|
|
133
|
+
--chunksize 25000 -f --new --simu species.fa --preset dna-r10-min -y
|
|
134
|
+
|
|
135
|
+
denseclass predict model_simu reads_dir/ inverse_label_map.json \
|
|
136
|
+
--chunksize 25000 > result_simu.txt
|
|
137
|
+
|
|
138
|
+
denseclass precision result_simu.txt test_labels.txt -m label_map.json
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### RNA direct-read classification
|
|
142
|
+
|
|
143
|
+
RNA reads are reverse-complement mapped and sequenced after the poly(A) tail, so
|
|
144
|
+
`gendata` / `predict` take `--rna --tail`, and RNA models use `rna.toml`:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
denseclass gendata train.pod5 \
|
|
148
|
+
--label_path train_labels.txt --save_path ./ --rna --seqlen 50000 --tail
|
|
149
|
+
|
|
150
|
+
denseclass train model \
|
|
151
|
+
-config rna.toml --directory ./ --label-map label_map.json \
|
|
152
|
+
-f --new -y --chunksize 50000 --rna
|
|
153
|
+
|
|
154
|
+
denseclass predict model test.pod5 inverse_label_map.json \
|
|
155
|
+
--rna --tail --chunksize 50000 > result_real.txt
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 🔗 Relationship to densecall
|
|
161
|
+
|
|
162
|
+
This package was previously `densecall classify`, wrapping `densecall/microbiome/`.
|
|
163
|
+
It is now standalone:
|
|
164
|
+
|
|
165
|
+
- **No `densecall` import anywhere.** The one helper that used to be shared
|
|
166
|
+
(`convert_base_name`) is vendored in `denseclass/base_util.py`.
|
|
167
|
+
- **Separate dependency set and release cycle.** Install and upgrade it on its own.
|
|
168
|
+
- **The trained `model/` directory is a normal model directory** and can be used with
|
|
169
|
+
the DenseCall basecalling `train` / `predict` tooling where applicable.
|
|
170
|
+
|
|
171
|
+
Migration is a command rename:
|
|
172
|
+
|
|
173
|
+
| Before | After |
|
|
174
|
+
|:--|:--|
|
|
175
|
+
| `densecall classify label` | `denseclass label` |
|
|
176
|
+
| `densecall classify gendata` | `denseclass gendata` |
|
|
177
|
+
| `densecall classify train` | `denseclass train` |
|
|
178
|
+
| `densecall classify predict` | `denseclass predict` |
|
|
179
|
+
|
|
180
|
+
All flags are unchanged.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 🛠Development
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
pytest tests/ # structural checks: imports, CLI tree, packaging
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The test suite is purely structural — it verifies that every module imports, the
|
|
191
|
+
command tree builds and formats, and the packaging metadata is consistent. It needs
|
|
192
|
+
no signal data or checkpoints. End-to-end behaviour is exercised through the examples
|
|
193
|
+
above.
|
|
194
|
+
|
|
195
|
+
### Publishing
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# 1. bump __version__ in denseclass/cli.py
|
|
199
|
+
python -m build
|
|
200
|
+
twine check dist/* && twine upload dist/*
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 📄 License
|
|
206
|
+
|
|
207
|
+
MIT — see [LICENCE.txt](LICENCE.txt).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Signal classification for nanopore reads.
|
|
2
|
+
|
|
3
|
+
Split out of the ``densecall`` basecaller into this standalone package. Nothing
|
|
4
|
+
here imports ``densecall``: the only helper that was shared
|
|
5
|
+
(``convert_base_name``) is vendored in ``base_util.py``.
|
|
6
|
+
|
|
7
|
+
Pipeline: label -> split -> gendata -> train -> predict -> evaluate
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from denseclass.cli import __version__, argparser, main, subcommands
|
|
11
|
+
|
|
12
|
+
__all__ = ['main', 'argparser', 'subcommands', '__version__']
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Base-name to IUPAC regular-expression helpers.
|
|
2
|
+
|
|
3
|
+
Vendored from ``densecall.mod_util`` so this package has no dependency on the
|
|
4
|
+
basecaller. Only ``convert_base_name`` was ever used here; the rest of
|
|
5
|
+
``mod_util`` pulls in remora/mappy, which this package does not need.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# IUPAC degenerate codes expanded into character classes.
|
|
9
|
+
_MERGE_BASES = {
|
|
10
|
+
"A": "A",
|
|
11
|
+
"C": "C",
|
|
12
|
+
"G": "G",
|
|
13
|
+
"T": "T",
|
|
14
|
+
"M": "[AC]",
|
|
15
|
+
"V": "[ACG]",
|
|
16
|
+
"R": "[AG]",
|
|
17
|
+
"H": "[ACT]",
|
|
18
|
+
"W": "[AT]",
|
|
19
|
+
"D": "[AGT]",
|
|
20
|
+
"S": "[CG]",
|
|
21
|
+
"B": "[CGT]",
|
|
22
|
+
"Y": "[CT]",
|
|
23
|
+
"N": "[ACGT]",
|
|
24
|
+
"K": "[GT]",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def convert_base_name(base_name):
|
|
29
|
+
"""Expand a base string into a regular expression pattern.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
base_name (str): Input base string to be converted, e.g. ``"NAY"``.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
str: Regular expression pattern equivalent to the input, e.g.
|
|
36
|
+
``"[ACGT]A[CT]"``.
|
|
37
|
+
"""
|
|
38
|
+
pattern = ""
|
|
39
|
+
for base in base_name:
|
|
40
|
+
pattern += _MERGE_BASES.get(base, base)
|
|
41
|
+
return pattern
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Command-line namespace for signal classification.
|
|
2
|
+
|
|
3
|
+
``argparser()`` builds the subcommand tree for introspection and library use;
|
|
4
|
+
``main()`` is the console entry point. Both share the same leaf registration,
|
|
5
|
+
and ``main`` mirrors DenseCall's two-level layout (leaves hang directly off the
|
|
6
|
+
top parser) so that ``denseclass <cmd> --help`` shows the leaf's help.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
|
|
10
|
+
|
|
11
|
+
__version__ = '0.1.0'
|
|
12
|
+
|
|
13
|
+
subcommands = [
|
|
14
|
+
'label', 'split', 'gendata', 'train', 'predict',
|
|
15
|
+
'precision', 'evaluation', 'comparison'
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
__all__ = ['argparser', 'main', 'subcommands', '__version__']
|
|
19
|
+
|
|
20
|
+
_DESCRIPTION = ('Signal classification: label map -> split -> gendata -> '
|
|
21
|
+
'train -> predict -> evaluate')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _leaves():
|
|
25
|
+
"""Yield (name, module) for each subcommand."""
|
|
26
|
+
for name in subcommands:
|
|
27
|
+
mod = __import__(f"denseclass.{name}", fromlist=['argparser', 'main'])
|
|
28
|
+
yield name, mod
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _add_subparsers(parser):
|
|
32
|
+
"""Attach every subcommand to ``parser`` as a real subparser."""
|
|
33
|
+
sub = parser.add_subparsers(
|
|
34
|
+
title='subcommands', description='valid commands',
|
|
35
|
+
help='additional help', dest='subcommand'
|
|
36
|
+
)
|
|
37
|
+
sub.required = True
|
|
38
|
+
for name, mod in _leaves():
|
|
39
|
+
p = sub.add_parser(name, parents=[mod.argparser()])
|
|
40
|
+
p.set_defaults(func=mod.main)
|
|
41
|
+
return sub
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def argparser():
|
|
45
|
+
"""The subcommand namespace on its own (``add_help=False``, since each leaf
|
|
46
|
+
carries its own ``-h``)."""
|
|
47
|
+
parser = ArgumentParser(
|
|
48
|
+
"denseclass",
|
|
49
|
+
formatter_class=ArgumentDefaultsHelpFormatter,
|
|
50
|
+
description=_DESCRIPTION,
|
|
51
|
+
add_help=False,
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
'-v', '--version', action='version',
|
|
55
|
+
version='%(prog)s {}'.format(__version__)
|
|
56
|
+
)
|
|
57
|
+
_add_subparsers(parser)
|
|
58
|
+
return parser
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main(args=None):
|
|
62
|
+
"""Console entry point: leaves hang off the top parser so
|
|
63
|
+
``denseclass <cmd> --help`` reaches the leaf."""
|
|
64
|
+
parser = ArgumentParser(
|
|
65
|
+
'denseclass',
|
|
66
|
+
formatter_class=ArgumentDefaultsHelpFormatter,
|
|
67
|
+
description=_DESCRIPTION,
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
'-v', '--version', action='version',
|
|
71
|
+
version='%(prog)s {}'.format(__version__)
|
|
72
|
+
)
|
|
73
|
+
_add_subparsers(parser)
|
|
74
|
+
|
|
75
|
+
parsed = parser.parse_args(args)
|
|
76
|
+
parsed.func(parsed)
|