History of bindingdb
The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.
[entries] Add the date when each entry was added, and remove icon_hash beff7c9
The dates are those of the commits that first added the entries' files (2020-08-11 for the entries of the initial commit); renamed entries keep the date of their first name. Changed: none
diff --git a/entries/bindingdb/__init__.py b/entries/bindingdb/__init__.py
index 2e8019c..27c9768 100644
--- a/entries/bindingdb/__init__.py
+++ b/entries/bindingdb/__init__.py
@@ -54,7 +54,7 @@ tags = ['Biological', 'Chemical', 'Drug interactions', 'Weighted', 'Bipartite',
url = ['https://www.bindingdb.org/', 'https://library.ucsd.edu/dc/collection/bb03870458']
citation = [('T. Liu, L. Hwang, S. K. Burley, C. I. Nitsche, C. Southan, W. P. Walters, M. K. Gilson, "BindingDB in 2024: a FAIR knowledgebase of protein-small molecule binding data." Nucleic Acids Research 53, D1633-D1644 (2025)',
'https://doi.org/10.1093/nar/gkae1075')]
-icon_hash = None
+date_added = '2026-10-31'
# The data curated by BindingDB are CC BY 4.0; the data imported from
# ChEMBL are CC BY-SA 3.0 (note in the archives)
upstream_license = ('Creative Commons Attribution 4.0 International (CC BY 4.0); data from ChEMBL: CC BY-SA 3.0',
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) bc3dbbd
The lists of properties at the end of the descriptions marked edge properties with "Edge" but left node properties unmarked, or grouped them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property was often unclear. Every item now starts with (Node), (Edge) or (Graph), followed by the property names, a colon and the explanation, one item per property or group of properties; qualifications (kinds of nodes, networks) follow the names in parentheses. Properties that were only named got a short explanation, and a few items that named the wrong kind of property were corrected.
diff --git a/entries/bindingdb/__init__.py b/entries/bindingdb/__init__.py
index 5b143db..2e8019c 100644
--- a/entries/bindingdb/__init__.py
+++ b/entries/bindingdb/__init__.py
@@ -34,22 +34,22 @@ There is one network for each year from 2023 to 2026, from the quarterly archive
The networks have the following properties:
-- `is_ligand`: whether the node is a ligand (otherwise it is a target).
-- `name`: the name of the ligand (often several synonyms separated by `::`), or of the target.
-- `monomer_id`: the BindingDB identifier of the ligand (`-1` for targets).
-- `smiles`, `inchi_key`: the SMILES string and the InChIKey of the ligand.
-- `pubchem_cid`, `chebi_id`, `chembl_id`, `drugbank_id`: the identifiers of the ligand in other databases.
-- `organism`: the organism of the target, as given by the curator or the data source.
-- `num_chains`: the number of protein chains of the target (more than one for complexes).
-- `uniprot_ids`: the UniProt (Swiss-Prot or, failing that, TrEMBL) accessions of the chains of the target.
-- `chain_sequences`: the amino-acid sequences of the chains of the target.
-- Edge `reactant_set_id`: the BindingDB identifier of the measurement.
-- Edge `ki`, `ic50`, `kd`, `ec50` (nM), `kon` (1/(M s)), `koff` (1/s): the measured values (NaN when not given).
-- Edge `qualifiers`: the qualifiers of the measured values, when given as bounds (e.g. `ic50 >`, for $IC_{50} > 10000$ nM).
-- Edge `ph`, `temp`: the pH and the temperature (°C) of the measurement (NaN if not given).
-- Edge `source`: the source of the measurement (e.g. `ChEMBL`, `US Patent`, `Curated from the literature by BindingDB`).
-- Edge `article_doi`, `pmid`, `pubchem_aid`, `patent_number`: the article, PubChem assay or patent of the measurement.
-- Edge `date` (2025, 2026): the date at which the measurement was added to BindingDB (as Unix time; `-1` if not given)."""
+- (Node) `is_ligand`: whether the node is a ligand (otherwise it is a target).
+- (Node) `name`: the name of the ligand (often several synonyms separated by `::`), or of the target.
+- (Node) `monomer_id`: the BindingDB identifier of the ligand (`-1` for targets).
+- (Node) `smiles`, `inchi_key`: the SMILES string and the InChIKey of the ligand.
+- (Node) `pubchem_cid`, `chebi_id`, `chembl_id`, `drugbank_id`: the identifiers of the ligand in other databases.
+- (Node) `organism`: the organism of the target, as given by the curator or the data source.
+- (Node) `num_chains`: the number of protein chains of the target (more than one for complexes).
+- (Node) `uniprot_ids`: the UniProt (Swiss-Prot or, failing that, TrEMBL) accessions of the chains of the target.
+- (Node) `chain_sequences`: the amino-acid sequences of the chains of the target.
+- (Edge) `reactant_set_id`: the BindingDB identifier of the measurement.
+- (Edge) `ki`, `ic50`, `kd`, `ec50` (nM), `kon` (1/(M s)), `koff` (1/s): the measured values (NaN when not given).
+- (Edge) `qualifiers`: the qualifiers of the measured values, when given as bounds (e.g. `ic50 >`, for $IC_{50} > 10000$ nM).
+- (Edge) `ph`, `temp`: the pH and the temperature (°C) of the measurement (NaN if not given).
+- (Edge) `source`: the source of the measurement (e.g. `ChEMBL`, `US Patent`, `Curated from the literature by BindingDB`).
+- (Edge) `article_doi`, `pmid`, `pubchem_aid`, `patent_number`: the article, PubChem assay or patent of the measurement.
+- (Edge) `date` (2025, 2026): the date at which the measurement was added to BindingDB (as Unix time; `-1` if not given)."""
tags = ['Biological', 'Chemical', 'Drug interactions', 'Weighted', 'Bipartite', 'Multigraph', 'Metadata']
url = ['https://www.bindingdb.org/', 'https://library.ucsd.edu/dc/collection/bb03870458']
citation = [('T. Liu, L. Hwang, S. K. Burley, C. I. Nitsche, C. Southan, W. P. Walters, M. K. Gilson, "BindingDB in 2024: a FAIR knowledgebase of protein-small molecule binding data." Nucleic Acids Research 53, D1633-D1644 (2025)',
[entries] New entry: bindingdb 3703a5f
diff --git a/entries/bindingdb/__init__.py b/entries/bindingdb/__init__.py
new file mode 100644
index 0000000..5b143db
--- /dev/null
+++ b/entries/bindingdb/__init__.py
@@ -0,0 +1,208 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2026 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import csv
+import datetime as dt
+import math
+import re
+import sys
+from collections import defaultdict
+
+from .. import *
+
+title = "BindingDB protein-ligand binding affinities (2023-2026)"
+description = r"""Bipartite networks of small molecules (ligands) and the protein targets they bind, from BindingDB, the public database of measured binding affinities, mostly of drug-like molecules and drug targets. The measurements are curated by BindingDB from articles and US and WO patents, or imported from ChEMBL, PubChem BioAssay, the PDSP Ki database, and other sources (D3R, CSAR, the Taylor group at UCSD).
+
+Nodes are ligands (BindingDB monomers) and targets (proteins, or complexes of several protein chains), and an edge connects a ligand to a target for each measurement of their binding (an inhibition constant $K_i$, a half-maximal inhibitory or effective concentration $IC_{50}$ or $EC_{50}$, a dissociation constant $K_d$, or association and dissociation rates), so that a ligand and a target can be connected by many edges (e.g. measurements in different articles or assays). Since the data do not give identifiers of the targets, targets are identified by the amino-acid sequences of their chains (the name of a target varies, e.g. with the range of residues of the construct, and the first one is given); the few targets without sequences are identified by their name and organism. The authors, institutions and publication dates of the measurements, the InChI strings and the links to other databases of the ligands, and the other properties of the target chains (PDB identifiers, names) are left out.
+
+There is one network for each year from 2023 to 2026, from the quarterly archives of BindingDB in the UC San Diego Library Digital Collections (which start in October 2023), from the archives of October: 2023-10-01, 2024-10-01, 2025-10-01 and 2026-10-01.
+
+The networks have the following properties:
+
+- `is_ligand`: whether the node is a ligand (otherwise it is a target).
+- `name`: the name of the ligand (often several synonyms separated by `::`), or of the target.
+- `monomer_id`: the BindingDB identifier of the ligand (`-1` for targets).
+- `smiles`, `inchi_key`: the SMILES string and the InChIKey of the ligand.
+- `pubchem_cid`, `chebi_id`, `chembl_id`, `drugbank_id`: the identifiers of the ligand in other databases.
+- `organism`: the organism of the target, as given by the curator or the data source.
+- `num_chains`: the number of protein chains of the target (more than one for complexes).
+- `uniprot_ids`: the UniProt (Swiss-Prot or, failing that, TrEMBL) accessions of the chains of the target.
+- `chain_sequences`: the amino-acid sequences of the chains of the target.
+- Edge `reactant_set_id`: the BindingDB identifier of the measurement.
+- Edge `ki`, `ic50`, `kd`, `ec50` (nM), `kon` (1/(M s)), `koff` (1/s): the measured values (NaN when not given).
+- Edge `qualifiers`: the qualifiers of the measured values, when given as bounds (e.g. `ic50 >`, for $IC_{50} > 10000$ nM).
+- Edge `ph`, `temp`: the pH and the temperature (°C) of the measurement (NaN if not given).
+- Edge `source`: the source of the measurement (e.g. `ChEMBL`, `US Patent`, `Curated from the literature by BindingDB`).
+- Edge `article_doi`, `pmid`, `pubchem_aid`, `patent_number`: the article, PubChem assay or patent of the measurement.
+- Edge `date` (2025, 2026): the date at which the measurement was added to BindingDB (as Unix time; `-1` if not given)."""
+tags = ['Biological', 'Chemical', 'Drug interactions', 'Weighted', 'Bipartite', 'Multigraph', 'Metadata']
+url = ['https://www.bindingdb.org/', 'https://library.ucsd.edu/dc/collection/bb03870458']
+citation = [('T. Liu, L. Hwang, S. K. Burley, C. I. Nitsche, C. Southan, W. P. Walters, M. K. Gilson, "BindingDB in 2024: a FAIR knowledgebase of protein-small molecule binding data." Nucleic Acids Research 53, D1633-D1644 (2025)',
+ 'https://doi.org/10.1093/nar/gkae1075')]
+icon_hash = None
+# The data curated by BindingDB are CC BY 4.0; the data imported from
+# ChEMBL are CC BY-SA 3.0 (note in the archives)
+upstream_license = ('Creative Commons Attribution 4.0 International (CC BY 4.0); data from ChEMBL: CC BY-SA 3.0',
+ 'https://creativecommons.org/licenses/by/4.0/')
+# The quarterly archives of BindingDB in the UC San Diego Library Digital
+# Collections (one per year, the October ones), with the zip file of the TSV
+# of all the data (component number and member name vary)
+ARCHIVES = {
+ "2023_10": ("bb6496315b", "_4_1.zip", "BindingDB_All_202310.tsv"),
+ "2024_10": ("bb4654545d", "_4_1.zip", "BindingDB_All.tsv"),
+ "2025_10": ("bb56118946", "_3_1.zip", "BindingDB_All.tsv"),
+ "2026_10": ("bb4419353t", "_3_1.zip", "BindingDB_All.tsv"),
+}
+upstream_prefix = 'https://library.ucsd.edu/dc/object'
+files = [(f"{obj}/{comp}/download:zip:{member}", alt, None)
+ for alt, (obj, comp, member) in ARCHIVES.items()]
+
+def fetch_upstream(force=False):
+ return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
+ force)
+
+_check_rows = None
+
+NUM = {"Ki (nM)": "ki", "IC50 (nM)": "ic50", "Kd (nM)": "kd", "EC50 (nM)": "ec50",
+ "kon (M-1-s-1)": "kon", "koff (s-1)": "koff"}
+LIGAND = {"BindingDB Ligand Name": "name", "Ligand SMILES": "smiles",
+ "Ligand InChI Key": "inchi_key", "PubChem CID": "pubchem_cid",
+ "ChEBI ID of Ligand": "chebi_id", "ChEMBL ID of Ligand": "chembl_id",
+ "DrugBank ID of Ligand": "drugbank_id"}
+EDGE = {"Curation/DataSource": "source", "Article DOI": "article_doi",
+ "PMID": "pmid", "PubChem AID": "pubchem_aid",
+ "Patent Number": "patent_number"}
+
+def to_float(x):
+ try:
+ return float(x)
+ except ValueError:
+ return math.nan
+
+def to_time(x):
+ try:
+ return int(dt.datetime.strptime(x, "%m/%d/%Y").replace(
+ tzinfo=dt.timezone.utc).timestamp())
+ except ValueError:
+ return -1
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+ name = __name__.split(".")[-1]
+ csv.field_size_limit(sys.maxsize)
+ for fname, alt, fmt in files:
+ if alts is not None and alt not in alts:
+ continue
+ with open_upstream_file(name, fname, "r", errors="replace") as f:
+ reader = csv.reader(f, delimiter="\t", quoting=csv.QUOTE_NONE)
+ h = next(reader)
+ c = {x: i for i, x in enumerate(h)}
+ # blocks of 12 columns per target chain (in the older archives,
+ # rows have only as many blocks as the target has chains)
+ start = h.index([x for x in h if x.startswith("BindingDB Target Chain Sequence")][0])
+ assert h[start + 4].startswith("UniProt (SwissProt) Primary ID") and \
+ h[start + 9].startswith("UniProt (TrEMBL) Primary ID"), h[start:start + 12]
+ org = "Target Source Organism According to Curator or DataSource"
+ nch = "Number of Protein Chains in Target (>1 implies a multichain complex)"
+ has_date = "Date in BindingDB" in c
+
+ vidx = {}
+ vvals = defaultdict(list)
+ strings = {}
+ def s(x):
+ return strings.setdefault(x, x)
+ edges = []
+ evals = defaultdict(list)
+ for i, row in enumerate(reader):
+ if _check_rows is not None and i >= _check_rows:
+ break
+ if len(row) < start or (len(row) - start) % 12 != 0:
+ raise ValueError(f"{alt}: malformed row {i}: {row[:2]}")
+ blocks = range(start, len(row), 12)
+ mid = row[c["BindingDB MonomerID"]]
+ key = ("l", mid)
+ u = vidx.get(key)
+ if u is None:
+ u = vidx[key] = len(vidx)
+ vvals["is_ligand"].append(True)
+ vvals["monomer_id"].append(int(mid))
+ for col, p in LIGAND.items():
+ vvals[p].append(row[c[col]])
+ for p in ("organism", "uniprot_ids", "chain_sequences"):
+ vvals[p].append([] if p != "organism" else "")
+ vvals["num_chains"].append(0)
+ seqs = tuple(row[j] for j in blocks if row[j])
+ tname = row[c["Target Name"]]
+ key = ("t", seqs) if seqs else ("tn", tname, row[c[org]])
+ v = vidx.get(key)
+ if v is None:
+ v = vidx[key] = len(vidx)
+ vvals["is_ligand"].append(False)
+ vvals["monomer_id"].append(-1)
+ for col, p in LIGAND.items():
+ vvals[p].append(tname if p == "name" else "")
+ vvals["organism"].append(row[c[org]])
+ vvals["uniprot_ids"].append(
+ [row[j + 4] or row[j + 9] for j in blocks
+ if row[j] and (row[j + 4] or row[j + 9])])
+ vvals["chain_sequences"].append(list(seqs))
+ try:
+ vvals["num_chains"].append(int(row[c[nch]]))
+ except ValueError:
+ vvals["num_chains"].append(len(seqs))
+ edges.append((u, v))
+ evals["reactant_set_id"].append(int(row[c["BindingDB Reactant_set_id"]]))
+ quals = []
+ for col, p in NUM.items():
+ x = row[c[col]].strip()
+ m = re.match(r"^([<>~=]+)\s*(.*)$", x)
+ if m:
+ quals.append(f"{p} {m.group(1)}")
+ x = m.group(2)
+ evals[p].append(to_float(x) if x else math.nan)
+ evals["qualifiers"].append(s(",".join(quals)))
+ evals["ph"].append(to_float(row[c["pH"]]))
+ evals["temp"].append(to_float(row[c["Temp (C)"]].strip().removesuffix("C").strip()))
+ for col, p in EDGE.items():
+ evals[p].append(s(row[c[col]]))
+ if has_date:
+ evals["date"].append(to_time(row[c["Date in BindingDB"]]))
+
+ g = Graph(directed=False)
+ g.add_vertex(len(vidx))
+ g.add_edge_list(numpy.array(edges, dtype="int64").reshape(-1, 2))
+ del edges, vidx
+ g.vp.is_ligand = g.new_vp("bool", vals=vvals.pop("is_ligand"))
+ g.vp.name = g.new_vp("string", vals=vvals.pop("name"))
+ g.vp.monomer_id = g.new_vp("int64_t", vals=vvals.pop("monomer_id"))
+ for p in ["smiles", "inchi_key", "pubchem_cid", "chebi_id", "chembl_id",
+ "drugbank_id", "organism"]:
+ g.vp[p] = g.new_vp("string", vals=vvals.pop(p))
+ g.vp.num_chains = g.new_vp("int32_t", vals=vvals.pop("num_chains"))
+ g.vp.uniprot_ids = g.new_vp("vector<string>", vals=vvals.pop("uniprot_ids"))
+ g.vp.chain_sequences = g.new_vp("vector<string>", vals=vvals.pop("chain_sequences"))
+ g.ep.reactant_set_id = g.new_ep("int64_t", vals=evals.pop("reactant_set_id"))
+ for p in list(NUM.values()) + ["ph", "temp"]:
+ g.ep[p] = g.new_ep("double", vals=evals.pop(p))
+ for p in ["qualifiers"] + list(EDGE.values()):
+ g.ep[p] = g.new_ep("string", vals=evals.pop(p))
+ if has_date:
+ g.ep.date = g.new_ep("int64_t", vals=evals.pop("date"))
+ yield alt, g