A fast taxonomic classifier for COI DNA barcodes. Paste a sequence, get a taxonomy with calibrated confidence at every rank, in milliseconds, on a laptop CPU.
| Main flow | Auto-testing | Vector space |
|---|---|---|
![]() |
![]() |
![]() |
| COI sequence classification | Confidence estimation | 3D exploration + k-mer fingerprints |
COI (Cytochrome c Oxidase I) is the standard barcode marker for animals. The usual ways to name a COI read all force a bad trade. BLAST is accurate but slow, and needs a database in the hundreds of gigabytes. Deep-learning classifiers want a GPU and constant retraining. I wanted something that runs on any machine, answers in real time, and stays honest about how sure it is.
taXR is that middle path: k-mer vectors, a compressed FAISS index over MIDORI2, a SINTAX-style bootstrap confidence per rank, and an automatic NCBI BLAST fallback for the hard reads. I built it with Team 8 for the Investigathon 2025 bioinformatics track.
- Classifies one COI sequence or a batch (FASTA or GenBank)
- Reports confidence per rank and marks the most specific rank it trusts
- Falls back to NCBI BLAST for low-confidence reads, then learns from the result
- Flags quality problems (odd length, ambiguous bases, GC out of range)
- Runs on CPU only, around 1 GB of RAM, no GPU
- Visualizes results as a 2D/3D phylogenetic tree, a meta-analysis dashboard, and a 3D vector-space view of the index
flowchart LR
S[COI sequence] --> K[k-mer vectorization<br/>k=6, 4096 dims]
K --> F[FAISS IVF-PQ search<br/>k=50 neighbors]
F --> B[Bootstrap confidence<br/>100 resamples]
B --> H[Per-rank hierarchical confidence]
H -->|confidence >= 50%| R[Taxonomy result]
H -->|confidence < 50%| N[NCBI BLAST fallback]
N --> I[Incremental index]
N --> R
Every sequence turns into a 4096-dimensional vector, one slot per possible 6-mer (4^6). Each slot counts how often that 6-mer shows up, then the vector is L1-normalized so a long sequence does not outweigh a short one. The k-mer to index step is JIT-compiled with Numba for speed.
# "ATGCGA" -> base-4 index. A=0, C=1, G=2, T=3
# ATGCGA = 0*4^5 + 3*4^4 + 2*4^3 + 1*4^2 + 2*4^1 + 0 = 3406
vector = np.zeros(4096)
for kmer in sequence:
idx = kmer_to_index(kmer)
vector[idx] += 1
vector /= vector.sum() # L1 normalizationThe reference index is a FAISS IVF-PQ (Inverted File with Product Quantization) over about 1.78M COI sequences from MIDORI2 (GB268). IVF carves the space into ~1000 clusters, so a query only searches the nearest few. PQ squeezes each 4096-dim vector to 64 bytes (256x) by quantizing sub-vectors against learned codebooks, dropping the index to ~80MB against ~100GB for a BLAST nt database.
nlist=1000, m=64, nbits=8 -> 64 bytes/vector
PQ makes the distances approximate, so any single nearest neighbor is only a rough guess.
taXR borrows the SINTAX trick and resamples the neighbor set 100 times. Each round draws a random 25% of the k=50 neighbors and asks which taxon wins on closest L2 distance. A taxon's confidence is the fraction of rounds it keeps winning, so a call that only holds up when one lucky neighbor lands in the subset scores low.
def bootstrap_confidence(neighbors, predicted_taxon, n_iter=100):
wins = 0
for _ in range(n_iter):
subset = random.sample(neighbors, k=12) # ~25% of k=50
winner = min(subset, key=lambda x: x.distance).taxon
if winner == predicted_taxon:
wins += 1
return wins / n_iter # 0 to 100%The same resampling runs independently at each rank, giving a confidence at every level from phylum down to species. taXR then flags the reliable rank, the most specific level still clearing 70%. Genus or family usually clears the bar; species often does not. Each rank carries a weight in the final score (phylum 1.0, class 0.95, order 0.90, family 0.85, genus 0.70, species 0.50).
Below 50% confidence, the read is out of distribution for the index. taXR sends it to NCBI BLAST (blastn against nt), polling every 60s for up to 5 min, and keeps only hits with identity >= 97% and E-value <= 1e-50. A resolved sequence drops into a secondary incremental index, so the fast path handles similar reads afterward.
Sequence quality checks. Before trusting a call, taXR runs cheap sanity checks and attaches warnings. A COI barcode is around 650 bp, so it flags anything under 300 or over 700 bp. It also watches the ambiguous-base fraction (N count) and GC content, warning when GC leaves the 35 to 55% band typical of COI. Warnings annotate the result without blocking classification.
- Backend: Python, FastAPI, Uvicorn, FAISS (faiss-cpu), NumPy, Numba, Biopython
- Frontend: Next.js 16, React 19, TypeScript, Tailwind CSS v4
- Visualization: Recharts, react-d3-tree, react-force-graph-3d, three.js, Mol* (structure viewer)
- Data: MIDORI2 (GB268) COI reference set, FAISS IVF-PQ index, dataset in Git LFS
- Infra: Vercel (frontend), GitHub Actions CI and backend deploy to a VPS
The classifier lives under backend/projects/taXR; the frontend at / redirects straight into the taXR app (classify + explore).
taXR/
backend/projects/taXR/core/
api.py FastAPI REST + WebSocket endpoints
classifier.py classification engine
kmer.py k-mer vectorization (Numba JIT)
blast_fallback.py NCBI BLAST integration
incremental_index.py dynamic index for new sequences
job_manager.py async job management
backend/projects/taXR/dataset/ FAISS index + taxonomy maps (Git LFS)
frontend/src/ landing + taXR app (classify + explore), D3/three.js components
.github/workflows/ CI + backend deploy
The reference index is trained on MIDORI2 (GB268), a curated set of 1.78M mitochondrial COI sequences. It ships in Git LFS alongside a taxonomy_map.pkl (seq_id to taxonomy) and sequence_ids.pkl, plus small BOLD and MIDORI query sets for testing.
Taxonomy strings follow the standard prefixed format:
k__Animalia;p__Arthropoda;c__Insecta;o__Lepidoptera;f__Nymphalidae;g__Danaus;s__plexippus
The FastAPI backend exposes REST and WebSocket endpoints. The main ones: POST /classify (single), POST /classify/batch and POST /classify/stream, GET /classify/status/{job_id} and /result/{job_id} for polling, WS /ws/blast/{job_id} for live BLAST progress, plus /align, /treeview, /index/stats, and /health.
curl -X POST https://api.taxr.space/classify \
-H "Content-Type: application/json" \
-d '{"sequence": "ATGCGATCGATCG...", "k": 50}'The response carries the taxonomy string, an overall confidence, the nearest match and distance, per-rank hierarchical_confidence, and the reliable_rank. The batch endpoint takes {id, sequence} items plus k and force_blast, returning a job_id to follow over the WebSocket when a read needs BLAST.
cd backend
pip install -r requirements.txt
# place the dataset files (Git LFS) where the classifier expects them
cd projects/taXR/core
uvicorn api:app --host 0.0.0.0 --port 8000Needs Python 3.9+; dependencies are in requirements.txt.
cd frontend
npm install
npm run dev # development
npm run build && npm start # productionPer-rank accuracy on two sets: BOLD 1K (1000 sequences, ~95/s) and a large set (500K sequences, ~283/s).
| Rank | BOLD 1K | 500K set |
|---|---|---|
| Phylum | 99.4% | 96.8% |
| Class | 97.2% | 88.5% |
| Order | 93.6% | 89.7% |
| Family | 88.8% | 79.7% |
| Genus | 90.0% | 53.5% |
| Species | 56.6% | 28.0% |
How it sits next to other classifiers:
| Method | Marker | Genus accuracy | Speed | DB size |
|---|---|---|---|---|
| taXR | COI | 90% | ~50/s | 80MB |
| BLAST | all | 97% | ~1/s | 100GB |
| RDP | 16S | 92% | ~100/s | 500MB |
| SINTAX | 16S | 50% | ~50/s | 2GB |
| IDTAXA | 16S | 85% | ~10/s | 1GB |
RDP, SINTAX, and IDTAXA are tuned for 16S rRNA in bacteria while taXR targets COI in animals, so read that table as a rough reference.
Working prototype, live at taxr.vercel.app. What I would add next:
- Multi-marker support: ITS (fungi), 16S rRNA (bacteria), rbcL and matK (plants)
- Chimera detection, both reference-based and de novo
- Integration with metabarcoding pipelines (QIIME2, DADA2)
Investigathon 2025, Team 8: J. Quiroga, A. Sidler, D. Garcia Taddia, E. Patino.
- Edgar RC (2016). SINTAX: a simple non-Bayesian taxonomy classifier for 16S and ITS sequences. bioRxiv
- Wang Q et al. (2007). Naive Bayesian classifier for rapid assignment of rRNA sequences. Appl Environ Microbiol
- Machida RJ et al. (2017). Metazoan mitochondrial gene sequence reference datasets for taxonomic assignment of environmental samples. Mol Ecol Resour
- Johnson M et al. (2008). NCBI BLAST: a better web interface. Nucleic Acids Res
- Felsenstein J (1985). Confidence limits on phylogenies: an approach using the bootstrap. Evolution 39(4): 783-791
MIT. Use it however you like, cite us if it helps.



