drugTargetInteractions: Mining the Druggable Genome

Introduction

Overview

The drugTargetInteractions package provides utilities for identifying drug-target interactions starting from either a gene/protein identifier (target → drug) or a compound identifier (drug → target). It covers seven independent drug-target data sources - ChEMBL (Gaulton et al. 2012; Bento et al. 2014), PubChem, DGIdb, Open Targets, the Therapeutic Target Database (TTD), the Broad Institute Drug Repurposing Hub (CLUE) and the IUPHAR/BPS Guide to PHARMACOLOGY (GtoPdb) - each queried through its own live REST/GraphQL API or, for TTD, the Repurposing Hub and GtoPdb, a small local SQLite built from public bulk data. ChEMBL remains additionally available through a downloaded local SQLite instance for users who prefer that (see the Supplement).

Six of these (ChEMBL, DGIdb, Open Targets, TTD, Broad Repurposing Hub, GtoPdb) provide curated drug-target annotations - an expert-curated call that a drug acts on a target via some mechanism - and are queried the same way via queryDrugTargets()/combineDrugTargets() (see Cross-Source Queries). PubChem instead provides raw bioassay measurements (individual assay results, no mechanism claim attached) - a genuinely different kind of data, covered separately in Bioassay Queries alongside ChEMBL’s own bioassay function, getChemblBioassay().

Because every source has its own native identifier vocabulary (a ChEMBL molecule ID is not a PubChem CID is not a DrugBank ID), the package also provides a generic ID-translation layer: live services for mapping protein/gene identifiers (UniProt’s REST ID Mapping API (Wu et al. 2006)) and finding paralogs/orthologs (Ensembl’s homology REST API), plus a local SQLite built from UniChem’s bulk compound cross-reference table for translating between compound ID types (ChEMBL, PubChem, DrugBank, ChEBI, and more). A dispatcher function, queryDrugTargets(), ties all of this together: give it an identifier of any recognised type and it resolves that identifier to whatever native ID each requested source needs, queries one or more sources, and returns the results.

This vignette is organised as follows:

  • Quick Start - a five-minute tour of the essential commands.
  • Data Sources in Detail - one section per annotation source, with both query directions.
  • Bioassay Queries - the separate, raw-measurement track (ChEMBL, PubChem).
  • ID Translation Layer - the standalone translation functions.
  • Cross-Source Queries - queryDrugTargets() across one or many sources at once, performance guidance, and combining results into a single table.
  • Genome-Wide Master Table - an HGNC-anchored, checkpointed build covering all human protein-coding genes at once.
  • Supplement - legacy/alternative functions kept for backward compatibility.

Install Package

As Bioconductor package drugTargetInteraction can be installed with the BiocManager::install() function.

if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("drugTargetInteractions")

Alternatively, the package can be installed from GitHub as follows.

devtools::install_github("girke-lab/drugTargetInteractions", build_vignettes=TRUE) # Installs from github

Load Package and Access Help

library("drugTargetInteractions") # Loads the package
library(help="drugTargetInteractions") # Lists package info
vignette(topic="drugTargetInteractions", package="drugTargetInteractions") # Opens vignette

Quick Start

This section is meant to be read (and run) in about five minutes. It uses only sources that need no local database setup, so every chunk here works immediately after installing the package - no downloads beyond the live API calls themselves.

One Source, One Call

Every per-source function uses the same queryBy = list(molType, idType, ids) interface. Target → drug for the FGFR1 gene via ChEMBL:

getChemblDrugTarget(list(molType = "protein", idType = "Uniprot", ids = "P11362"))[
    , c("QueryIDs", "Drug_Name", "Action_Type", "Max_Phase")]
##    QueryIDs                 Drug_Name Action_Type Max_Phase
## 1    P11362   PAZOPANIB HYDROCHLORIDE   INHIBITOR         4
## 2    P11362    INFIGRATINIB PHOSPHATE   INHIBITOR         4
## 3    P11362              INFIGRATINIB   INHIBITOR         4
## 4    P11362               REGORAFENIB   INHIBITOR         4
## 5    P11362                   RG-1530   INHIBITOR         1
## 6    P11362                 LUCITANIB   INHIBITOR         2
## 7    P11362        BRIVANIB ALANINATE   INHIBITOR         3
## 8    P11362                 ORANTINIB   INHIBITOR         3
## 9    P11362        NINTEDANIB ESYLATE   INHIBITOR         4
## 10   P11362              FEXAGRATINIB   INHIBITOR         2
## 11   P11362  PD-0166285 HYDROCHLORIDE   INHIBITOR         1
## 12   P11362                 CP-459632   INHIBITOR         1
## 13   P11362               ERDAFITINIB   INHIBITOR         4
## 14   P11362                    E-7090   INHIBITOR         2
## 15   P11362               FUTIBATINIB   INHIBITOR         4
## 16   P11362                  BRIVANIB   INHIBITOR         3
## 17   P11362                LY-2874455   INHIBITOR         1
## 18   P11362 FGFR INHIBITOR DEBIO 1347   INHIBITOR         2
## 19   P11362               ROGARATINIB   INHIBITOR         2
## 20   P11362                 TG100-801   INHIBITOR         2
## 21   P11362              DERAZANTINIB   INHIBITOR         2
## 22   P11362               SURUFATINIB   INHIBITOR         3
## 23   P11362               PEMIGATINIB   INHIBITOR         4
## 24   P11362                  HMPL-453   INHIBITOR         2
## 25   P11362               ENMD-981693   INHIBITOR         2
## 26   P11362                    XL-999   INHIBITOR         2

The reverse direction (drug → target) uses the same function with molType = "cmp":

getChemblDrugTarget(list(molType = "cmp", idType = "chembl_id", ids = "CHEMBL1421"))[  # dasatinib
    , c("QueryIDs", "UniProt_ID", "Action_Type")]
##      QueryIDs UniProt_ID Action_Type
## 1  CHEMBL1421     P00519   INHIBITOR
## 2  CHEMBL1421     P09619   INHIBITOR
## 3  CHEMBL1421     P10721   INHIBITOR
## 4  CHEMBL1421     P29317   INHIBITOR
## 5  CHEMBL1421     P00519   INHIBITOR
## 6  CHEMBL1421     P11274   INHIBITOR
## 7  CHEMBL1421     P06239   INHIBITOR
## 8  CHEMBL1421     P07947   INHIBITOR
## 9  CHEMBL1421     P06241   INHIBITOR
## 10 CHEMBL1421     P12931   INHIBITOR
## 11 CHEMBL1421     P07948   INHIBITOR
## 12 CHEMBL1421     P42685   INHIBITOR
## 13 CHEMBL1421     P51451   INHIBITOR
## 14 CHEMBL1421     P08631   INHIBITOR
## 15 CHEMBL1421     P09769   INHIBITOR
## 16 CHEMBL1421     Q9H3Y6   INHIBITOR

Many Sources, One Call

queryDrugTargets() accepts an identifier of essentially any type (not just each source’s own native ID - see ID Translation Layer) and queries as many sources as you like in one call:

res <- queryDrugTargets(
    list(molType = "gene", idType = "symbol", ids = "FGFR1"),
    sources = c("chembl", "dgidb", "opentargets"))
names(res)              # one data.frame per source that returned results
## [1] "chembl"      "dgidb"       "opentargets"
sapply(res, nrow)
##      chembl       dgidb opentargets 
##          26         143          94

One Table

res above is a list - each source keeps its own native columns. To see one aligned table across sources, use combineDrugTargets():

combineDrugTargets(res)[1:6, ]
##   query_id gene_symbol               drug_name    action source
## 1    FGFR1        <NA> PAZOPANIB HYDROCHLORIDE INHIBITOR ChEMBL
## 2    FGFR1        <NA>  INFIGRATINIB PHOSPHATE INHIBITOR ChEMBL
## 3    FGFR1        <NA>            INFIGRATINIB INHIBITOR ChEMBL
## 4    FGFR1        <NA>             REGORAFENIB INHIBITOR ChEMBL
## 5    FGFR1        <NA>                 RG-1530 INHIBITOR ChEMBL
## 6    FGFR1        <NA>               LUCITANIB INHIBITOR ChEMBL

That is the essential workflow: pick an identifier, pick one or more sources, query, optionally combine. The rest of this vignette covers each piece in more detail.

Data Sources in Detail

Each source below is queried through its own bidirectional function, using that source’s native identifier types directly. queryDrugTargets() (see Cross-Source Queries) builds on top of these by resolving other identifier types first.

The table below is a quick-reference cheat sheet for every valid queryBy = list(molType, idType, ids) combination accepted by these per-source functions - each row corresponds to one working call, with a real, live-tested example value for ids. Full detail on each source follows in the subsections below.

Function molType idType Example ids
getChemblDrugTarget() "protein" "Uniprot" "P11362" (FGFR1)
getChemblDrugTarget() "cmp" "chembl_id" "CHEMBL25" (aspirin)
getDgidbDrugTarget() "gene" "symbol" "FGFR1"
getDgidbDrugTarget() "cmp" "name" "imatinib"
getOpenTargetsDrugTarget() "gene" "symbol" "FGFR1"
getOpenTargetsDrugTarget() "cmp" "name" "aspirin"
getPubchemDrugTarget() "gene" "symbol" "KLB"
getPubchemDrugTarget() "cmp" "name" "aspirin"
ttdTargetAnnot() "protein" "symbol" "FGFR1"
ttdTargetAnnot() "protein" "uniprot" "FGFR1_HUMAN" (mnemonic, not accession)
ttdTargetAnnot() "protein" "ttd_target_id" "T47101"
ttdTargetAnnot() "cmp" "name" "Pemigatinib"
ttdTargetAnnot() "cmp" "ttd_drug_id" "D0O6UY"
broadRepurposingHubAnnot() "protein"/"gene" "symbol" "FGFR1"
broadRepurposingHubAnnot() "cmp" "name" "erdafitinib"
broadRepurposingHubAnnot() "cmp" "broad_id" "BRD-K84868168-001-01-0"
gtoPdbTargetAnnot() "protein"/"gene" "symbol" "FGFR1"
gtoPdbTargetAnnot() "protein"/"gene" "gtp_target_id" "1808"
gtoPdbTargetAnnot() "cmp" "name" "pemigatinib"
gtoPdbTargetAnnot() "cmp" "gtp_ligand_id" "9767" (pemigatinib)

ids accepts a vector of more than one value for every row above; a single example is shown here for brevity. getChemblBioassay() and getPubchemDrugTarget()’s bioassay-track sibling functions (see Bioassay Queries) accept the same molType/idType combinations as their annotation counterparts in this table.

ChEMBL

ChEMBL (Gaulton et al. 2012; Bento et al. 2014) is queried live through its REST API via getChemblDrugTarget(). Native identifiers: a UniProt accession for target → drug, a ChEMBL molecule ID for drug → target.

chembl_t2d <- getChemblDrugTarget(
    list(molType = "protein", idType = "Uniprot", ids = "P11362"))  # FGFR1
head(chembl_t2d[, c("Drug_Name", "MOA", "Action_Type", "Max_Phase")])
##                 Drug_Name                                           MOA Action_Type Max_Phase
## 1 PAZOPANIB HYDROCHLORIDE Fibroblast growth factor receptor 1 inhibitor   INHIBITOR         4
## 2  INFIGRATINIB PHOSPHATE   Fibroblast growth factor receptor inhibitor   INHIBITOR         4
## 3            INFIGRATINIB   Fibroblast growth factor receptor inhibitor   INHIBITOR         4
## 4             REGORAFENIB Fibroblast growth factor receptor 1 inhibitor   INHIBITOR         4
## 5                 RG-1530 Fibroblast growth factor receptor 1 inhibitor   INHIBITOR         1
## 6               LUCITANIB Fibroblast growth factor receptor 1 inhibitor   INHIBITOR         2
chembl_d2t <- getChemblDrugTarget(
    list(molType = "cmp", idType = "chembl_id", ids = "CHEMBL25"))  # aspirin
chembl_d2t[, c("UniProt_ID", "Organism", "MOA")]
##   UniProt_ID     Organism                      MOA
## 1     P35354 Homo sapiens Cyclooxygenase inhibitor
## 2     P23219 Homo sapiens Cyclooxygenase inhibitor

Large ID batches are chunked automatically (see Performance Considerations). Standalone helpers are also available: getChemblMolecule() (compound properties), getChemblTarget() (search targets by gene/protein name), and getChemblBioactivities() (raw IC50/Ki/Kd/EC50 measurements for one target).

DGIdb

The Drug Gene Interaction Database is queried through its GraphQL API via getDgidbDrugTarget(). DGIdb’s schema is bidirectional by construction - a single interactions() query returns nodes that already carry both a gene and a drug identity - so both directions use the same underlying query, just filtered by a different argument. Native identifiers: a gene symbol or a drug name, both matched case-insensitively.

dgidb_t2d <- getDgidbDrugTarget(
    list(molType = "gene", idType = "symbol", ids = "FGFR1"))
head(dgidb_t2d[, c("drug_name", "interaction_types", "sources")])
##      drug_name interaction_types                                              sources
## 1 DERAZANTINIB         inhibitor TdgClinicalTrial; CKB-CORE; GuideToPharmacology; TTD
## 2  ENMD-981693         inhibitor                                               ChEMBL
## 3    SUNITINIB         inhibitor                        CKB-CORE; GuideToPharmacology
## 4  ENTRECTINIB                                                                    DTC
## 5 FEXAGRATINIB         inhibitor   MyCancerGenome; OncoKB; CKB-CORE; CIViC; TALC; TTD
## 6     PD173074                                                        CKB-CORE; CIViC
dgidb_d2t <- getDgidbDrugTarget(
    list(molType = "cmp", idType = "name", ids = "imatinib"))
head(dgidb_d2t[, c("gene_name", "interaction_types", "interaction_score")])
##   gene_name interaction_types interaction_score
## 1       KIT         inhibitor        0.74132923
## 2    MAPK10                          0.01394415
## 3    PDGFRB         inhibitor        0.06759367
## 4     IKZF1                          0.29003830
## 5    PDGFRA                          0.21326345
## 6    CYP2F1                          1.45019149

The sources column matters for reuse: DGIdb aggregates interactions from many upstream databases (ChEMBL, DrugBank, TTD, and others), each with its own license - a row’s sources value tells you where that specific interaction actually came from.

Open Targets

Open Targets is queried through its GraphQL API via getOpenTargetsDrugTarget(). Unlike DGIdb, its schema is asymmetric: target → drug goes through Target.drugAndClinicalCandidates, drug → target through Drug.mechanismsOfAction. Native identifiers: a gene symbol (an Ensembl gene ID also passes through unresolved) for target → drug, a compound name (a ChEMBL ID also passes through unresolved, since Open Targets drug IDs are ChEMBL IDs) for drug → target.

ot_t2d <- getOpenTargetsDrugTarget(
    list(molType = "gene", idType = "symbol", ids = "FGFR1"))
head(ot_t2d[, c("drug_name", "mechanism_of_action", "max_clinical_stage")])
##   drug_name                                     mechanism_of_action max_clinical_stage
## 1 TG100-801                      Ephrin type-B receptor 4 inhibitor            PHASE_2
## 2 TG100-801           Fibroblast growth factor receptor 1 inhibitor            PHASE_2
## 3 TG100-801  Platelet-derived growth factor receptor beta inhibitor            PHASE_2
## 4 TG100-801           Fibroblast growth factor receptor 2 inhibitor            PHASE_2
## 5 TG100-801                                           SRC inhibitor            PHASE_2
## 6 TG100-801 Vascular endothelial growth factor receptor 1 inhibitor            PHASE_2
ot_d2t <- getOpenTargetsDrugTarget(
    list(molType = "cmp", idType = "name", ids = "aspirin"))
ot_d2t[, c("approved_symbol", "mechanism_of_action")]  # PTGS1/PTGS2 = COX1/COX2
##   approved_symbol      mechanism_of_action
## 1           PTGS2 Cyclooxygenase inhibitor
## 2           PTGS1 Cyclooxygenase inhibitor

Note that agonist targets or those without a drugged pocket (e.g. FGF21, NLRP3, TFEB, ADIPOR1/2 in this package’s original test-gene set) legitimately return zero rows from Open Targets - that reflects the state of drug discovery for that target, not a query failure.

Requesting Additional Fields

getChemblDrugTarget(), getPubchemDrugTarget(), getDgidbDrugTarget() and getOpenTargetsDrugTarget() return a small, harmonized set of columns by default (fields = "core") - this is exactly what lets combineDrugTargets() bind rows from different sources into one table (see Combining Results). For a single-source query, though, each underlying API exposes many more fields than this curated set. Pass fields = "all" to get everything available, source-prefixed to avoid name clashes between sources:

chembl_all <- getChemblDrugTarget(
    list(molType = "cmp", idType = "chembl_id", ids = "CHEMBL25"), fields = "all")
dim(chembl_all)
## [1]   2 111
chembl_all[, c("Drug_Name", "mechanism.mechanism_comment", "molecule.molecule_type")]
##   Drug_Name mechanism.mechanism_comment molecule.molecule_type
## 1   ASPIRIN                        <NA>         Small molecule
## 2   ASPIRIN                        <NA>         Small molecule

Use listDrugTargetFields() to browse what a source offers before deciding what to keep:

listDrugTargetFields("chembl")[1:10]
##  [1] "QueryIDs"       "chembl_id"      "Drug_Name"      "MOA"            "Action_Type"   
##  [6] "Max_Phase"      "First_Approval" "ChEMBL_TID"     "UniProt_ID"     "Desc"

fields also accepts a character vector directly, so a single call can ask for just the extra columns you actually want:

getChemblDrugTarget(
    list(molType = "cmp", idType = "chembl_id", ids = "CHEMBL25"),
    fields = c("Drug_Name", "molecule.molecule_type", "target.organism"))
##   QueryIDs Drug_Name molecule.molecule_type target.organism
## 1 CHEMBL25   ASPIRIN         Small molecule    Homo sapiens
## 2 CHEMBL25   ASPIRIN         Small molecule    Homo sapiens

The same fields argument works identically on all four source functions (and on queryDrugTargets(), which forwards it through). For ChEMBL and PubChem (REST APIs), fields = "all" costs nothing extra - the full response is already fetched, just no longer discarded. For DGIdb and Open Targets (GraphQL APIs), it does request a wider response, since GraphQL only returns fields a query explicitly asks for.

TTD (Therapeutic Target Database)

TTD has no live API. Its flat files are downloaded once and assembled into a small local SQLite via buildTtdDb(); ttdTargetAnnot() then queries that local database repeatedly. This mirrors ChEMBL’s local-SQLite option (see the Supplement) - build once, query many times.

ttdDbPath <- buildTtdDb(rerun = TRUE)  # first build must fetch; rerun = FALSE reuses the cache afterward

Native identifiers for ttdTargetAnnot(): for target → drug, a gene symbol (idType = "symbol"), a UniProt mnemonic entry name such as "FGFR1_HUMAN" (idType = "uniprot" - note this is not the UniProt accession "P11362" other sources use), or TTD’s own TargetID (idType = "ttd_target_id"); for drug → target, a drug name or TTD’s own DrugID (idType = "ttd_drug_id").

The result’s Uniprot_acc column carries a resolved UniProt accession where available (via the package’s UniProt REST scaffold, keyed on GeneName, not on string-mangling the mnemonic) - check the paired uniprot_source column before trusting it: "resolved" means it worked, "TTD_missing" means TTD itself had no usable ID for that target at all (overwhelmingly family/pathway-level “targets” with no single accession by construction, e.g. “Fibroblast growth factor receptor (FGFR)” rather than the single-protein “FGFR1”), and "unresolved"/"resolution_failed" mean resolution was attempted but failed - a real gene symbol with no UniProt mapping, or a transient UniProt outage, respectively.

ttd_t2d <- ttdTargetAnnot(
    list(molType = "protein", idType = "symbol", ids = "FGFR1"), ttdDbPath)
head(ttd_t2d[, c("DrugName", "Highest_status", "MOA", "Uniprot_acc", "molecule_type")])
##                 DrugName Highest_status       MOA Uniprot_acc  molecule_type
## 1                KW-2449        Phase 1      <NA>      P11362 small molecule
## 2                MK-2461      Phase 1/2 Inhibitor      P11362 small molecule
## 3 Anti-FGFR1 mab program  Investigative      <NA>      P11362       antibody
## 4             SAR-106881  Investigative   Agonist      P11362          other
## 5            Romiplostim       Approved Inhibitor      P11362 small molecule
## 6                 E-3810        Phase 3 Inhibitor      P11362 small molecule
ttd_d2t <- ttdTargetAnnot(
    list(molType = "cmp", idType = "name", ids = "Pemigatinib"), ttdDbPath)
ttd_d2t[, c("GeneName", "Highest_status", "MOA", "PubChem_CID", "Indication")]
##   GeneName Highest_status       MOA PubChem_CID
## 1    FGFR1       Approved Inhibitor    86705695
## 2    FGFR3       Approved Inhibitor    86705695
## 3    FGFR2       Approved Inhibitor    86705695
##                                                                                                            Indication
## 1 Cholangiocarcinoma [2C12.10]: Approved; Myeloproliferative syndrome [2A22]: Phase 2; Bladder cancer [2C94]: Phase 2
## 2 Cholangiocarcinoma [2C12.10]: Approved; Myeloproliferative syndrome [2A22]: Phase 2; Bladder cancer [2C94]: Phase 2
## 3 Cholangiocarcinoma [2C12.10]: Approved; Myeloproliferative syndrome [2A22]: Phase 2; Bladder cancer [2C94]: Phase 2

