Skip to main content

scigantic-bindingdb

CI PyPI PyPI - Python Version License

Query BindingDB directly from a public S3 mirror with DuckDB.

import scigantic_bindingdb as bindingdb

df = bindingdb.query("""
    SELECT reactant_set_id, ligand_smiles, ki_nm_value
    FROM measurements
    WHERE ki_nm_value IS NOT NULL
    LIMIT 5
""")

That query runs against s3://scigantic-bindingdb over DuckDB's httpfs extension. Nothing is downloaded first, and there's no local database file sitting on disk afterward.

Installation

$ pip install scigantic-bindingdb

Repeat calls reuse the connection

measurements(), chembl_bridge(), dti_pairs() and query() each open a connection to run one query. Under the hood that's a cursor() on a shared, lazily-created base connection per release, not a fresh duckdb.connect() every time: the first call in a process pays for INSTALL/LOAD httpfs, the S3 secret, and registering the five core views, and every later call for that release skips straight to the query.

Measured, not asserted, on 2026-08-29: 8 repeat calls to measurements(uniprot_id="P00533", endpoint="ki") in a loop, before vs after this was fixed:

measured
8 calls, fresh connection each time 318s total, ~39.8s/call
8 calls, shared base connection + cursor() 5.3s total, ~0.66s/call (~60x)

connect() still returns something you can close(), and closing it only closes your cursor; the base connection stays open for the next call to reuse. A single DuckDB connection isn't safe for concurrent execute() calls from multiple threads, so each call gets its own cursor rather than the base connection itself, which is what makes this safe from a thread pool too.

What's different about BindingDB

Every row is one binding measurement (Ki, IC50, Kd or EC50) between one ligand and one protein target, rather than ChEMBL's assay-centric bioactivity record. BindingDB ships no relational database, just a flat TSV; the mirror normalizes it into measurements (one row per measurement), target_chains (one row per protein chain of the target, since BindingDB's raw format repeats a column block once per chain in a multimer) and target_chain_names.

Measurements, filtered the way that avoids the two sharp edges

df = bindingdb.measurements(uniprot_id="P00533", endpoint="ki")  # EGFR

Two things this does that a raw query on measurements doesn't do for you:

Censored values stay out unless you ask for them. Ki/IC50/Kd/EC50 are occasionally reported as >X or <X rather than an exact value, the same idea as ChEMBL's standard_relation. exact_only=True, the default, keeps only rows where that endpoint's qualifier is =:

df = bindingdb.measurements(uniprot_id="P00533", endpoint="ic50", exact_only=False)  # include censored bounds too

Target filtering doesn't fan out on multichain complexes. uniprot_id matches against target_chains with an EXISTS check, not a JOIN, so a target whose accession appears on more than one chain of the same complex still returns each measurement once.

bindingdb.query() still reaches the raw tables directly for anything this leaves out.

Cross-referencing ChEMBL

BindingDB ingests ChEMBL as one of its own curated source feeds (51.3% of measurements in the 202608 release), and ChEMBL separately absorbs some BindingDB patent-derived bioactivity data, so the two archives are not independent corpora. derived/bindingdb_chembl_bridge.parquet, built once at mirror time, joins measurements to scigantic-chembl by BindingDB's own chembl_id column where present (authoritative) and falls back to an exact InChIKey match where it's missing:

df = bindingdb.chembl_bridge(reactant_set_id="50000001")

with_names=True (the default) also reaches into the live scigantic-chembl mirror for the matched compound's ChEMBL preferred name, a second public-bucket read over the same connection: not a mount-level dependency between the two archives, just a query-time join across two buckets that are both public and read-only here.

Drug-target-interaction pairs

BindingDB is the dataset most DTI/proteochemometric tooling (like DeepPurpose) is built around, specifically because it ships a full protein sequence alongside every affinity measurement, something ChEMBL's bioactivity tables don't do as directly. derived/dti_pairs.parquet is BindingDB reshaped into the (ligand, target, affinity) triples a model trains on, done once rather than re-derived by every caller:

df = bindingdb.dti_pairs(endpoint="ki", single_chain_only=True)
  reactant_set_id  ligand_smiles       target_sequence  uniprot_id  endpoint  affinity_nm  p_affinity
           764556  Cc1ncoc1-c1nnc...   MASLSQLSSHLN...      P35462        ki         1.74    8.759451

Only exact measurements (never a censored >X/<X bound treated as a real label), and p_affinity is already computed as -log10(affinity_nm * 1e-9), the same transform as ChEMBL's pchembl_value. 2,589,053 pairs across the four endpoints in the 202608 release, 1,163,672 distinct ligands, 9,219 distinct UniProt targets.

Multichain targets are represented by chain 1's sequence only, standard practice for DTI benchmarks. Pass single_chain_only=True to drop the 5.7% of rows where that simplifies an actual multi-protein complex, if single-chain purity matters for your model. See derived/DTI_README.md in the mirror for the exact filters applied.

Working offline

Off by default, since zero setup is the whole point. Turn it on to run the same queries repeatedly without re-fetching from S3:

import scigantic_bindingdb as bindingdb

bindingdb.enable_cache()
df = bindingdb.chembl_bridge()  # downloads the bridge table once, then reads from disk

chembl_bridge() and dti_pairs() each need exactly one derived file, so caching downloads that one file to ~/.cache/scigantic-bindingdb (override with enable_cache(cache_dir=...) or the SCIGANTIC_BINDINGDB_CACHE environment variable) and reuses it after that.

connect(), query() and measurements() don't participate in this: connect() registers five core tables as views the first time a release is used, so caching them there would mean the first call for any release eagerly downloads everything regardless of what the query actually touches. Cache one table yourself if you want it locally: bindingdb.cache_resolve("202608/parquet/measurements.parquet") downloads it and returns the local path, usable directly in read_parquet(...).

What's mirrored

bindingdb.releases()
release raw tables ChEMBL bridge DTI pairs
202608 yes yes yes

This table isn't hardcoded. releases() reads a small manifest published alongside each mirror run. If it can't be reached, calls fall back to the snapshot shipped with whatever version you have installed and print a warning, rather than failing outright.

Not mirrored yet: BindingDB's 3D SDF structures and precomputed similarity/substructure search (no fingerprint corpus has been built for this archive). bindingdb.query() still reaches every raw table the mirror carries.

Command line

$ scigantic-bindingdb info
$ scigantic-bindingdb query "SELECT count(*) FROM measurements" --release 202608

License

MIT-0. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

scigantic_bindingdb-0.2.3.tar.gz (24.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

scigantic_bindingdb-0.2.3-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

File details

Details for the file scigantic_bindingdb-0.2.3.tar.gz.

File metadata

  • Download URL: scigantic_bindingdb-0.2.3.tar.gz
  • Upload date:
  • Size: 24.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.16

File hashes

Hashes for scigantic_bindingdb-0.2.3.tar.gz
Algorithm Hash digest
SHA256 055be93ea0800096cf40bd553072f3fda426b641e250a5d515699b7c16aa3bb5
MD5 51a2d43f3ca4d89d0ac313f813ca8f77
BLAKE2b-256 47842b3b678216afa56d55a689318e2154ad4199a6c099614f152277d163cacd

See more details on using hashes here.

File details

Details for the file scigantic_bindingdb-0.2.3-py3-none-any.whl.

File metadata

File hashes

Hashes for scigantic_bindingdb-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e3a34d6d97ed658e4ef661a983a493299ef97d83f6585134bd70437830bc69ea
MD5 78214139cb87c917380f2ee320d48733
BLAKE2b-256 4689f0d46e25fc5884d6f99b7c302c7caa8c7e559a6b54be5bee26598a30c472

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.5

2 files

0.2.4

2 files

This release

0.2.3 This release

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page