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.
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.
Read the ingest column as a draw. gffbase spans 1.21× to 0.69× against
gffutils: ahead where per-feature overhead dominates, behind on the
attribute-dense whole-genome files. Both engines are attribute-bound and
effectively serial. The durable advantages — batched extraction, spatial
indexing, SQL over the whole corpus — are elsewhere. 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 |
10 min 14 s |
7 min 1 s |
0.69× |
53.57 GB |
707 ±0% (n=5) |
963 ms / 1.93 M desc |
GENCODE v49 (basic) |
GFF3 |
6,066,054 |
11 min 8 s |
9 min 59 s |
0.90× |
62.00 GB |
705 ±0% (n=5) |
1096 ms / 1.93 M desc |
RefSeq GRCh38.p14 |
GFF3 |
4,932,571 |
7 min 2 s |
6 min 34 s |
0.93× |
26.80 GB |
540 ±0% (n=5) |
588 ms / 999 k desc |
CHESS 3.1.3 |
GFF3 |
2,761,061 |
1 min 53 s |
2 min 17 s |
1.21× |
3.38 GB |
702 ±0% (n=5) |
202 ms / 161 k desc |
MANE v1.5 (Ensembl) |
GFF3 |
524,834 |
40.0 s |
45.7 s |
1.14× |
3.98 GB |
840 ±0% (n=5) |
206 ms / 156 k desc |
Install¶
pip install gffbase
Note
This site documents gffbase 0.2.0, released 2026-09-11. Upgrading from 0.1.0 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:
for tx in db.children("ENSG00000139618", 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 - 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?
- Why is my 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