Note Indication packs every disease Pemigatinib is associated with, each with its own clinical status (e.g. approved for cholangiocarcinoma but only Phase 2 for bladder cancer) - richer than the single Highest_status column, which only ever reflects the drug’s single most-advanced status across all diseases combined.

molecule_type is a derived classification - TTD’s own DRUGTYPE field where available, a SMILES-presence/name heuristic otherwise (see ?buildTtdDb) - not an authoritative structural determination. Useful for filtering before a downstream cheminformatics step (e.g. keep only "small molecule" rows before computing descriptors), but not something to trust for anything decision-critical without checking Smiles directly.

Like the four REST-backed sources, ttdTargetAnnot() supports fields = "core"/"all"/<vector> (see Requesting Additional Fields) - though since ttd_interactions is one flat local table with nothing larger to opt into, "core" and "all" return the same full column set here; the option mainly exists to narrow down to a specific subset:

listDrugTargetFields("ttd")
##  [1] "QueryIDs"       "TargetID"       "GeneName"       "Uniprot"        "TargetType"    
##  [6] "DrugID"         "DrugName"       "Smiles"         "Highest_status" "MOA"           
## [11] "Uniprot_acc"    "uniprot_source" "molecule_type"  "PubChem_CID"    "PubChem_SID"   
## [16] "CAS"            "ChEBI_ID"       "Indication"
ttdTargetAnnot(
    list(molType = "protein", idType = "symbol", ids = "FGFR1"), ttdDbPath,
    fields = c("DrugName", "CAS", "ChEBI_ID"))[1:3, ]
##   QueryIDs               DrugName         CAS    ChEBI_ID
## 1    FGFR1                KW-2449 841258-76-2 CHEBI:91441
## 2    FGFR1                MK-2461 917879-39-1        <NA>
## 3    FGFR1 Anti-FGFR1 mab program        <NA>        <NA>

Every downloaded TTD file’s own release stamp (version and date - TTD does not always move these together across files, see ?buildTtdDb) is attached to ttdTargetAnnot()’s result as attr(result, "ttd_release"):

attr(ttd_t2d, "ttd_release")
##          file version       date
## 1     targets 10.1.01 2024.01.10
## 2       drugs 10.1.01 2024.01.10
## 3     mapping    <NA>       <NA>
## 4  crossmatch 10.1.01 2024.01.10
## 5 drugDisease 10.1.01 2024.03.30

Distribution note: buildTtdDb() only ever downloads TTD’s own files into your local cache and builds the SQLite there - the package itself ships no TTD data (TTD’s license permits academic use but has no explicit redistribution grant). The cross-matching columns (PubChem_CID, PubChem_SID, CAS, ChEBI_ID) are TTD’s own; this release’s cross-matching file does not carry ChEMBL_ID, DrugBank_ID, or InChIKey at all (confirmed by reading the raw file, not just its header) - joining to those needs a separate hop through getUnichemMapping()/buildUnichemDb() via PubChem_CID, not this function.

Broad Repurposing Hub (CLUE)

Like TTD, the Broad Institute Drug Repurposing Hub has no live API - two flat TSV files (drug-level and physical-sample-level annotation) are downloaded once and assembled into a small local SQLite via buildBroadRepurposingHubDb(); broadRepurposingHubAnnot() then queries that local database repeatedly.

brhDbPath <- buildBroadRepurposingHubDb(rerun = TRUE)  # first build must fetch; rerun = FALSE reuses the cache afterward

Native identifiers for broadRepurposingHubAnnot(): for target → drug, a gene symbol (idType = "symbol" - the Repurposing Hub only exposes gene symbols, no accession system of its own); for drug → target, a drug name (idType = "name", matched case-insensitively against the Hub’s own lower-case pert_iname) or a specific physical sample/batch ID (idType = "broad_id").

broad_t2d <- broadRepurposingHubAnnot(
    list(molType = "protein", idType = "symbol", ids = "FGFR1"), brhDbPath)
head(broad_t2d[, c("pert_iname", "clinical_phase", "moa")])
##           pert_iname  clinical_phase                                                        moa
## 1            AZD4547 Phase 2/Phase 3                                             FGFR inhibitor
## 2           brivanib         Phase 3                           FGFR inhibitor | VEGFR inhibitor
## 3 brivanib-alaninate         Phase 3                           FGFR inhibitor | VEGFR inhibitor
## 4         CH-5183284         Phase 2                         fibroblast growth factor inhibitor
## 5         danusertib         Phase 2 Aurora kinase inhibitor | growth factor receptor inhibitor
## 6       derazantinib         Phase 2                                  tyrosine kinase inhibitor
broad_d2t <- broadRepurposingHubAnnot(
    list(molType = "cmp", idType = "name", ids = "pemigatinib"), brhDbPath)
broad_d2t[, c("target_gene", "clinical_phase", "moa")]
##   target_gene clinical_phase            moa
## 1       FGFR1       Launched fgfr inhibitor
## 2       FGFR2       Launched fgfr inhibitor
## 3       FGFR3       Launched fgfr inhibitor

Distribution note: buildBroadRepurposingHubDb() only ever downloads the Repurposing Hub’s own files into your local cache and builds the SQLite there - the package itself ships no Repurposing Hub data. Its license is more restrictive than TTD’s: the source files explicitly state they are “provided for non-commercial use only.”

Two tables, not one: unlike TTD, the Repurposing Hub’s sample-level file has a genuinely different grain than a drug-target edge - one compound routinely has several physical samples/lots. broad_interactions (what broadRepurposingHubAnnot() returns above) is kept strictly at one row per (pert_iname, target_gene); physical-sample/QC metadata (purity, vendor, catalog number, etc.) lives separately in broad_samples, queryable directly by broad_id. A structure_ambiguous column flags the rare case (~2% of compounds) where one pert_iname display name actually covers more than one distinct chemical structure (different salts/stereoisomers) - smiles/InChIKey/pubchem_cid are then "; "-joined rather than picking one arbitrarily; structure-sensitive work should key on InChIKey, not name, for those rows.

Known operational caveat, worked around automatically: repo-hub.broadinstitute.org has been observed serving an incomplete TLS certificate chain (missing the InCommon intermediate certificate). Browsers usually mask this by auto-fetching the missing intermediate; curl/R’s default download methods generally do not. This is a server-side issue, not something fixable at the source - but downloadBroadRepurposingHub() now retries automatically with a CA bundle extended to include the one missing (legitimate, publicly-issued) certificate, so a plain call should succeed regardless.

GtoPdb (IUPHAR/BPS Guide to PHARMACOLOGY)

GtoPdb offers a documented REST API, but its per-target endpoints are one-resource-per-call with no bulk target-to-gene-symbol mapping endpoint - impractical for a genome-wide build. Instead, buildGtoPdbDb() uses GtoPdb’s bulk /services/interactions endpoint (one call returns the whole ~24,000-row interaction table as JSON) joined locally against a small bulk flat file GtoPdb separately publishes for target-ID-to-HGNC mapping, assembled into a small local SQLite - the same build-once, query-many pattern as TTD and the Repurposing Hub, just sourced from REST plus one small file instead of pure flat files.

gtoPdbDbPath <- buildGtoPdbDb(rerun = TRUE)  # first build must fetch; rerun = FALSE reuses the cache afterward

