4. 药物知识图谱构建:主流数据源与预处理
构建药物知识图谱,第一步就是搞数据。很多人觉得算法模型是核心,其实在工业界,数据质量直接决定了你后面所有的活儿能不能出彩。
我个人习惯,先把能拿到的数据源梳理清楚,再动手写代码。别急着跑模型,先问问自己:这些数据长什么样?哪些字段能用?怎么把它们串起来?
本章我们就来盘一盘药物KG领域的主流数据源,以及怎么把这些“脏活累活”干漂亮。
4.1 主流数据源:从哪拿数据?
药物相关数据源很多,但常用的就那么几个。我把它们分成三类:药物-靶点关系型、蛋白质功能型、相互作用网络型。
| 数据源 | 核心内容 | 适用场景 | 访问方式 |
|---|---|---|---|
| DrugBank | 药物-靶点关联、药物分类、ADMET | 药物重定位、靶点发现 | XML/JSON 下载,API |
| UniProt | 蛋白质序列、功能注释、GO条目 | 靶点特征提取 | SPARQL 端点,RDF 下载 |
| STRING | 蛋白质-蛋白质相互作用 (PPI) | 网络传播、通路分析 | TSV 下载,REST API |
| ChEMBL | 生物活性数据、化合物-靶点亲合力 | 分子对接验证、SAR分析 | MySQL dump,API |
| TTD (Therapeutic Target Database) | 靶点-疾病关联、药物-靶点-通路 | 疾病机制研究 | Excel 表格下载 |
我的经验:别迷信单一数据源。DrugBank 的靶点标注最权威,但更新慢;ChEMBL 的活性数据全,但清洗工作量巨大。我通常会以 DrugBank 为主干,用 UniProt 做序列对齐,再用 STRING 补全网络关系。
举个例子,从 DrugBank 抓取一个药物的靶点信息,原始 XML 长这样:
<drug type="biotech">
<drugbank-id primary="true">DB00001</drugbank-id>
<name>Lepirudin</name>
<targets>
<target>
<id>BE0001234</id>
<name>Thrombin</name>
<organism>Homo sapiens</organism>
<polypeptide>
<id>P00734</id>
<source>UniProt</source>
</polypeptide>
</target>
</targets>
</drug>
你看,id 字段直接关联到 UniProt 的 P00734。这就是图谱构建的骨架——跨数据源的实体对齐。
避坑指南:我曾经在解析 DrugBank XML 时,没注意 target 节点下可能有多个 polypeptide 条目,导致漏丢了不少异构体数据。写 parser 的时候,一定要检查列表是不是完整的。
4.2 数据预处理:洗数据是门手艺
拿到的原始数据,说白了就是一堆半结构化文本。你得把它变成干净的三元组。
核心流程包括三步:命名实体识别、实体归一化、关系抽取。
4.2.1 命名实体识别 (NER)
找出来文本里哪些词是药物、靶点、疾病。比如一篇 PubMed 摘要里的这句话:
"Imatinib inhibits BCR-ABL tyrosine kinase in chronic myeloid leukemia."
你需要识别出:Imatinib (药物), BCR-ABL (靶点), chronic myeloid leukemia (疾病)。
我习惯用字典匹配 + 机器学习模型组合。字典用 DrugBank 和 MeSH 的术语表,模型可以用 BioBERT 微调。
4.2.2 实体归一化 (Normalization)
同一个实体在不同数据源叫法不同。比如 Thrombin 也叫 Coagulation factor II。归一化就是把它们映射到同一个标准 ID。
常用的映射表是 UniProt ID 和 Gene Symbol 的对应关系。举个例子:
{
"Thrombin": "P00734",
"Coagulation factor II": "P00734",
"F2": "P00734"
}
你可以在本地维护一份这样的 JSON 映射文件:
{
"uniprot_id": "P00734",
"gene_symbol": ["F2", "CF2", "PT"],
"protein_name": ["Thrombin", "Coagulation factor II", "Prothrombin"]
}
关键点:归一化是整个构建过程里最容易出错的一环。建议先跑一遍自动匹配,再做一轮人工校验,尤其是那些有歧义的基因名称。
4.3 数据融合:把碎片拼成图谱
数据融合的核心是链接预测的前置工作。你需要决定怎么把 DrugBank 的药物节点、UniProt 的蛋白质节点、STRING 的相互作用边,融合成一个统一的图。
常见策略:
- 同构融合:所有实体都作为节点,所有关系作为边。适用于 GNN 模型。
- 异构融合:保留实体类型(药物、靶点、疾病),关系也有类型(抑制、激活、结合)。适用于 RGCN 等异构模型。
- 属性融合:把序列特征、化学结构指纹作为节点属性存入。
我常用的融合流程,用 Python 伪代码可以写成:
import pandas as pd
# 1. 加载 DrugBank 药物-靶点关系
drug_target = pd.read_csv('drugbank_targets.tsv', sep='\t')
# 2. 加载 UniProt 蛋白质序列信息
uniprot_protein = pd.read_csv('uniprot_human.tsv', sep='\t')
# 3. 加载 STRING PPI 网络
string_ppi = pd.read_csv('string_human_500.tsv', sep='\t', header=None,
names=['protein1', 'protein2', 'score'])
# 4. 通过 UniProt ID 对齐
merged = drug_target.merge(uniprot_protein, left_on='target_uniprot',
right_on='uniprot_id', how='inner')
# 5. 构建图边列表
edges = []
for _, row in merged.iterrows():
edges.append(('Drug', row['drug_id'], 'Target', row['uniprot_id']))
edges.append(('Protein', row['uniprot_id'], 'PPI', row['string_partner']))
我曾经踩过的坑:STRING 数据里的蛋白质标识符经常是 ENSEMBL ID,不是 UniProt ID。你需要先做一个 ID 转换。别直接拿来用,否则连边对不上,整个图就是个哑炮。
4.4 知识图谱构建流程总览
下面这张图概括了从数据源到知识图谱的完整脉络。你做完预处理后,应该得到的就是这样一个结构化的输出。
4.5 跨源冲突处理
不同数据源对同一关系的描述可能打架。比如 DrugBank 说某个药物抑制靶点 A,而 ChEMBL 说它是激动剂。怎么处理?
我的做法:
- 置信度加权:优先采纳权威数据源(DrugBank 的靶点关系优先级高于文献挖掘)。
- 版本标记:保留所有关系来源,在图中加入
source属性。 - 人工审核:对于高冲突的边,生成待审列表,交给领域专家确认。
小技巧:你可以用 Neo4j 的 apoc.refactor.mergeNodes 来合并同一实体的多个表示,前提是你能保证 ID 映射绝对准确。
最后,构建好的知识图谱通常以 triples.csv 形式落盘,格式如下:
head relation tail
DB00001 targets P00734
DB00001 treats C001233
P00734 interacts_with Q12345
DB00002 inhibits P01374
这就是后续靶点特征学习、图神经网络训练最直接的输入。说白了,前面的所有预处理工作,都是为了打出这样一份干净的三元组清单。