GFFBase¶
A genomic-annotation engine built for whole-genome scale: a Rust GFF3/GTF
parser, DuckDB columnar storage, and a zero-copy PyArrow interface — with the
gffutils API preserved, so most code migrates by changing one import.
GFFBase reads GFF3 and GTF at whole-genome scale and answers the questions a genomics pipeline actually asks — spatial queries over intervals, hierarchy walks from gene to transcript to exon, and bulk extraction into Arrow, pandas or polars without a Python loop in the middle. The storage engine is DuckDB, so a database is one portable file and every query is columnar.
What you can do with GFFBase¶
Ask for a region and get the features that overlap it, from an R-tree index built during ingest.
children, parents and the batched forms, backed by a transitive
closure so depth costs nothing at query time.
The legacy API is preserved surface-for-surface. Most scripts move by changing one import.
children_batched(format="arrow") returns one Arrow table for
thousands of anchors.
Head-to-head benchmarks on the canonical human annotations, with the machine, versions and commit of each run recorded.
Build, inspect, validate and migrate a database without writing Python.
Byte-order marks, CDS-only and AUGUSTUS GTF, Liftoff ids, tar archives, truncated gzip: each read as it was meant, and reported.
The defaults and why they were chosen, what ingest costs stage by stage, and when a loop should become one batched query.
Measured performance¶
Every figure below is generated from the committed benchmark artifact, never typed in. See Performance for the full sweep and Benchmark methodology for how it was run.
gffbase ingests every corpus faster, 1.92× to 3.62× against gffutils,
into a smaller database. Batched extraction, spatial indexing and SQL over the
whole corpus come on top. peak RSS is ingest plus exhaustive validation,
not what the default path costs.
Corpus |
Format |
Lines |
gffbase ingest |
legacy ingest |
speedup |
peak RSS (ingest + full validation) |
spatial qps |
batched (5 k anchors) |
|---|---|---|---|---|---|---|---|---|
GENCODE v49 (basic) |
GTF |
6,068,892 |
3 min 36 s |
6 min 54 s |
1.92× |
53.47 GB |
728 ±1% (n=5) |
614 ms / 1.93 M desc |
GENCODE v49 (basic) |
GFF3 |
6,066,054 |
3 min 13 s |
9 min 45 s |
3.03× |
61.93 GB |
770 ±1% (n=5) |
666 ms / 1.93 M desc |
RefSeq GRCh38.p14 |
GFF3 |
4,932,571 |
2 min 5 s |
6 min 31 s |
3.13× |
26.69 GB |
588 ±1% (n=5) |
443 ms / 999 k desc |
CHESS 3.1.3 |
GFF3 |
2,761,061 |
37.0 s |
2 min 14 s |
3.62× |
2.93 GB |
678 ±3% (n=5) |
155 ms / 161 k desc |
MANE v1.5 (Ensembl) |
GFF3 |
524,834 |
15.6 s |
45.1 s |
2.89× |
4.00 GB |
890 ±0% (n=5) |
136 ms / 156 k desc |
Install¶
pip install gffbase
Note
This site documents gffbase 0.3.0, released 2026-09-27: ingest 2.4-2.9x faster, in under a third of the memory on whole-genome files; prefetched loops; and noisy files read as they were meant (see the changelog). Upgrading from 0.2.0 fixes defects that silently lost data; upgrading from 0.1.0 also fixes two SQL injection vulnerabilities; see the security advisory.
Quick start¶
from gffbase import create_db
with create_db("gencode.v49.annotation.gtf.gz", "gencode.duckdb") as db:
# GENCODE ids carry their version.
for tx in db.children("ENSG00000139618.19", featuretype="transcript"):
print(tx.id, tx.start, tx.end)
for feature in db.region("chr17:43044295-43125483", featuretype="exon"):
print(feature)
See Installation for the supported platforms, and Quickstart for a worked example that runs end to end.
Getting started
Using GFFBase
- Usage Gallery — every public method, copy-pasteable
- 1. Database initialization
- 2. Standard relational queries
- 3. Spatial queries
- 4. Feature object — fields, attributes, mutation, export
- 5. Vectorized ML API — the ones that make GFFBase fast
- 6. Synthesis & convenience methods
- 7. Advanced / escape hatches
- 8. Putting it all together — end-to-end ML pipeline
- Analysis gallery
- Command line
- Connections & concurrency
- Migrating from
gffutilstogffbase - Noisy and non-standard files
- Cookbooks
Background
Reference
- API reference
- Frequently asked questions
- Is gffbase really a drop-in replacement for gffutils?
- What is the one thing that will surprise me?
- Why does my ingest fail with
DuplicateIDError? - Do I need to close the database?
- Can several processes read one database at once?
- How do I get a dataframe out of it?
- How do I get every exon for thousands of transcripts?
- Is the database bigger than the SQLite one?
native_available()says False. Does that matter?- Do I need
bedtoolsorpybedtools? - Can I still use my old gffutils database?
- What about a database made by gffbase 0.1.x?
- How do I know my database is sound?
- Which Python versions are supported?
- How do I cite it?
- Troubleshooting
- Citation
- License
- Contact