Native identifiers for gtoPdbTargetAnnot(): for target → drug, a gene symbol (idType = "symbol") or GtoPdb’s own target ID (idType = "gtp_target_id"); for drug → target, a ligand name (idType = "name") or GtoPdb’s own ligand ID (idType = "gtp_ligand_id"). Interactions are pre-filtered to species == "Human" at build time.

gtopdb_t2d <- gtoPdbTargetAnnot(
    list(molType = "protein", idType = "symbol", ids = "FGFR1"), gtoPdbDbPath)
head(gtopdb_t2d[, c("ligandName", "type", "action", "affinity")])
##                     ligandName      type     action  affinity
## 1                    dovitinib Inhibitor Inhibition 8.0 - 8.1
## 2                 dabogratinib Inhibitor Inhibition       6.6
## 3                   pexmetinib Inhibitor Inhibition       7.6
## 4                 infigratinib Inhibitor Inhibition       9.1
## 5                    orantinib Inhibitor Inhibition       5.7
## 6 compound 2c [PMID: 24900538] Inhibitor Inhibition       8.1
gtopdb_d2t <- gtoPdbTargetAnnot(
    list(molType = "cmp", idType = "name", ids = "pemigatinib"), gtoPdbDbPath)
gtopdb_d2t[, c("target_gene", "type", "action", "affinity")]
##   target_gene      type     action affinity
## 1       FGFR1 Inhibitor Inhibition      7.0
## 2       FGFR2 Inhibitor Inhibition      7.0
## 3       FGFR3 Inhibitor Inhibition      7.0

GtoPdb is a more targeted, expert-curated resource than TTD/the Repurposing Hub, not exhaustive - of this vignette’s 8 target genes, only FGFR1 and NLRP3 are covered.

Distribution note: unlike TTD and the Repurposing Hub, GtoPdb’s data is licensed under the Open Data Commons Open Database License (ODbL) with contents under CC-BY-SA 4.0 - clear terms that explicitly permit redistribution (with attribution/share-alike). buildGtoPdbDb() still only ever downloads into your local cache and builds the SQLite there, matching the other local-SQLite sources’ posture for consistency, though that default isn’t a licensing requirement here the way it is for TTD/ the Repurposing Hub.

Bioassay Queries

Everything in Data Sources in Detail returns curated drug-target annotations - a database’s own expert-curated call that a drug acts on a target via some mechanism (ChEMBL’s drug_mechanism, DGIdb’s aggregated interactions, Open Targets’ mechanismsOfAction, TTD’s target-drug mappings). That’s a genuinely different kind of data from a raw bioassay measurement - one experimental result, e.g. “this compound inhibited this target with an IC50 of 42nM in this specific assay” - with no claim about mechanism or curation attached. PubChem’s REST API only ever returns the latter, and ChEMBL exposes both kinds side by side. To keep the two from being silently conflated, this package always keeps them in separate functions and never combines them: queryDrugTargets()/combineDrugTargets() (see Cross-Source Queries) only ever touch the four annotation sources - PubChem is not one of their sources.

ChEMBL

getChemblBioassay() is the bioassay-track sibling of getChemblDrugTarget(): same bidirectional queryBy interface (UniProt accession for target → drug, ChEMBL molecule ID for drug → target), but pulling from ChEMBL’s activity resource instead of drug_mechanism.

chembl_ba_t2d <- getChemblBioassay(
    list(molType = "protein", idType = "Uniprot", ids = "P11362"),  # FGFR1
    standardType = "IC50")
head(chembl_ba_t2d[, c("Drug_Name", "standard_value", "standard_units")])
##   Drug_Name standard_value standard_units
## 1      <NA>             NA           <NA>
## 2      <NA>             42             nM
## 3      <NA>          10000             nM
## 4      <NA>          50000             nM
## 5      <NA>          50000             nM
## 6      <NA>           1520             nM
chembl_ba_d2t <- getChemblBioassay(
    list(molType = "cmp", idType = "chembl_id", ids = "CHEMBL1421"))  # dasatinib
head(chembl_ba_d2t[, c("UniProt_ID", "standard_type", "standard_value")])
##   UniProt_ID standard_type standard_value
## 1     P34152    Inhibition             NA
## 2       <NA>          GI50          15.00
## 3       <NA>          GI80       50000.00
## 4       <NA>          IC50             NA
## 5     O60885      Delta TM          -1.33
## 6     P06241            Kd           4.00

A single-target convenience helper, getChemblBioactivities(), is also available for a quick one-target lookup without the batching/direction machinery. getChemblBioassay() also supports the same fields = "core"/"all"/<vector> mechanism as the annotation functions (see Requesting Additional Fields) - use listBioassayFields("chembl") to browse the extra columns available:

listBioassayFields("chembl")[1:10]
##  [1] "QueryIDs"          "chembl_id"         "Drug_Name"         "ChEMBL_TID"       
##  [5] "UniProt_ID"        "Organism"          "Desc"              "assay_chembl_id"  
##  [9] "assay_description" "standard_type"

PubChem

PubChem’s bioactivity data is queried through its PUG-REST API and NCBI E-utilities via getPubchemDrugTarget(). Native identifiers: a gene symbol (an NCBI GeneID also passes through unresolved) for target → drug, a compound name (a PubChem CID also passes through unresolved) for drug → target.

pubchem_t2d <- getPubchemDrugTarget(
    list(molType = "gene", idType = "symbol", ids = "KLB"))
head(pubchem_t2d[, c("drug_name", "activity_name", "activity_value_uM")])
##   drug_name activity_name activity_value_uM
## 1      <NA>          <NA>              <NA>
pubchem_d2t <- getPubchemDrugTarget(
    list(molType = "cmp", idType = "name", ids = "aspirin"))
pubchem_d2t[, c("gene_symbol", "activity_name", "activity_value_uM")]
##   gene_symbol activity_name activity_value_uM
## 1       NAPRT            Ki            0.0005
## 2       PTGS1          IC50            0.3500
## 3       PTGS2          IC50            2.4000
## 4       ITGB3          IC50            5.0000
## 5      ITGA2B          IC50            5.0000
## 6      CYP2D6       Potency           15.4871
## 7      CYP3A7       Potency           15.4871
## 8      CYP1A2       Potency           24.5454
## 9     CYP2C19       Potency           79.1862

PubChem is compound-centric and high-volume; results are capped per gene (maxCids, default 400) and filtered to Active, numeric, potency-endpoint measurements (IC50/Ki/Kd/EC50/AC50/Potency) by default. Because compound-centric assay data spans whatever species each assay used (e.g. aspirin’s classic COX1/COX2 potency assays are annotated against sheep GeneIDs in PubChem), getPubchemTargets()/getPubchemDrugTarget() filter to human (taxid = 9606) by default - pass taxid = NULL for all species. Like ChEMBL’s bioassay function, fields = "core"/"all"/<vector> and listBioassayFields("pubchem") are available here too.

ID Translation Layer

Every source function above expects that source’s native identifier type. The functions in this section translate between identifier types directly, and are also what queryDrugTargets() uses internally.

Protein/Gene IDs

getUniprotMapping() maps identifiers via UniProt’s REST ID Mapping service. from/to use UniProt’s own database-name vocabulary (see UniProt’s ID Mapping help page for the full list) - a few of the most useful:

getUniprotMapping(c("FGFR1", "KLB"), from = "Gene_Name", to = "UniProtKB-Swiss-Prot")
##    From     To
## 1 FGFR1 P11362
## 2   KLB Q86Z14
getUniprotMapping("P11362", from = "UniProtKB_AC-ID", to = "Ensembl")
##     From                 To
## 1 P11362 ENSG00000077782.24

taxId restricts matches to one organism (default 9606 = human) - important because a gene symbol can match entries from several species.

Paralogs and Orthologs

getEnsemblParalogs()/getEnsemblOrthologs() query Ensembl’s homology REST API. Their type classification (ortholog_one2one, other_paralog, etc.) comes from gene-tree reconciliation, not raw sequence-identity thresholding, so it is a more principled “best hit” signal than picking the highest perc_id alone - prefer type == "ortholog_one2one" when you need a single best cross-species match.

getEnsemblParalogs("NLRP3")[, c("homolog_id", "type", "perc_id")]
##         homolog_id          type  perc_id
## 1  ENSG00000167984 other_paralog 20.65730
## 2  ENSG00000179583 other_paralog 14.86730
## 3  ENSG00000158077 other_paralog 34.30920
## 4  ENSG00000253548 other_paralog  8.24742
## 5  ENSG00000022556 other_paralog 21.75140
## 6  ENSG00000167207 other_paralog 19.54590
## 7  ENSG00000185792 other_paralog 32.59330
## 8  ENSG00000179709 other_paralog 28.43510
## 9  ENSG00000142405 other_paralog 46.55980
## 10 ENSG00000167634 other_paralog 22.08290
## 11 ENSG00000140853 other_paralog 12.70100
## 12 ENSG00000091592 other_paralog 17.65110
## 13 ENSG00000182261 other_paralog 29.77100
## 14 ENSG00000160703 other_paralog 16.41030
## 15 ENSG00000106100 other_paralog 18.99270
## 16 ENSG00000171487 other_paralog 25.67450
## 17 ENSG00000179873 other_paralog 20.23230
## 18 ENSG00000173572 other_paralog 29.53020
## 19 ENSG00000174885 other_paralog 25.92590
## 20 ENSG00000160505 other_paralog 33.09860
getEnsemblOrthologs("NLRP3", targetSpecies = "mouse")[
    , c("homolog_id", "homolog_species", "type", "perc_id")]
##           homolog_id homolog_species             type perc_id
## 1 ENSMUSG00000032691    mus_musculus ortholog_one2one  82.575

Large gene families need the default query shape here - by design, getEnsemblParalogs()/getEnsemblOrthologs() already avoid a pathological slowdown found for such families (see ?.dot-dtiEnsemblHomologyFetch for details); pass condensed = TRUE for an even faster existence-check-only path when you don’t need perc_id/perc_pos.

Compound IDs

getUnichemMapping() translates compound identifiers via a local SQLite built from UniChem’s bulk compound cross-reference table (buildUnichemDb()). It is genuinely source-agnostic: from/to accept any source name present in the underlying data (not a fixed list hand-coded per database) - "chembl", "pubchem", "drugbank", "chebi", and dozens more.

## One-time setup (not run automatically in this vignette - takes on the
## order of an hour and downloads ~1.5GB). Rerun = FALSE afterward always
## reuses whatever was built, regardless of which day that was.
unichemDbPath <- buildUnichemDb()
unichemDbPath <- buildUnichemDb(rerun = FALSE)
getUnichemMapping("CHEMBL25", from = "chembl", to = "pubchem", unichemDbPath)   # aspirin -> CID
getUnichemMapping("CHEMBL25", from = "chembl", to = "drugbank", unichemDbPath) # aspirin -> DrugBank ID

Unlike UniProt’s or Ensembl’s REST APIs, UniChem’s own live per-compound API does not support batch queries and was found to be intermittently unreliable for even single lookups - the local-SQLite approach exists specifically to avoid depending on it (see ?buildUnichemDb). Because the build is a real, deliberate operation (not something to trigger silently), buildUnichemDb(rerun = FALSE) is always required explicitly before using getUnichemMapping() or queryDrugTargets() on structured compound IDs.

Cross-Source Queries

Querying One or All Sources

queryDrugTargets() resolves queryBy$ids to whatever native identifier each requested sources entry needs, then dispatches. queryBy$idType uses the same canonical vocabulary as the translation layer above - not each source’s own native type (see the table in Data Sources in Detail for those):

molType idType Example ids
"gene"/"protein" "symbol" "FGFR1"
"gene"/"protein" "uniprot" "P11362" (FGFR1)
"gene"/"protein" "ensembl" "ENSG00000077782" (FGFR1)
"cmp" "name" "aspirin"
"cmp" "chembl_id" "CHEMBL25" (aspirin)
"cmp" "pubchem_id" PubChem CID
"cmp" "drugbank_id" "DB00945" (aspirin)
"cmp" "chebi_id" ChEBI ID

Compound-side resolution needs a local UniChem SQLite (unichemDbPath, see Compound IDs) for most idType != to combinations, except three zero-network-database shortcuts: idType/to already identical (a passthrough, including "name" -> "name"), "chembl_id" -> "name" (direct via getChemblMolecule()), and "name" -> "pubchem_id" (direct via PubChem’s own name search). Every other combination needs it, including "name" -> any structured type other than "pubchem_id" (an extra hop past the PubChem CID) and any structured type other than "chembl_id" -> "name" (an extra hop through a ChEMBL ID first). Gene/protein-side resolution never needs a local database - it always goes through UniProt’s REST API.

res_one <- queryDrugTargets(
    list(molType = "gene", idType = "symbol", ids = "FGFR1"), sources = "chembl")
names(res_one)
## [1] "chembl"
## Starting from an Ensembl gene ID instead of a symbol - resolved
## automatically (via a UniProt accession as an intermediate hop, since
## UniProt's mapping API requires one side of any call to be UniProtKB).
res_ensembl <- queryDrugTargets(
    list(molType = "gene", idType = "ensembl", ids = "ENSG00000077782"),
    sources = c("chembl", "opentargets"))
sapply(res_ensembl, nrow)
##      chembl opentargets 
##          26          94
## TTD, Broad Repurposing Hub and GtoPdb each need a local db path (see
## above); other sources ignore the ones they don't use.
res_all_live <- queryDrugTargets(
    list(molType = "gene", idType = "symbol", ids = "FGFR1"),
    sources = c("chembl", "dgidb", "opentargets", "ttd", "broad", "gtopdb"),
    ttdDbPath = ttdDbPath, brhDbPath = brhDbPath, gtoPdbDbPath = gtoPdbDbPath)
sapply(res_all_live, nrow)
##      chembl       dgidb opentargets         ttd       broad      gtopdb 
##          26         143          94          37          37          47

Starting from a compound identifier that needs the UniChem SQLite works the same way once one has been built:

res_drugbank <- queryDrugTargets(
    list(molType = "cmp", idType = "drugbank_id", ids = "DB00945"),  # aspirin
    sources = "chembl", unichemDbPath = unichemDbPath)
res_drugbank$chembl[, c("QueryIDs", "UniProt_ID", "Organism")]

A source that a query resolved nothing for, or whose own call failed (e.g. a required local database path was not supplied), simply contributes no element to the result list rather than raising an error - set verbose = TRUE on any of the calls above to see why.

Performance Considerations

None of the seven sources support unlimited-size ID batches in a single request, and the live APIs are courtesy-throttled client-side regardless. The defaults already reflect each source’s real constraints (do not raise these without a specific reason):

Source Constraint Default batch size
ChEMBL REST server request-line length (~4KB; httr2 percent-encodes separators) 200 IDs/request
PubChem Compound-property lookup batch 100 CIDs/request; bioactivity summary 50 CIDs/request
DGIdb Courtesy chunking (no documented server limit found) 300 names/request
Open Targets GraphQL aliasing payload size 25 targets/request
UniProt ID Mapping Server-enforced job size cap up to 100,000 IDs/job (asynchronous)
UniChem No batching in the live API at all - hence the local-SQLite design n/a (local queries)

Every per-source function chunks large ids vectors into these batch sizes automatically - including queryDrugTargets(), for the six annotation sources it dispatches to (see Bioassay Queries for why PubChem isn’t one of them) - so a query of, say, 2,000 UniProt accessions works the same as one - it just takes proportionally longer and issues proportionally more requests. When testing or debugging with a deliberately small chunkSize, pick one relative to the endpoint’s fan-out, not arbitrarily small: a tiny chunkSize against a low-fan-out endpoint (e.g. ChEMBL’s molecule lookup, one record per ID) is a cheap way to exercise pagination, but the same tiny chunkSize against a high-fan-out join (e.g. ChEMBL’s target/mechanism join, where a handful of UniProt accessions can expand into 100+ drug rows) multiplies into far more live requests than intended.

Combining Results

combineDrugTargets() row-binds a small set of canonical columns (query_id, gene_symbol, drug_name, action, source by default) across whichever sources are present in a queryDrugTargets() result, mapping each source’s own column names to this shared vocabulary:

combined <- combineDrugTargets(res_all_live)
table(combined$source)
## 
## Broad Repurposing Hub                ChEMBL                 DGIdb                GtoPdb 
##                    37                    26                   143                    47 
##           OpenTargets                   TTD 
##                    94                    37
head(combined)
##   query_id gene_symbol               drug_name    action source
## 1    FGFR1        <NA> PAZOPANIB HYDROCHLORIDE INHIBITOR ChEMBL
## 2    FGFR1        <NA>  INFIGRATINIB PHOSPHATE INHIBITOR ChEMBL
## 3    FGFR1        <NA>            INFIGRATINIB INHIBITOR ChEMBL
## 4    FGFR1        <NA>             REGORAFENIB INHIBITOR ChEMBL
## 5    FGFR1        <NA>                 RG-1530 INHIBITOR ChEMBL
## 6    FGFR1        <NA>               LUCITANIB INHIBITOR ChEMBL

query_id reflects your original query token, not each source’s own (possibly translated) QueryIDs - useful once a query starts from a non-native identifier, as in res_ensembl above:

unique(combineDrugTargets(res_ensembl)$query_id)  # still "ENSG00000077782" for every source
## [1] "ENSG00000077782"

ChEMBL’s REST output has no gene-symbol column at all (it is UniProt-accession-keyed), so gene_symbol is NA for ChEMBL rows by default. Pass resolveGeneSymbol = TRUE to fill it in via one extra getUniprotMapping() call:

combineDrugTargets(res_all_live, resolveGeneSymbol = TRUE) |>
    subset(source == "ChEMBL") |> head(3)
##   query_id gene_symbol               drug_name    action source
## 1    FGFR1       FGFR1 PAZOPANIB HYDROCHLORIDE INHIBITOR ChEMBL
## 2    FGFR1       FGFR1  INFIGRATINIB PHOSPHATE INHIBITOR ChEMBL
## 3    FGFR1       FGFR1            INFIGRATINIB INHIBITOR ChEMBL

This is deliberately just column-name alignment and row append - not deduplication and not cross-source identity resolution (the same compound or target appearing under different native IDs in different sources is not merged into one row). A more principled harmonized container - an S4 class analysed but not yet built, so that different sources’ rows for the literal same compound/target could be properly joined - is a separate, deferred phase. Every source’s full original data remains available unchanged in the list queryDrugTargets() returned, regardless of what you pass to combineDrugTargets().

Genome-Wide Master Table

queryDrugTargets() is designed for on-demand queries of a handful to a few thousand identifiers. A different, common need is a periodically rebuilt, shareable master table covering essentially all human protein-coding genes at once - the natural resource for genome-wide questions like “do disease-associated gene variants have known drugs annotated?” or “do hits from a LINCS perturbation-signature search have known targets/MOAs?”. A single target-centric genome-wide run already answers the reverse drug → target question too: every row already carries whichever drug matched that gene, so filtering the assembled table by drug identifier gives you that direction for free - no separate compound-centric run is needed.

The code in this section is illustrative only (eval=FALSE) - a genome-wide run queries live APIs for ~19,200 genes and can take hours; it has no place executing during vignette or R CMD check builds. Run it directly in an interactive session or a scheduled job instead.

The HGNC Gene Table (the Anchor)

The gene-centric anchor is HGNC’s complete gene set - the single authoritative source for the current approved symbol and every previous/alias symbol a gene has ever had, alongside its Ensembl gene ID, UniProt accession(s), and Entrez ID, all in one row per approved gene (~19,200 of them are protein-coding). getHgncGeneTable() downloads (and caches) it and reshapes the pipe-separated multi-value columns into list-columns:

hgncTable <- getHgncGeneTable()
nrow(hgncTable)
hgncTable[hgncTable$symbol == "FGFR1", c("symbol", "ensembl_gene_id")]
hgncTable$uniprot_ids[hgncTable$symbol == "FGFR1"]

By default this uses a pinned quarterly HGNC snapshot (not the rolling always-latest file) specifically so a master table built today and one built next year start from the same gene list unless you deliberately ask otherwise (current = TRUE for the rolling file, or archiveFile = "..." for any other specific HGNC archive snapshot).

Symbol normalization matters here: DGIdb and TTD echo back whatever gene symbol was current when their own records were curated, and a nontrivial fraction are now outdated relative to HGNC. buildHgncSymbolMap() builds a lookup from every historical prev_symbol/alias_symbol to its gene’s current approved symbol; normalizeGeneSymbols() applies it (flagging any symbol it still can’t resolve, rather than silently dropping it):

symbolMap <- buildHgncSymbolMap(hgncTable)
normalizeGeneSymbols(c("FGFR1", "ABL"), symbolMap = symbolMap, hgncTable = hgncTable)

Building the Table, With Checkpointing

buildGenomeWideDrugTargetTable() loops the four annotation sources’ own bidirectional functions over every gene in an HGNC table, in checkpointed chunks written to outDir as it goes - so an interrupted run resumes from the last completed chunk instead of starting over (rerun = FALSE, the default, skips anything the chunk manifest already marks done):

ttdDbPath <- buildTtdDb(rerun = FALSE)
masterTable <- buildGenomeWideDrugTargetTable(
    hgncTable = hgncTable,
    sources = c("chembl", "dgidb", "opentargets", "ttd"),
    ttdDbPath = ttdDbPath,
    outDir = "genomewide_build",  # persists chunk files + a manifest here
    chunkGenes = 500L)
sapply(masterTable, nrow)

This deliberately covers only the four curated-annotation sources - not ChEMBL’s bioassay function or PubChem (see Bioassay Queries). ChEMBL’s full activity table is tens of millions of rows and PubChem’s accessor is inherently a one-gene-at-a-time loop; a genome-wide sweep of either is a different kind of job, better done from their bulk data downloads than by looping this package’s live-API functions tens of thousands of times.

