DrugBank describes how a drug acts on the proteins it binds. These
proteins are grouped into four CETT types: Carriers,
Enzymes, Targets and
Transporters. For each drug–protein pair DrugBank
records one or more actions, such as inhibitor,
agonist, antagonist or
substrate.
Since this release, the actions and
polypeptides$general_information tables of every CETT type
include a drugbank_id column. Earlier versions keyed these
tables only by the CETT identifier (for example target_id).
Many drugs bind the same protein, so that key alone could not say
which drug an action belonged to. Identical rows from different
drugs were also collapsed into one.
This vignette shows why the drug link matters with a short pharmacology question.
Lepirudin is a recombinant hirudin and a direct thrombin inhibitor. It was used to anticoagulate patients with heparin-induced thrombocytopenia (HIT), a setting where heparins cannot be given. It was later withdrawn from the market. That leaves a common question for formulary and pharmacovigilance work:
Which other drugs act on the same target with the same action? And what else do they act on, so we can spot differences in their safety profile?
We answer this using the two DrugBank records that ship with
dbparser:
DB00001, LepirudinDB00006, Bivalirudin, a synthetic peptide derived from
hirudinWe parse each record and keep only the drug general information and the CETT sections. In a real analysis you would parse the full DrugBank XML once.
parse_record <- function(file_name) {
parseDrugBank(
db_path = system.file("extdata", file_name, package = "dbparser"),
drug_options = NULL,
parse_salts = FALSE,
parse_products = FALSE,
references_options = NULL,
cett_options = c("targets", "enzymes")
)
}
lepirudin <- parse_record("drugbank_record_biotech.xml")
bivalirudin <- parse_record("drugbank_record_small_molecule.xml")
dbs <- list(lepirudin, bivalirudin)
# Stack the same table from every parsed record
stack_table <- function(getter) {
bind_rows(lapply(dbs, function(db) getter(db)))
}
drugs <- stack_table(function(db) db$drugs$general_information) %>%
select(drugbank_id, drug_name = name, type)
drugs
#> # A tibble: 2 × 3
#> drugbank_id drug_name type
#> <chr> <chr> <chr>
#> 1 DB00001 Lepirudin biotech
#> 2 DB00006 Bivalirudin small moleculetarget_actions <- stack_table(function(db) db$cett$targets$actions)
target_actions
#> # A tibble: 2 × 3
#> action target_id drugbank_id
#> <chr> <chr> <chr>
#> 1 inhibitor BE0000048 DB00001
#> 2 inhibitor BE0000048 DB00006Both drugs inhibit the same target, BE0000048. Each row
now says which drug it comes from.
Suppose we only had target_id, which is what earlier
versions of dbparser gave us. To attach actions to drugs we
would have to join the actions to the target table on
target_id alone:
targets <- stack_table(function(db) db$cett$targets$general_information)
targets %>%
select(drugbank_id, target_id, target_name = name) %>%
inner_join(select(target_actions, -drugbank_id), by = "target_id")
#> # A tibble: 4 × 4
#> drugbank_id target_id target_name action
#> <chr> <chr> <chr> <chr>
#> 1 DB00001 BE0000048 Prothrombin inhibitor
#> 2 DB00001 BE0000048 Prothrombin inhibitor
#> 3 DB00006 BE0000048 Prothrombin inhibitor
#> 4 DB00006 BE0000048 Prothrombin inhibitorThat join gives four rows instead of two. Each drug gets the other
drug’s action as well as its own. Here both actions happen to be
inhibitor, so the mistake is easy to miss. With a
promiscuous target that many drugs act on in different ways, this kind
of join quietly mixes up agonists and antagonists. Joining on both keys
gives the correct result:
drug_target_actions <- targets %>%
select(drugbank_id, target_id, target_name = name, known_action) %>%
inner_join(target_actions, by = c("drugbank_id", "target_id")) %>%
inner_join(drugs, by = "drugbank_id")
drug_target_actions
#> # A tibble: 2 × 7
#> drugbank_id target_id target_name known_action action drug_name type
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 DB00001 BE0000048 Prothrombin yes inhibitor Lepirudin biotech
#> 2 DB00006 BE0000048 Prothrombin yes inhibitor Bivalirudin small mo…The polypeptide table gives each target’s UniProt accession and gene
symbol. It now also carries drugbank_id, so we can join it
on the same two keys. Gene symbols are the usual way into pathway,
expression or genetic-association resources.
target_genes <- stack_table(
function(db) db$cett$targets$polypeptides$general_information) %>%
select(drugbank_id, target_id, uniprot_id = polypeptide_id, gene_name,
cellular_location)
drug_target_genes <- drug_target_actions %>%
inner_join(target_genes, by = c("drugbank_id", "target_id")) %>%
select(drug_name, target_name, gene_name, uniprot_id, action, known_action,
cellular_location)
drug_target_genes
#> # A tibble: 2 × 7
#> drug_name target_name gene_name uniprot_id action known_action
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Lepirudin Prothrombin F2 P00734 inhibitor yes
#> 2 Bivalirudin Prothrombin F2 P00734 inhibitor yes
#> # ℹ 1 more variable: cellular_location <chr>Two drugs with the same primary target can still differ in what else they act on. We stack the target and enzyme actions into one profile per drug. Each action is kept with its own drug, so the comparison is straightforward.
cett_profile <- function(cett_type) {
id_col <- paste0(sub("s$", "", cett_type), "_id")
info <- stack_table(function(db) db$cett[[cett_type]]$general_information)
acts <- stack_table(function(db) db$cett[[cett_type]]$actions)
if (NROW(info) == 0 || NROW(acts) == 0) {
return(NULL)
}
info %>%
select(drugbank_id, cett_id = all_of(id_col), protein = name,
known_action) %>%
inner_join(rename(acts, cett_id = all_of(id_col)),
by = c("drugbank_id", "cett_id")) %>%
mutate(cett_type = cett_type)
}
profile <- bind_rows(cett_profile("targets"), cett_profile("enzymes")) %>%
inner_join(drugs, by = "drugbank_id") %>%
select(drug_name, cett_type, protein, action, known_action)
profile
#> # A tibble: 3 × 5
#> drug_name cett_type protein action known_action
#> <chr> <chr> <chr> <chr> <chr>
#> 1 Lepirudin targets Prothrombin inhibitor yes
#> 2 Bivalirudin targets Prothrombin inhibitor yes
#> 3 Bivalirudin enzymes Myeloperoxidase inhibitor unknownBivalirudin also has an inhibitor action recorded on the
enzyme myeloperoxidase (MPO), but with
known_action = "unknown". This means DrugBank lists the
interaction without a confirmed role in how the drug works. Lepirudin
has no such entry. A reviewer could follow up on this as a hypothesis,
for example by checking the literature or adverse-event data (see
vignette("drugbank_nside")). It is not an established
clinical difference.
actions and
polypeptides$general_information tables for carriers,
enzymes, targets and transporters now include
drugbank_id.drugbank_id
and the CETT id (target_id, enzyme_id, …).
Joining on the CETT id alone can attach one drug’s actions to another
drug.dplyr pipelines
answer mechanism questions such as “which drugs share this target and
action?” across a whole DrugBank release.