Skip to content

Repository files navigation

taXR

taXR

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.

Demo Investigathon 2025

Video demos

Main flow Auto-testing Vector space
Main demo Auto-testing Vector space
COI sequence classification Confidence estimation 3D exploration + k-mer fingerprints

Why I built it

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.

What it does

  • 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

How it works

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
Loading

1. K-mer vectorization

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 normalization

2. FAISS IVF-PQ search

The 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.

3. Bootstrap confidence (SINTAX-style)

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%

4. Hierarchical confidence

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).

5. BLAST fallback

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.

Secondary highlights

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.

Tech stack

  • 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

Repo layout

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

Dataset

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

API

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.

Running it

Backend

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 8000

Needs Python 3.9+; dependencies are in requirements.txt.

Frontend

cd frontend
npm install
npm run dev            # development
npm run build && npm start  # production

Benchmarks

Per-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.

Status and future work

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)

Team

Investigathon 2025, Team 8: J. Quiroga, A. Sidler, D. Garcia Taddia, E. Patino.

References

  • 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

License

MIT. Use it however you like, cite us if it helps.

About

A CPU classifier for COI DNA barcodes, identifying a read in milliseconds on a laptop. K-mer vectors in a FAISS index, with bootstrap confidence per rank.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages