amrpredict 0.3.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.
- amrpredict-0.3.0/.gitignore +8 -0
- amrpredict-0.3.0/LICENSE +21 -0
- amrpredict-0.3.0/PKG-INFO +304 -0
- amrpredict-0.3.0/README.md +253 -0
- amrpredict-0.3.0/docs/api-reference.md +287 -0
- amrpredict-0.3.0/docs/cli.md +182 -0
- amrpredict-0.3.0/docs/known-issues.md +139 -0
- amrpredict-0.3.0/docs/quickstart.md +135 -0
- amrpredict-0.3.0/pyproject.toml +58 -0
- amrpredict-0.3.0/src/amrpredict/__init__.py +259 -0
- amrpredict-0.3.0/src/amrpredict/_paths.py +11 -0
- amrpredict-0.3.0/src/amrpredict/amr_constants.py +202 -0
- amrpredict-0.3.0/src/amrpredict/cli.py +125 -0
- amrpredict-0.3.0/src/amrpredict/genome.py +496 -0
- amrpredict-0.3.0/src/amrpredict/lgbm.py +561 -0
- amrpredict-0.3.0/src/amrpredict/models/ab_rate_full.joblib +0 -0
- amrpredict-0.3.0/src/amrpredict/models/amr_lgbm_final_model.txt +10634 -0
- amrpredict-0.3.0/src/amrpredict/models/genome/feature_meta.json +536 -0
- amrpredict-0.3.0/src/amrpredict/models/genome/genome_metrics.json +52 -0
- amrpredict-0.3.0/src/amrpredict/models/genome/model.txt +10704 -0
- amrpredict-0.3.0/src/amrpredict/models/genome_genes/feature_meta.json +811 -0
- amrpredict-0.3.0/src/amrpredict/models/genome_genes/genome_metrics.json +52 -0
- amrpredict-0.3.0/src/amrpredict/models/genome_genes/model.txt +10961 -0
- amrpredict-0.3.0/src/amrpredict/models/genome_genes/species_profiles.npz +0 -0
- amrpredict-0.3.0/src/amrpredict/models/genus_ab_rate_full.joblib +0 -0
- amrpredict-0.3.0/src/amrpredict/models/lgbm_meta.joblib +0 -0
- amrpredict-0.3.0/src/amrpredict/models/lgbm_metrics.json +97 -0
- amrpredict-0.3.0/src/amrpredict/models/rl_agent/policy.npz +0 -0
- amrpredict-0.3.0/src/amrpredict/models/rl_agent/rl_agent.json +22 -0
- amrpredict-0.3.0/src/amrpredict/models/taxon_ab_rate_full.joblib +0 -0
- amrpredict-0.3.0/src/amrpredict/models/taxon_species.csv +13059 -0
- amrpredict-0.3.0/src/amrpredict/models/timeline_calibration.json +21 -0
- amrpredict-0.3.0/src/amrpredict/models/timeline_ml/model.txt +1683 -0
- amrpredict-0.3.0/src/amrpredict/models/timeline_ml/timeline_metrics.json +145 -0
- amrpredict-0.3.0/src/amrpredict/models/timeline_ml/timeline_ml.json +1 -0
- amrpredict-0.3.0/src/amrpredict/registry.py +67 -0
- amrpredict-0.3.0/src/amrpredict/rl_agent.py +177 -0
- amrpredict-0.3.0/src/amrpredict/timeline.py +275 -0
- amrpredict-0.3.0/src/amrpredict/timeline_ml.py +174 -0
- amrpredict-0.3.0/tests/test_api.py +116 -0
- amrpredict-0.3.0/tests/test_fasta.py +159 -0
- amrpredict-0.3.0/tests/test_models.py +123 -0
amrpredict-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hamza
|
|
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,304 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: amrpredict
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Antimicrobial resistance prediction from bacterial genomes and MIC metadata
|
|
5
|
+
Project-URL: Homepage, https://amrpredict.live
|
|
6
|
+
Project-URL: Source, https://github.com/hhammza/FYP1
|
|
7
|
+
Project-URL: Issues, https://github.com/hhammza/FYP1/issues
|
|
8
|
+
Author: Hamza
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Hamza
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: amr,antimicrobial-resistance,bioinformatics,genomics,lightgbm,machine-learning
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Intended Audience :: Science/Research
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
41
|
+
Requires-Python: >=3.9
|
|
42
|
+
Requires-Dist: joblib>=1.3
|
|
43
|
+
Requires-Dist: lightgbm>=4.0
|
|
44
|
+
Requires-Dist: numpy>=1.24
|
|
45
|
+
Requires-Dist: pandas>=2.0
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: twine>=4.0; extra == 'dev'
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# amrpredict
|
|
53
|
+
|
|
54
|
+
Antimicrobial resistance prediction from bacterial genomes and MIC metadata.
|
|
55
|
+
|
|
56
|
+
The models of the AMR Intelligence Platform (a final-year project) behind one
|
|
57
|
+
flat Python API and a command-line tool: the same models, and the same answers,
|
|
58
|
+
as the project's web app. The trained artifacts ship inside the package, so
|
|
59
|
+
there is nothing to download and it works offline.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import amrpredict
|
|
63
|
+
|
|
64
|
+
amrpredict.forecast('ciprofloxacin', taxon_id=562, mic_value=4)
|
|
65
|
+
# {'prediction': 'Resistant', 'probability': 0.7055, 'confidence': 70.6,
|
|
66
|
+
# 'drug_class': 'fluoroquinolone', 'model_run': 'D3_forecaster_deploy_v7', ...}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install amrpredict
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Requires Python 3.9+. Dependencies: LightGBM, pandas, NumPy, joblib. The
|
|
76
|
+
download is about 6 MB because the model artifacts are bundled.
|
|
77
|
+
|
|
78
|
+
For the gene model (the most accurate genome model), also install NCBI
|
|
79
|
+
[AMRFinderPlus](https://github.com/ncbi/amr) so the `amrfinder` program is on
|
|
80
|
+
your PATH (e.g. `conda install -c bioconda ncbi-amrfinderplus`). Without it,
|
|
81
|
+
`predict_fasta()` uses the k-mer model.
|
|
82
|
+
|
|
83
|
+
From a checkout:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install -e ".[dev]"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## The models
|
|
90
|
+
|
|
91
|
+
| Function | Model | Input | Measured on data it never saw |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| `forecast()` | LightGBM, calibrated | Antibiotic + organism + MIC | AUC 0.774 (lab-confirmed rows 0.908) |
|
|
94
|
+
| `predict_fasta()` | Gene model: LightGBM on AMRFinderPlus genes and mutations | Complete genome FASTA | Lab AUC 0.977 [0.975–0.979] |
|
|
95
|
+
| `predict_fasta()` without AMRFinderPlus | k-mer model: LightGBM on 4-mer frequencies | Complete genome FASTA | Lab AUC 0.935 [0.931–0.939] |
|
|
96
|
+
| `forecast_trend()` | Timeline ML model: LightGBM on yearly resistant shares | Genus + antibiotic | 13.98 percentage points mean error on 2017–2022 |
|
|
97
|
+
| `plan_treatment()` | RL agent (PPO), trained on the simulation | One of 3 antibiotics | Beats every fixed schedule in the simulation |
|
|
98
|
+
| `simulate_timeline()` | Biological simulation | Genome FASTA + antibiotic | **Not a trained model** |
|
|
99
|
+
|
|
100
|
+
Every trained model was tested on genomes (or years) it never saw in training.
|
|
101
|
+
`simulate_timeline()` is a deterministic simulation, not a learned model: it
|
|
102
|
+
has no accuracy score, and its output is illustrative rather than predictive.
|
|
103
|
+
`status()` reports `trained: False` for it, deliberately.
|
|
104
|
+
|
|
105
|
+
## Usage
|
|
106
|
+
|
|
107
|
+
### Resistance from metadata and an MIC
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
r = amrpredict.forecast(
|
|
111
|
+
'ciprofloxacin',
|
|
112
|
+
taxon_id=562, # NCBI taxonomy ID — 562 is E. coli
|
|
113
|
+
mic_value=4, # mg/L
|
|
114
|
+
mic_sign='=', # '=', '>', '<=' ...
|
|
115
|
+
genus='Escherichia',
|
|
116
|
+
species='coli',
|
|
117
|
+
# threshold=None: the model's own (0.15, chosen to keep very major
|
|
118
|
+
# errors <= 10% on validation genomes); pass a number to override
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
print(r['prediction']) # 'Resistant'
|
|
122
|
+
print(r['probability']) # calibrated
|
|
123
|
+
print(r['evidence']['label']) # which inputs counted
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
On 1,569,432 test rows from genomes it never saw: AUC 0.774 [0.772–0.775],
|
|
127
|
+
recall 91.4%, very major error 8.6%, major error 60.8% at that threshold
|
|
128
|
+
(`status()['lgbm_forecasting']['metrics']` has the full set). Every argument
|
|
129
|
+
except `antibiotic` is optional; with fewer inputs the model falls back to
|
|
130
|
+
population-level resistance rates.
|
|
131
|
+
|
|
132
|
+
### Resistance from a complete genome
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
with open('genome.fna') as fh:
|
|
136
|
+
fasta = fh.read()
|
|
137
|
+
|
|
138
|
+
r = amrpredict.predict_fasta(fasta, 'ciprofloxacin')
|
|
139
|
+
|
|
140
|
+
print(r['prediction'], r['probability'])
|
|
141
|
+
print(r['model_used']) # 'LightGBM on AMRFinderPlus resistance genes (G_genes_deploy)'
|
|
142
|
+
print(r['genes_found'][:3]) # gene model: the resistance genes and mutations found
|
|
143
|
+
print(r['species_detected']) # gene model: the species, from the genome
|
|
144
|
+
print(r.get('top_kmers')) # k-mer model: the most frequent 4-mers
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- Upload a **complete assembly** (at least 100,000 bp); every contig is read.
|
|
148
|
+
- `use_genes=None` (default) picks the gene model when AMRFinderPlus is
|
|
149
|
+
installed, else the k-mer model; `use_genes=False` always uses the k-mer
|
|
150
|
+
model; `use_genes=True` requires the gene model. If AMRFinderPlus fails on
|
|
151
|
+
one genome, the k-mer model answers and the result has a `warning`.
|
|
152
|
+
- `threshold=None` uses each model's own threshold, chosen on validation
|
|
153
|
+
genomes for very major error near 10%.
|
|
154
|
+
- Bad input (too short, empty) returns a dict with `error`; it never raises.
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
amrpredict.identify_species(fasta)
|
|
158
|
+
# {'species': 'Pseudomonas aeruginosa', 'genus': 'Pseudomonas', 'distance': 0.00028}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Yearly resistance trend and its forecast
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
r = amrpredict.forecast_trend('Salmonella', 'ciprofloxacin', years_ahead=5)
|
|
165
|
+
|
|
166
|
+
for h in r['history']: # observed: BV-BRC laboratory results per year
|
|
167
|
+
print(h['year'], h['resistant_pct'], h['n'])
|
|
168
|
+
for f in r['forecast']: # the model's forecast with an 80% band
|
|
169
|
+
print(f['year'], f['resistant_pct'], f['lo'], f['hi'])
|
|
170
|
+
print(r['metrics']) # its held-out error against simple baselines
|
|
171
|
+
|
|
172
|
+
amrpredict.trend_series() # {genus: [antibiotics]} with enough history
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Trained on 553,223 dated laboratory results (2000–2022; a year counts with at
|
|
176
|
+
least 20 results), it predicts each year from the years before it and was
|
|
177
|
+
tested on 2017–2022: mean absolute error 13.98 percentage points, against
|
|
178
|
+
14.33 for "same as last year", 16.75 for a linear trend and 17.25 for the
|
|
179
|
+
average. The forecast is made one year at a time; the band widens with each.
|
|
180
|
+
|
|
181
|
+
### Which drug to give each week
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
r = amrpredict.plan_treatment('ciprofloxacin', n_weeks=52)
|
|
185
|
+
|
|
186
|
+
print(r['best']) # the policy that keeps drugs working longest
|
|
187
|
+
for p in r['policies']: # the RL agent, then fixed schedules
|
|
188
|
+
print(p['label'], p['failure_week'], p['total_reward'])
|
|
189
|
+
print(r['policies'][0]['policy'][:8]) # the agent's drug for weeks 1-8
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
A PPO agent chooses ciprofloxacin, gentamicin or imipenem each week in the
|
|
193
|
+
project's cycling simulation; the policies are compared on the same simulated
|
|
194
|
+
episode. It learned from simulated episodes only, not patient data: use it to
|
|
195
|
+
explore cycling strategies, not to treat patients.
|
|
196
|
+
|
|
197
|
+
### Week-by-week simulation (not a trained model)
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
r = amrpredict.simulate_timeline(fasta, 'ciprofloxacin', n_weeks=8)
|
|
201
|
+
|
|
202
|
+
for week in r['timeline']:
|
|
203
|
+
print(week['week'], week['resistant_fraction'], week['mic_fold_change'])
|
|
204
|
+
print(r['failure_week'])
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`n_weeks=8` returns **9** entries — week 0 is the untreated baseline. Despite
|
|
208
|
+
the name, `resistant_fraction` and its siblings are **percentages (0–100)**.
|
|
209
|
+
See [Known issues](docs/known-issues.md).
|
|
210
|
+
|
|
211
|
+
### Inspect what loaded
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
amrpredict.status()
|
|
215
|
+
# {'version': '0.3.0',
|
|
216
|
+
# 'lgbm_forecasting': {'trained': True, 'run_id': 'D3_forecaster_deploy_v7', 'metrics': {...}},
|
|
217
|
+
# 'genome_resistance': {'trained': True, 'run_id': 'G_kmer_deploy', 'searches_genes': False, ...},
|
|
218
|
+
# 'trend_forecast': {'trained': True, 'run_id': 'TL_timeline_ml', ...},
|
|
219
|
+
# 'rl_agent': {'trained': True, 'drugs': ['ciprofloxacin', 'gentamicin', 'imipenem'], ...},
|
|
220
|
+
# 'mutation_timeline': {'trained': False, ...}}
|
|
221
|
+
|
|
222
|
+
amrpredict.antibiotics() # the genome models' 111 antibiotics
|
|
223
|
+
amrpredict.antibiotics('forecast') # also 'trend' and 'rl'
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Command line
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
amrpredict forecast ciprofloxacin --taxon-id 562 --mic-value 4
|
|
230
|
+
amrpredict predict genome.fna ciprofloxacin # --kmers or --genes to choose
|
|
231
|
+
amrpredict species genome.fna
|
|
232
|
+
amrpredict trend Salmonella ciprofloxacin --years 5
|
|
233
|
+
amrpredict plan ciprofloxacin --weeks 52
|
|
234
|
+
amrpredict timeline genome.fna ciprofloxacin --weeks 12
|
|
235
|
+
amrpredict antibiotics --model trend
|
|
236
|
+
amrpredict status
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Output is JSON on stdout, so it composes. Unusable input (a short FASTA, a
|
|
240
|
+
drug the RL agent does not know) exits 0 with an `error` key, as the Python
|
|
241
|
+
API returns it:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
amrpredict --compact forecast meropenem --taxon-id 287 | jq -r .prediction
|
|
245
|
+
cat genome.fna | amrpredict predict - ciprofloxacin
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Diagnostics go through `logging` to stderr, never stdout. To see them:
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
import logging
|
|
252
|
+
logging.basicConfig(level=logging.INFO)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Using your own models
|
|
256
|
+
|
|
257
|
+
Every entry point takes `model_dir`, a folder laid out like the bundled one
|
|
258
|
+
(`amrpredict.default_model_dir()`): the forecaster's files at the top
|
|
259
|
+
(`amr_lgbm_final_model.txt`, the `*.joblib` rate tables, `lgbm_metrics.json`),
|
|
260
|
+
and the folders `genome/`, `genome_genes/`, `timeline_ml/` and `rl_agent/`.
|
|
261
|
+
They are produced by the main project's `experiments/promote.py`.
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
amrpredict.forecast('ciprofloxacin', taxon_id=562, model_dir='/path/to/models')
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Performance notes
|
|
268
|
+
|
|
269
|
+
Models load lazily on first use and are cached process-wide, so the first call
|
|
270
|
+
pays about 1–2 s and later calls are fast. A complete genome takes about a
|
|
271
|
+
second with the k-mer model; the gene model adds AMRFinderPlus's 10–45 s.
|
|
272
|
+
To release the memory: `amrpredict.registry.reset()`.
|
|
273
|
+
|
|
274
|
+
## Changes in 0.3.0
|
|
275
|
+
|
|
276
|
+
- `predict_fasta()` uses the web app's genome models: the gene model (lab AUC
|
|
277
|
+
0.977) with AMRFinderPlus, else the k-mer LightGBM (0.935). The July k-mer
|
|
278
|
+
RandomForest (0.695 on genomes it never saw) is removed, and with it the
|
|
279
|
+
scikit-learn dependency. `predict_fasta()` now needs a complete genome
|
|
280
|
+
(100,000 bp or more) and reads all of it.
|
|
281
|
+
- New: `forecast_trend()` and `trend_series()` (timeline ML model),
|
|
282
|
+
`plan_treatment()` (RL agent), `identify_species()`; CLI commands `trend`,
|
|
283
|
+
`plan` and `species`.
|
|
284
|
+
- `status()` reports `genome_resistance`, `trend_forecast` and `rl_agent`
|
|
285
|
+
(`kmer_resistance` is gone); `antibiotics()` takes the model to ask.
|
|
286
|
+
|
|
287
|
+
## Development
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
pip install -e ".[dev]"
|
|
291
|
+
pytest
|
|
292
|
+
python -m build && twine check dist/*
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Documentation
|
|
296
|
+
|
|
297
|
+
- [Quickstart](docs/quickstart.md)
|
|
298
|
+
- [API reference](docs/api-reference.md)
|
|
299
|
+
- [CLI reference](docs/cli.md)
|
|
300
|
+
- [Known issues](docs/known-issues.md)
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# amrpredict
|
|
2
|
+
|
|
3
|
+
Antimicrobial resistance prediction from bacterial genomes and MIC metadata.
|
|
4
|
+
|
|
5
|
+
The models of the AMR Intelligence Platform (a final-year project) behind one
|
|
6
|
+
flat Python API and a command-line tool: the same models, and the same answers,
|
|
7
|
+
as the project's web app. The trained artifacts ship inside the package, so
|
|
8
|
+
there is nothing to download and it works offline.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
import amrpredict
|
|
12
|
+
|
|
13
|
+
amrpredict.forecast('ciprofloxacin', taxon_id=562, mic_value=4)
|
|
14
|
+
# {'prediction': 'Resistant', 'probability': 0.7055, 'confidence': 70.6,
|
|
15
|
+
# 'drug_class': 'fluoroquinolone', 'model_run': 'D3_forecaster_deploy_v7', ...}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install amrpredict
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires Python 3.9+. Dependencies: LightGBM, pandas, NumPy, joblib. The
|
|
25
|
+
download is about 6 MB because the model artifacts are bundled.
|
|
26
|
+
|
|
27
|
+
For the gene model (the most accurate genome model), also install NCBI
|
|
28
|
+
[AMRFinderPlus](https://github.com/ncbi/amr) so the `amrfinder` program is on
|
|
29
|
+
your PATH (e.g. `conda install -c bioconda ncbi-amrfinderplus`). Without it,
|
|
30
|
+
`predict_fasta()` uses the k-mer model.
|
|
31
|
+
|
|
32
|
+
From a checkout:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install -e ".[dev]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## The models
|
|
39
|
+
|
|
40
|
+
| Function | Model | Input | Measured on data it never saw |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| `forecast()` | LightGBM, calibrated | Antibiotic + organism + MIC | AUC 0.774 (lab-confirmed rows 0.908) |
|
|
43
|
+
| `predict_fasta()` | Gene model: LightGBM on AMRFinderPlus genes and mutations | Complete genome FASTA | Lab AUC 0.977 [0.975–0.979] |
|
|
44
|
+
| `predict_fasta()` without AMRFinderPlus | k-mer model: LightGBM on 4-mer frequencies | Complete genome FASTA | Lab AUC 0.935 [0.931–0.939] |
|
|
45
|
+
| `forecast_trend()` | Timeline ML model: LightGBM on yearly resistant shares | Genus + antibiotic | 13.98 percentage points mean error on 2017–2022 |
|
|
46
|
+
| `plan_treatment()` | RL agent (PPO), trained on the simulation | One of 3 antibiotics | Beats every fixed schedule in the simulation |
|
|
47
|
+
| `simulate_timeline()` | Biological simulation | Genome FASTA + antibiotic | **Not a trained model** |
|
|
48
|
+
|
|
49
|
+
Every trained model was tested on genomes (or years) it never saw in training.
|
|
50
|
+
`simulate_timeline()` is a deterministic simulation, not a learned model: it
|
|
51
|
+
has no accuracy score, and its output is illustrative rather than predictive.
|
|
52
|
+
`status()` reports `trained: False` for it, deliberately.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Resistance from metadata and an MIC
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
r = amrpredict.forecast(
|
|
60
|
+
'ciprofloxacin',
|
|
61
|
+
taxon_id=562, # NCBI taxonomy ID — 562 is E. coli
|
|
62
|
+
mic_value=4, # mg/L
|
|
63
|
+
mic_sign='=', # '=', '>', '<=' ...
|
|
64
|
+
genus='Escherichia',
|
|
65
|
+
species='coli',
|
|
66
|
+
# threshold=None: the model's own (0.15, chosen to keep very major
|
|
67
|
+
# errors <= 10% on validation genomes); pass a number to override
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
print(r['prediction']) # 'Resistant'
|
|
71
|
+
print(r['probability']) # calibrated
|
|
72
|
+
print(r['evidence']['label']) # which inputs counted
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
On 1,569,432 test rows from genomes it never saw: AUC 0.774 [0.772–0.775],
|
|
76
|
+
recall 91.4%, very major error 8.6%, major error 60.8% at that threshold
|
|
77
|
+
(`status()['lgbm_forecasting']['metrics']` has the full set). Every argument
|
|
78
|
+
except `antibiotic` is optional; with fewer inputs the model falls back to
|
|
79
|
+
population-level resistance rates.
|
|
80
|
+
|
|
81
|
+
### Resistance from a complete genome
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
with open('genome.fna') as fh:
|
|
85
|
+
fasta = fh.read()
|
|
86
|
+
|
|
87
|
+
r = amrpredict.predict_fasta(fasta, 'ciprofloxacin')
|
|
88
|
+
|
|
89
|
+
print(r['prediction'], r['probability'])
|
|
90
|
+
print(r['model_used']) # 'LightGBM on AMRFinderPlus resistance genes (G_genes_deploy)'
|
|
91
|
+
print(r['genes_found'][:3]) # gene model: the resistance genes and mutations found
|
|
92
|
+
print(r['species_detected']) # gene model: the species, from the genome
|
|
93
|
+
print(r.get('top_kmers')) # k-mer model: the most frequent 4-mers
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Upload a **complete assembly** (at least 100,000 bp); every contig is read.
|
|
97
|
+
- `use_genes=None` (default) picks the gene model when AMRFinderPlus is
|
|
98
|
+
installed, else the k-mer model; `use_genes=False` always uses the k-mer
|
|
99
|
+
model; `use_genes=True` requires the gene model. If AMRFinderPlus fails on
|
|
100
|
+
one genome, the k-mer model answers and the result has a `warning`.
|
|
101
|
+
- `threshold=None` uses each model's own threshold, chosen on validation
|
|
102
|
+
genomes for very major error near 10%.
|
|
103
|
+
- Bad input (too short, empty) returns a dict with `error`; it never raises.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
amrpredict.identify_species(fasta)
|
|
107
|
+
# {'species': 'Pseudomonas aeruginosa', 'genus': 'Pseudomonas', 'distance': 0.00028}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Yearly resistance trend and its forecast
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
r = amrpredict.forecast_trend('Salmonella', 'ciprofloxacin', years_ahead=5)
|
|
114
|
+
|
|
115
|
+
for h in r['history']: # observed: BV-BRC laboratory results per year
|
|
116
|
+
print(h['year'], h['resistant_pct'], h['n'])
|
|
117
|
+
for f in r['forecast']: # the model's forecast with an 80% band
|
|
118
|
+
print(f['year'], f['resistant_pct'], f['lo'], f['hi'])
|
|
119
|
+
print(r['metrics']) # its held-out error against simple baselines
|
|
120
|
+
|
|
121
|
+
amrpredict.trend_series() # {genus: [antibiotics]} with enough history
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Trained on 553,223 dated laboratory results (2000–2022; a year counts with at
|
|
125
|
+
least 20 results), it predicts each year from the years before it and was
|
|
126
|
+
tested on 2017–2022: mean absolute error 13.98 percentage points, against
|
|
127
|
+
14.33 for "same as last year", 16.75 for a linear trend and 17.25 for the
|
|
128
|
+
average. The forecast is made one year at a time; the band widens with each.
|
|
129
|
+
|
|
130
|
+
### Which drug to give each week
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
r = amrpredict.plan_treatment('ciprofloxacin', n_weeks=52)
|
|
134
|
+
|
|
135
|
+
print(r['best']) # the policy that keeps drugs working longest
|
|
136
|
+
for p in r['policies']: # the RL agent, then fixed schedules
|
|
137
|
+
print(p['label'], p['failure_week'], p['total_reward'])
|
|
138
|
+
print(r['policies'][0]['policy'][:8]) # the agent's drug for weeks 1-8
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
A PPO agent chooses ciprofloxacin, gentamicin or imipenem each week in the
|
|
142
|
+
project's cycling simulation; the policies are compared on the same simulated
|
|
143
|
+
episode. It learned from simulated episodes only, not patient data: use it to
|
|
144
|
+
explore cycling strategies, not to treat patients.
|
|
145
|
+
|
|
146
|
+
### Week-by-week simulation (not a trained model)
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
r = amrpredict.simulate_timeline(fasta, 'ciprofloxacin', n_weeks=8)
|
|
150
|
+
|
|
151
|
+
for week in r['timeline']:
|
|
152
|
+
print(week['week'], week['resistant_fraction'], week['mic_fold_change'])
|
|
153
|
+
print(r['failure_week'])
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`n_weeks=8` returns **9** entries — week 0 is the untreated baseline. Despite
|
|
157
|
+
the name, `resistant_fraction` and its siblings are **percentages (0–100)**.
|
|
158
|
+
See [Known issues](docs/known-issues.md).
|
|
159
|
+
|
|
160
|
+
### Inspect what loaded
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
amrpredict.status()
|
|
164
|
+
# {'version': '0.3.0',
|
|
165
|
+
# 'lgbm_forecasting': {'trained': True, 'run_id': 'D3_forecaster_deploy_v7', 'metrics': {...}},
|
|
166
|
+
# 'genome_resistance': {'trained': True, 'run_id': 'G_kmer_deploy', 'searches_genes': False, ...},
|
|
167
|
+
# 'trend_forecast': {'trained': True, 'run_id': 'TL_timeline_ml', ...},
|
|
168
|
+
# 'rl_agent': {'trained': True, 'drugs': ['ciprofloxacin', 'gentamicin', 'imipenem'], ...},
|
|
169
|
+
# 'mutation_timeline': {'trained': False, ...}}
|
|
170
|
+
|
|
171
|
+
amrpredict.antibiotics() # the genome models' 111 antibiotics
|
|
172
|
+
amrpredict.antibiotics('forecast') # also 'trend' and 'rl'
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Command line
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
amrpredict forecast ciprofloxacin --taxon-id 562 --mic-value 4
|
|
179
|
+
amrpredict predict genome.fna ciprofloxacin # --kmers or --genes to choose
|
|
180
|
+
amrpredict species genome.fna
|
|
181
|
+
amrpredict trend Salmonella ciprofloxacin --years 5
|
|
182
|
+
amrpredict plan ciprofloxacin --weeks 52
|
|
183
|
+
amrpredict timeline genome.fna ciprofloxacin --weeks 12
|
|
184
|
+
amrpredict antibiotics --model trend
|
|
185
|
+
amrpredict status
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Output is JSON on stdout, so it composes. Unusable input (a short FASTA, a
|
|
189
|
+
drug the RL agent does not know) exits 0 with an `error` key, as the Python
|
|
190
|
+
API returns it:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
amrpredict --compact forecast meropenem --taxon-id 287 | jq -r .prediction
|
|
194
|
+
cat genome.fna | amrpredict predict - ciprofloxacin
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Diagnostics go through `logging` to stderr, never stdout. To see them:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import logging
|
|
201
|
+
logging.basicConfig(level=logging.INFO)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Using your own models
|
|
205
|
+
|
|
206
|
+
Every entry point takes `model_dir`, a folder laid out like the bundled one
|
|
207
|
+
(`amrpredict.default_model_dir()`): the forecaster's files at the top
|
|
208
|
+
(`amr_lgbm_final_model.txt`, the `*.joblib` rate tables, `lgbm_metrics.json`),
|
|
209
|
+
and the folders `genome/`, `genome_genes/`, `timeline_ml/` and `rl_agent/`.
|
|
210
|
+
They are produced by the main project's `experiments/promote.py`.
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
amrpredict.forecast('ciprofloxacin', taxon_id=562, model_dir='/path/to/models')
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Performance notes
|
|
217
|
+
|
|
218
|
+
Models load lazily on first use and are cached process-wide, so the first call
|
|
219
|
+
pays about 1–2 s and later calls are fast. A complete genome takes about a
|
|
220
|
+
second with the k-mer model; the gene model adds AMRFinderPlus's 10–45 s.
|
|
221
|
+
To release the memory: `amrpredict.registry.reset()`.
|
|
222
|
+
|
|
223
|
+
## Changes in 0.3.0
|
|
224
|
+
|
|
225
|
+
- `predict_fasta()` uses the web app's genome models: the gene model (lab AUC
|
|
226
|
+
0.977) with AMRFinderPlus, else the k-mer LightGBM (0.935). The July k-mer
|
|
227
|
+
RandomForest (0.695 on genomes it never saw) is removed, and with it the
|
|
228
|
+
scikit-learn dependency. `predict_fasta()` now needs a complete genome
|
|
229
|
+
(100,000 bp or more) and reads all of it.
|
|
230
|
+
- New: `forecast_trend()` and `trend_series()` (timeline ML model),
|
|
231
|
+
`plan_treatment()` (RL agent), `identify_species()`; CLI commands `trend`,
|
|
232
|
+
`plan` and `species`.
|
|
233
|
+
- `status()` reports `genome_resistance`, `trend_forecast` and `rl_agent`
|
|
234
|
+
(`kmer_resistance` is gone); `antibiotics()` takes the model to ask.
|
|
235
|
+
|
|
236
|
+
## Development
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
pip install -e ".[dev]"
|
|
240
|
+
pytest
|
|
241
|
+
python -m build && twine check dist/*
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Documentation
|
|
245
|
+
|
|
246
|
+
- [Quickstart](docs/quickstart.md)
|
|
247
|
+
- [API reference](docs/api-reference.md)
|
|
248
|
+
- [CLI reference](docs/cli.md)
|
|
249
|
+
- [Known issues](docs/known-issues.md)
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
MIT — see [LICENSE](LICENSE).
|