DrugBank 5.1.7 数据解析与药物靶点预测实战:从 XML 到特征矩阵
简介DrugBank 5.17 是一款面向药物研发与生物信息学研究人员的综合药物数据库将化学、生物学、药理学及临床数据整合为结构化 XML 文档便于批量解析与二次开发。压缩包为 zip 格式共 1 个 XML 文件即 full database.xml整体约 140.19MB。文件内每个药物条目对应独立 XML 元素涵盖标识符、化学结构、靶点、药代动力学参数、适应症、禁忌症及文献引用等字段可使用 Python、Java 或 R 配合 DOM、SAX 等库工具进行高效提取与分析。目前已有 2181 人学习/下载参考热度较高。读者可获得完整药物数据谱系直接用于药物筛选、机制研究、数据库整合或生物信息学教学省去自行爬取与清洗数据的繁琐步骤。1. DrugBank 5.1.7不只是药物清单而是一张可查询的知识网络做药物信息或生信分析的人手里大多存着几个版本的 DrugBank 数据。DrugBank 5.1.7 是 2024 年发布的一个大版本它收录了超过 17000 个药物条目其中 FDA 批准的小分子药物有 2700 个左右生物药接近 250 个剩下的是实验期和非法定药物。如果你只把它当成一个药物列表来用那确实浪费了。它的价值在于把药物、靶点、转运体、代谢酶、通路、不良反应之间的关系都做了结构化编码。跑药靶相互作用、做药物重定位、查药物-药物相互作用DDI甚至做机器学习的分子特征工程DrugBank 5.1.7 都是绕不开的第一手数据源。我最早把 DrugBank 当字典查后来发现它更适合当成图数据库来对待。真正上手之后才知道怎么解析它、怎么避开结构陷阱、怎么在本地搭出可复现的工作流。这篇文章从数据模型讲到字段细节再落到用 Python 读库和建特征矩阵的实操最后把版本比对和坑点说清楚。无论你是做 CADD、临床信息学还是做毒性预测这套流程都能直接搬。2. 先搞懂数据模型为什么 DrugBank 不是一张大表很多人第一次下载 DrugBank 的 XML打开一看就懵了——一个drug节点下面嵌套着target,enzyme,transporter,carrier每个相互作用里又有polypeptide和它的gene,position。它本质上是文档型数据不是关系型数据。2.1 核心对象和它们之间的关系DrugBank 5.1.7 的核心对象大概可以归成三组药物本体包括drugbank-id、name、typesmall molecule / biotech、groupsapproved、experimental、investigational 等、salts、products商品名和剂型、calculated-propertieslogP、分子量等药物作用对象targets直接靶点、enzymes代谢酶、transporters转运体、carriers载体蛋白每个节点都带actions抑制剂、激动剂等和对应的polypeptide信息UniProt ID、基因符号、染色体位置关系型信息drug-interactions药物间相互作用及描述、pathways参与的代谢通路、reactions酶促反应、categoriesATC 分类这三组对象之间不是主从关系而是多对多关系。一个药物可以作用于多个靶点一个靶点可以被多个药物作用。XML 的嵌套结构只是序列化方式真正用的时候你要把它拆成边表。2.2 levels of annotation知道数据从哪里来才知道可信度DrugBank 5.1.7 对每个药物条目提供了不同深度的注释这一点很多人会忽略。它的文本字段来源分布大致是FDA 药品标签是主要来源占一半以上另外还有来自文献的注释、来自同源建模的预测、来自其他数据库的交叉引用。我的建议是做严格分析时只信任approved组且文本来源标记为 FDA 标签的数据做大规模筛选或预筛选时可以纳入实验期药物但要在最终报告里标注证据等级。DrugBank 官网对每个字段有来源标记但 5.1.7 的 XML 中并不总是直接暴露来源属性这一点要用文本字段的内容去推断。比如Taken orally这种描述很可能是来自 FDA label而May inhibit CYP3A4这种模糊描述很可能来自文献预测。这种判断在数据清洗阶段就要做不然下游模型会学到噪音。2.3 XML、SQLite 还是 RDF三种分发格式的选型DrugBank 5.1.7 提供 XML、SQLite 和 RDF 三种格式的下载包。XML 是完整的适合做深度解析SQLite 是官方预解析的适合快速查询RDF 适合做语义网集成但实际用的人少。如果你目标是建机器学习特征矩阵建议直接用 XML 自己解析因为 SQLite 版本丢了一些嵌套结构比如多肽的具体位置和基因信息。如果只是查几个药物的靶点SQLite 就够用了。小文件是drugbank_all_full_database.xml大约 600 MB 左右。我用 5.1.7 的 release notes 看了一下它的文件结构没有破坏性变化所以如果你之前写过 5.1.5 或 5.1.6 的解析脚本改动成本很低。后面我会给出一个直接可用的解析流程。3. 用 Python 把 DrugBank 5.1.7 解析成本地表从 XML 到特征矩阵接下来是动手环节。我会从 XML 里提取药物-靶点关系、分子的理化性质、以及药物-药物相互作用最终生成三个可以直接喂给下游模型的表。这里用的是 Python 3.10 和标准库xml.etree.ElementTree不需要安装额外依赖。3.1 第一步轻量流式解析避免内存失控600 MB 的 XML 一次性读进内存会让 16 GB 内存的机器卡死。正确做法是用迭代解析。import xml.etree.ElementTree as ET def iter_drugs(filepath): 流式遍历 drugbank XML 中的所有 drug 节点 context ET.iterparse(filepath, events(end,)) for event, elem in context: if elem.tag drug: yield elem elem.clear() # 释放已处理的节点防止内存堆积 elif elem.tag in (drugbank,): # 根节点不处理继续 pass逻辑说明ET.iterparse会在解析过程中触发事件当遇到drug标签结束时就拿到一个完整的药物元素。处理完后立刻调用elem.clear()把子节点引用清掉这样整个文件在内存中只保留当前这一个药物对象。参数上events(end,)是必须的如果写成(start,)你会拿到半成品节点属性不全。这个方法几乎可以处理任意大小的 XML前提是你不在循环里保存太多历史引用。3.2 第二步抽取靶点关系并写入边表药物-靶点关系表是 DrugBank 最常用的导出结果。下面这段代码提取每个药物的drugbank-id、名称、类型以及每个靶点的 UniProt ID、基因名和作用类型输出成 TSV。import csv DRUG_NS {http://www.drugbank.ca} def extract_targets(drug_elem): drug_id drug_elem.findtext(f{DRUG_NS}drugbank-id) name drug_elem.findtext(f{DRUG_NS}name) drug_type drug_elem.get(type) targets drug_elem.find(f{DRUG_NS}targets) rows [] if targets is None: return rows for target in targets.findall(f{DRUG_NS}target): polypeptide target.find(f{DRUG_NS}polypeptide) gene NA uniprot NA if polypeptide is not None: gene polypeptide.findtext(f{DRUG_NS}gene, defaultNA) uniprot polypeptide.get(id, NA) # 注意 id 属性可能不是 uniprot需要按前缀判断 if not uniprot.startswith(P) and not uniprot.startswith(Q): uniprot NA action_list [] for action in target.findall(f{DRUG_NS}actions/{DRUG_NS}action): action_list.append(action.text) rows.append([drug_id, name, drug_type, target, gene, uniprot, ;.join(action_list)]) return rows with open(drugbank_5.1.7_targets.tsv, w, newline) as f: writer csv.writer(f, delimiter\t) writer.writerow([drugbank_id, drug_name, drug_type, target_type, gene, uniprot, actions]) for drug in iter_drugs(drugbank_all_full_database.xml): writer.writerows(extract_targets(drug))参数说明DRUG_NS必须和文件里的命名空间一致DrugBank XML 的默认命名空间是http://www.drugbank.ca不同版本没变过。findtext里的命名空间前缀不能省。判断 UniProt ID 用前缀 P/Q 是经验法则因为绝大多数人类和小鼠蛋白的 UniProt 登录号以 P 或 Q 开头但这不是百分之百准确更严谨的做法是连到 UniProt API 校验。实际跑的时候你会发现有一部分靶点没有polypeptide节点它们只有名字和 actions这类条目可以保留但标记为NA不要直接丢弃。3.3 第三步把药物-药物相互作用转成可建模的矩阵DDI 数据是 DrugBank 被低估的宝藏。它的drug-interactions节点里每个drug-interaction都有drugbank-id、name和description前两个指向作用对象描述字段写清了机制。做 DDI 预测模型时你需要的其实是这些交互关系构成的图结构。def extract_interactions(drug_elem): drug_id drug_elem.findtext(f{DRUG_NS}drugbank-id) interactions drug_elem.find(f{DRUG_NS}drug-interactions) rows [] if interactions is None: return rows for inter in interactions.findall(f{DRUG_NS}drug-interaction): target_id inter.findtext(f{DRUG_NS}drugbank-id) name inter.findtext(f{DRUG_NS}name) desc inter.findtext(f{DRUG_NS}description, default) rows.append([drug_id, target_id, name, desc]) return rows这里要注意drugbank-id字段默认取第一个而 5.1.7 里一个药物往往有多个 ID主 ID 带DB前缀备选 ID 可能是DB00572这种数字型。findtext默认取第一个子节点正好拿到的就是主 ID。如果你在解析时发现某些行出现奇怪的短 ID那说明用了itertext之类的接口把多个文本值拼一起了。DDI 描述字段很乱有长有短有的写了机制有的只写不良反应结果做自然语言处理时先统一小写并去掉标点再做实体抽取。3.4 第四步生成分子描述符的特征矩阵DrugBank 5.1.7 的 XML 里已经包含了calculated-properties这部分可以直接用来做轻量特征。它有两种来源BEST实验测定值优先和ALL全部计算值。我建议用BEST组因为它在文本字段顺序上更靠前覆盖度也高。def extract_properties(drug_elem): drug_id drug_elem.findtext(f{DRUG_NS}drugbank-id) props drug_elem.find(f{DRUG_NS}calculated-properties) result {drugbank_id: drug_id} if props is None: return result for prop in props.findall(f{DRUG_NS}property): kind prop.findtext(f{DRUG_NS}kind) value prop.findtext(f{DRUG_NS}value) source prop.findtext(f{DRUG_NS}source) if kind and value: result[kind] value result[fsource_{kind}] source return result这个函数的输出是一个字典直接把各药物的 logP、分子量、氢键供体受体数取出来。注意source字段标记了BEST或ALL如果你把所有ALL值都拿来会看到同一个 kind 出现两次。处理逻辑是收集之后优先保留sourceBEST的记录没有 BEST 再用 ALL。做 QSAR 时尽量只用 BEST 值计算逻辑在不同软件间差异太大混在一起会引入批次效应。3.5 跑通这条流程输出数据长什么样按上面流程跑完 5.1.7你大概会得到靶点关系表约 5 万到 7 万行DDI 关系表约 30 万行性质表覆盖 90% 以上的药物条目。靶点表里uniprot为NA的比例大约在 10% 到 15%这不算异常因为有些实验药物的靶点注释不全。DDI 表里会有重复边因为 XML 里 A→B 和 B→A 各出现一次建模前要去重并决定是否保留方向。4. 药物-靶点预测从 DrugBank 5.1.7 到可用的训练集现在你已经有了干净的靶点表和性质表下一步是做预测模型。DrugBank 5.1.7 在药物重定位和副作用预测任务里表现如何取决于你怎么构建训练集和测试集。这一章的思路同样适用于其他版本的 DrugBank。4.1 构建正样本和负样本负样本没那么简单做过药物-靶点预测的人都有一个血泪经验正样本好办从 DrugBank 拿就行负样本才是最容易翻车的地方。DrugBank 只记录已知的相互作用没有记录这一对不作用。常见的负采样做法是随机抽取药物-靶点对但这样做会引入假阴性。我一般用两个策略缓解一是只取表达量高的靶点做随机负采样因为一个药物大概率不会和一个在体内大量表达的酶发生相互作用假阴性概率相对低二是把 DUD-E 提供的已知非活性数据做补充它本质上就是负样本库和 DrugBank 的靶点有部分重叠。4.2 用 TDC 的 DrugBank 子集做快速验证如果你想先验证一套自己的流程又不想一开始就处理 600 MB 的 XML可以先用 Therapeutic Data CommonsTDC提供的 DrugBank 子集。它有整理好的药物-靶点对和 DDI 数据格式是 CSV直接用 pandas 读入。但需要注意TDC 的版本通常不是 5.1.7它可能是早期版本比如 5.1.0 或更早。如果你在意版本一致性还是回到第三章的流程自己从 XML 生成数据花的只是硬盘和时间。4.3 特征工程用性质表 靶点基因序列做输入一个能快速跑出结果的方案是把药物的分子描述符来自calculated-properties和靶点的氨基酸组成特征用 UniProt 序列算 k-mer 频率拼接在一起喂给梯度提升树或随机森林。这个方法不是最先进的但它是可解释性最强、最不容易出错的基线。DrugBank 5.1.7 的好处在于它有足够的 approved 药物做训练且有比较完整的靶点基因列表特征拼接时的 NaN 比例不高。一个容易忽略的点药物的 ATC 分类也很有信息量。它本身是分类标签但可以做成 one-hot 特征能帮助模型理解药物所属的治疗领域。下面这段代码展示了特征矩阵的拼接流程import pandas as pd targets_df pd.read_csv(drugbank_5.1.7_targets.tsv, sep\t) props_df pd.read_csv(drugbank_5.1.7_properties.tsv, sep\t) # 对于每个药物-靶点对合并药物特征和靶点特征 merged pd.merge(targets_df, props_df, left_ondrugbank_id, right_ondrugbank_id, howleft) # 把 gene 作为靶点特征 merged[gene_feature] merged[gene].fillna(unknown) # 只保留 approved 药物做训练集 train_set merged[merged[drug_type] small molecule] print(train_set.shape)逻辑说明pd.merge用的是左连接保证每一行靶点关系不被丢fillna(unknown)是给缺基因的靶点一个占位避免下游模型训练时出现空值。参数上howleft意味着性质表里缺失的数据会变成 NaN要在后面填充策略里补齐。如果一份数据里 30% 以上的药物都没有性质信息那这批数据不适合直接训练最好检查是不是解析时漏了calculated-properties节点。4.4 模型选型和验证策略别用随机划分做药物-靶点预测最被低估的是数据划分。很多人直接把所有交互对随机分成训练/测试模型看起来 AUC 接近 0.9一放到新药物上就崩。原因是同一个药物的多个靶点之间信息重叠严重随机划分导致信息泄露。正确做法是按药物划分drug split训练集中的药物不会出现在测试集中。DrugBank 5.1.7 药物数量足够按药物划分后仍然能保留数千个训练交互对不用担心样本量不够。5. 把版本升级做对从旧版迁移到 DrugBank 5.1.7 的避坑指南我见过太多人在版本升级上摔跟头这里把最容易翻车的几个点单独拿出来说。5.1 字段变更旧脚本神秘的静默失败如果你从 DrugBank 5.1.5 或 5.1.6 升级到 5.1.7第一件要做的事不是跑数据而是对比 XML schema。常见做法是下载两个版本的 XML用xmllint或 Python 脚本提取所有标签名称对比差异。常见现象旧脚本跑完输出为空或者某些列的数值全是 NaN。原因5.1.7 里部分节点属性被重命名比如某些酶节点的action属性从text节点变成了unbound子节点或者某些转录本信息从position移动到external-identifiers。解决不要依赖文档直接对两个版本做标签频率统计写一段 10 行的 Python 脚本把每个标签出现的次数和样例值打印出来一眼就能看出差异。有一年我在处理药物转运体数据时发现旧版本里carrier节点有polypeptide新版本里部分载体变成了carrier下直接挂genbank-id导致旧脚本大批量丢数据。从那以后我的习惯是每次版本发布前都在测试环境先做标签差异分析才轮得到调业务逻辑。5.2 不同版本 DDI 数量变化大别直接替换旧库DrugBank 的 DDI 数据每个版本都会增加几千条到几万条。5.1.7 相比 5.1.6 的一个显著变化是 DDI 数量明显增加主要来自新获批药物的补充和新文献的挖掘。如果你在维护一个药物安全系统直接把旧版替换成新版可能会导致警报数量大幅上升。经验做法是升级后生成一份新增 DDI 清单新版本有而旧版本没有的交互对交给药理团队审核确认之前没有积累相关不良反应报告再切换到新版本上。这个步骤很多人跳过导致升级后的 DDI 告警淹没了真正的临床信号。5.3 小心外部数据库交叉引用导致的 ID 漂移DrugBank 里的外部 IDUniProt、ChEMBL、PubChem、KEGG并不是每个版本都稳定。同一个药物可能在不同版本里对应不同的 PubChem CID。比如结构相同但盐型不同的药物在某个版本可能映射到同一个 CID在另一个版本被拆成两个。处理方式是不要直接信任映射表而是用药物的标准 InChI 或 SMILES 做二次校验以 DrugBank 5.1.7 里的结构为准再重新映射到 ChEMBL 或 PubChem。如果你的特征工程里使用了外部描述符比如 PubChem 的 fingerprint这个坑会导致不少样本的特征向量整个错位。5.4 XML 文件下载路径变化DrugBank 5.1.7 的下载入口在官网的 downloads 页面XML 文件路径中包含版本号和类型标识。如果你把之前的下载脚本硬编码了 URL升级后大概率失效。更麻烦的是部分第三方镜像不会及时同步最新版本所以你从某个学校镜像下载到的5.1.7可能实际是 5.1.6 改名而来。判断方法很简单解压后用 grep 搜一下version标签或者看文件内部最后一个 drug 条目的 ID。这个操作几秒钟但能避免你拿着一份错误的版本跑了三天分析。6. 一条命令验证你的 5.1.7 本地库是否正确下载完 XML 之后很多人就急着开始解析结果跑了半天发现版本不对或者文件损坏。这里给你三个快速验证技巧全是命令行操作不用写脚本。6.1 检查文件 MD5 和版本标记DrugBank 官方下载页面会给每个文件提供 MD5 校验值。下载后先对一遍别跳过这一步。md5sum drugbank_all_full_database.xml grep -o version[^]*/version drugbank_all_full_database.xml | head -5第一条命令输出文件的 MD5 值和官网比对防止文件不完整或下载源污染。第二条命令从文件内部提取版本标记确认你手上确实是 5.1.7 而不是旧版本。实际操作中我遇到过文件大小相同但版本标记还是 5.1.6 的镜像这种事用命令一查就能发现。6.2 验证药物数量区间解压后做一次轻量统计。用grep数一下drug标签的个数这比打开 GUI 工具都快。grep -c drug type drugbank_all_full_database.xml5.1.7 的 drug 标签数量应该在一万七千上下。注意这里的计数方式是按行匹配如果 XML 里的drug标签在行中间grep -c也能正确计数因为它是对每行做匹配计数。如果文件是压缩包先解压再执行不要对.gz文件直接 grep。数量偏离太大就说明下载的版本不对。6.3 用 SQLite 版本快速交叉验证如果你手头有 DrugBank 官方的 SQLite 文件可以用一行 SQL 验证 XML 和 SQLite 的药物数是否一致。这不是多余动作因为我遇到过 XML 里有 17000 个标签但实际可解析的只有 16900 个的情况原因往往是某个子节点的内容有问题导致解析中断。SELECT COUNT(*) FROM drugbank;如果 SQLite 文件和 XML 的结果对不上参考第五章的排查思路检查是不是不同版本的文件混用了或者下载过程中文件被截断。最好在同一时间从官网下载 XML 和 SQLite这样可以保证它们是同一次发布的产物。6.4 验证完成的下一步当你验证通过接下来就可以回到第三章的解析流程生成靶点表和 DDI 表。我个人的习惯是在生成文件名的开头打上版本号比如drugbank517_targets.tsv每次跑新的分析都保留日期标记。这样两星期后回头看一份表你能立刻知道它是从哪个版本来的不会混淆。毕竟做数据工作最贵的不是时间是用了错版本数据重新跑完一遍才发现的返工成本。希望这套流程能帮你把 DrugBank 5.1.7 真正用起来少踩我踩过的那些坑。本文还有配套的精品资源点击获取