The result is a named list - one data.frame per source, each row tagged with hgnc_id/symbol/ensembl_gene_id alongside that source’s own native columns (deliberately not passed through combineDrugTargets()’s harmonization, so no information is lost from a resource meant to be reused repeatedly; apply combineDrugTargets() afterward if you want the flattened cross-source view). The assembled result is also cached via BiocFileCache, so a completed build is reusable across sessions and machines without recomputation - exactly the “compute every few months, share the result” workflow this section is meant to support.

Supplement

The functions below predate, and are independent of, the sources and translation layer described above. They remain available as backward-compatible/alternative options but are not the default workflow recommended by this vignette.

Local ChEMBL SQLite Workflow

Before ChEMBL’s REST API was added, this package’s ChEMBL support was exclusively through a downloaded local SQLite instance of the full ChEMBL database, queried via drugTargetAnnot() (and the related getDrugTarget(), which queries a pre-generated flat-file export rather than the SQLite directly). This remains useful for bulk, offline, or very-high-volume ChEMBL querying where the REST API’s batch limits (see Performance Considerations) are impractical.

config <- genConfig(chemblDbPath = "chembldb.db")
downloadChemblDb(rerun = TRUE, config = config)  # downloads the full ChEMBL SQLite (several GB)
queryBy <- list(molType = "protein", idType = "UniProt_ID", ids = "P11362")
drugTargetAnnot(queryBy, config = config)

UniProt.ws-Based ID Mapping

getUniprotIDs() predates getUniprotMapping() (see ID Translation Layer) and is based on the Bioconductor UniProt.ws package rather than a direct REST call. It is kept for backward compatibility; getUniprotMapping() is the recommended path going forward.

getUniprotIDs(taxId = 9606, kt = "ENSEMBL", keys = c("ENSG00000077782"))

biomaRt-Based Paralogs

getParalogs() predates getEnsemblParalogs()/getEnsemblOrthologs() (see ID Translation Layer) and is based on biomaRt’s BioMart interface rather than Ensembl’s REST API directly. It returns within-human paralogs only (no cross-species orthologs). Kept for backward compatibility.

queryBy <- list(molType = "gene", idType = "external_gene_name", ids = "FGFR1")
getParalogs(queryBy)

Static UniChem Mirror

downloadUniChem()/cmpIdMapping() predate buildUnichemDb()/ getUnichemMapping() (see ID Translation Layer) and work from a small, static, pre-computed ChEMBL↔︎{DrugBank, PubChem, ChEBI} cross-reference snapshot hosted on a project S3 bucket, rather than UniChem’s own current bulk data. Kept for backward compatibility and for the local-ChEMBL-SQLite workflow above, which uses it internally via cmpIdMapping().

downloadUniChem(rerun = TRUE)
cmpIdMapping(outfile = "cmp_ids.rds", rerun = TRUE)

Session Info

sessionInfo()
## R version 4.6.1 (2026-06-24)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 26.04 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
##  [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
## [10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Etc/UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] drugTargetInteractions_1.21.4 BiocStyle_2.41.0             
## 
## loaded via a namespace (and not attached):
##   [1] tidyselect_1.2.1            dplyr_1.2.1                 blob_1.3.0                 
##   [4] filelock_1.0.3              Biostrings_2.81.5           bitops_1.0-9               
##   [7] lazyeval_0.2.3              fastmap_1.2.0               RCurl_1.98-1.19            
##  [10] BiocFileCache_3.3.0         GenomicAlignments_1.49.1    XML_3.99-0.23              
##  [13] digest_0.6.39               lifecycle_1.0.5             ProtGenerics_1.45.0        
##  [16] KEGGREST_1.53.5             RSQLite_3.53.3              magrittr_2.0.5             
##  [19] compiler_4.6.1              rlang_1.3.0                 sass_0.4.10                
##  [22] progress_1.2.3              tools_4.6.1                 yaml_2.3.12                
##  [25] rtracklayer_1.73.0          knitr_1.51                  prettyunits_1.2.0          
##  [28] S4Arrays_1.13.0             bit_4.6.0                   curl_7.1.0                 
##  [31] DelayedArray_0.39.3         abind_1.4-8                 BiocParallel_1.47.0        
##  [34] withr_3.0.3                 purrr_1.2.2                 BiocGenerics_0.59.10       
##  [37] sys_3.4.3                   grid_4.6.1                  stats4_4.6.1               
##  [40] biomaRt_2.69.0              SummarizedExperiment_1.43.0 cli_3.6.6                  
##  [43] rmarkdown_2.31              crayon_1.5.3                generics_0.1.4             
##  [46] otel_0.2.0                  httr_1.4.8                  rjson_0.2.23               
##  [49] BiocBaseUtils_1.15.1        readxl_1.5.0                DBI_1.3.0                  
##  [52] cachem_1.1.0                stringr_1.6.0               parallel_4.6.1             
##  [55] AnnotationDbi_1.75.2        cellranger_1.1.0            AnnotationFilter_1.37.0    
##  [58] BiocManager_1.30.27         XVector_0.53.0              restfulr_0.0.17            
##  [61] matrixStats_1.5.0           vctrs_0.7.3                 Matrix_1.7-5               
##  [64] jsonlite_2.0.0              IRanges_2.47.2              hms_1.1.4                  
##  [67] S4Vectors_0.51.5            bit64_4.8.2                 ensembldb_2.37.3           
##  [70] GenomicFeatures_1.65.0      maketools_1.3.2             jquerylib_0.1.4            
##  [73] glue_1.8.1                  codetools_0.2-20            stringi_1.8.7              
##  [76] GenomeInfoDb_1.49.1         GenomicRanges_1.65.1        BiocIO_1.23.3              
##  [79] UCSC.utils_1.9.0            tibble_3.3.1                pillar_1.11.1              
##  [82] rappdirs_0.3.4              htmltools_0.5.9             Seqinfo_1.3.0              
##  [85] R6_2.6.1                    dbplyr_2.6.0                httr2_1.3.0                
##  [88] lattice_0.22-9              evaluate_1.0.5              Biobase_2.73.1             
##  [91] cigarillo_1.3.1             png_0.1-9                   Rsamtools_2.29.0           
##  [94] memoise_2.0.1               bslib_0.11.0                rjsoncons_1.3.3            
##  [97] SparseArray_1.13.2          xfun_0.60                   MatrixGenerics_1.25.0      
## [100] UniProt.ws_2.53.3           buildtools_1.0.0            pkgconfig_2.0.3

References

Bento, A Patrı́cia, Anna Gaulton, Anne Hersey, et al. 2014. The ChEMBL bioactivity database: an update.” Nucleic Acids Res. 42 (Database issue): D1083–90. https://doi.org/10.1093/nar/gkt1031.
Gaulton, Anna, Louisa J Bellis, A Patricia Bento, et al. 2012. ChEMBL: a large-scale bioactivity database for drug discovery.” Nucleic Acids Res. 40 (Database issue): D1100–7. https://doi.org/10.1093/nar/gkr777.
Wu, C H, R Apweiler, A Bairoch, et al. 2006. The Universal Protein Resource (UniProt): an expanding universe of protein information.” Nucleic Acids Res. 34 (Database issue): D187–91. https://doi.org/10.1093/nar/gkj161.