数据获取:从公开数据库获取药物相关数据
做药物图谱,数据是基础。光有工具,没有数据,一切都是白搭。
我之前遇到个项目,团队花了三周设计前端交互,结果发现连基本的药物-靶点关系都没拉全。嗯,从那以后我就把这一步放到了最前面。
这一节,我带大家从 DrugBank 和 CTD 两个公开源把数据搞到手。
3.1 数据从哪里来
公开的药物数据库不少。但我个人最常用的就两个:
- DrugBank:药物与靶点关系的权威来源,数据质量高。
- CTD(Comparative Toxicogenomics Database):药物-基因-疾病三者关联,覆盖面广。
为什么不选别的?说白了,DrugBank 的靶点数据更“干净”,CTD 的基因关联更“完整”。两者搭配,基本够用。
3.2 获取原始数据
有两种方式拿到数据:网页下载、API 调用。
3.2.1 从 DrugBank 下载
DrugBank 提供 XML 格式的完整数据包。你想想看,光下载就需要一个晚上释放。我当时第一次跑,愣是等了四个小时。
登录 DrugBank 官网,申请学术账号,下载 XML 文件:
# 文件路径示例
drugbank_all_full_database.xml # 完整数据包,约 2GB
下载后,用 Python 解析 XML:
import xml.etree.ElementTree as ET
tree = ET.parse('drugbank_all_full_database.xml')
root = tree.getroot()
namespace = '{http://www.drugbank.ca}'
for drug in root.findall(f'{namespace}drug'):
drugbank_id = drug.find(f'{namespace}drugbank-id')
name = drug.find(f'{namespace}name')
print(drugbank_id.text, name.text)
这个 XML 结构嵌套很深。我个人习惯,先只拉出药物 ID 和名称做索引。
3.2.2 从 CTD 抓取
CTD 提供 CSV 文件,按类别分:
# 下载化学-基因相互作用数据
wget http://ctdbase.org/reports/CTD_chem_gene_ixns.csv.gz
gunzip CTD_chem_gene_ixns.csv.gz
CSV 格式,直接 Pandas 加载:
import pandas as pd
df = pd.read_csv('CTD_chem_gene_ixns.csv')
print(df.columns.tolist())
print(df.head())
CTD 的数据比 DrugBank 粗糙,但胜在量大。很多时候,药物图谱的“疾病”节点就靠它补全。
3.2.3 用 API 增量查询
全量下载一次就够了。后续更新用 API 更灵活。
DrugBank API 示例:
# 查询特定药物信息
curl -X GET "https://api.drugbank.com/v1/drugs/DB00001" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
返回 JSON:
{
"drugbank_id": "DB00001",
"name": "Leptirudin",
"type": "Biotech",
"targets": [
{
"id": "BE00001",
"name": "Thrombin",
"action": "inhibitor"
}
]
}
CTD API 也可以:
# 查询化学物质
curl -X GET "https://ctdbase.org/v1/chemical/C00001" \
-H "Accept: application/json"
API 模式适合你只更新增量数据的时候。全量建库,还是建议用文件。
3.3 数据的结构长什么样
两个数据源的结构很不一样。我整理了个对比表,方便大家对照:
| 数据源 | 核心实体 | 关系类型 | 关键字段 |
|---|---|---|---|
| DrugBank | 药物、靶点 | 药物→靶点(抑制/激活) | drugbank_id, name, target_id, action |
| CTD | 化学物、基因、疾病 | 化学物→基因(相互作用);化学物→疾病 | chemical_id, gene_id, disease_id, interaction |
| 对比点 | DrugBank 靶点更精确 | CTD 疾病关联更丰富 | 需做 ID 映射对齐 |
你看,数据源之间天然有缝隙。后续做图谱对齐时,这是必须处理的。
3.4 清洗入库
数据拿到手,不能直接用。脏数据是家常便饭。
3.4.1 处理命名冲突
同一种药物,在两个库叫法不一样。比如“乙酰水杨酸”和“阿司匹林”。
我的处理方式:统一用 DrugBank 的药物名为标准。
def standardize_drug_name(name_from_ctd):
mapping = {
'Acetylsalicylic Acid': 'Aspirin',
'Aspirin': 'Aspirin',
'Paracetamol': 'Acetaminophen'
}
return mapping.get(name_from_ctd, name_from_ctd)
3.4.2 合并去重
两个库有很多重复记录。合并前要先去掉 ID 相同的项。
# 合并 DrugBank 和 CTD 的靶点数据
combined = pd.concat([drugbank_targets, ctd_targets])
combined.drop_duplicates(subset=['drugbank_id', 'target_id'], inplace=True)
3.4.3 填充缺失字段
CTD 的数据很多字段为空。我遇到过一个案例,疾病字段有 30% 是空值。
这时候就要靠 DrugBank 的关联数据往回推。或者直接标记为“未知”。
# 填充缺失值
combined['disease_name'] = combined['disease_name'].fillna('unknown')
3.5 构建基础关系矩阵
数据清洗干净后,我习惯先建一张“药物-靶点关系表”。说白了,就是把化合物和药靶之间的作用关系梳理清楚。
举个例子:
drug_id target_id action source
DB00001 BE00001 inhibitor DrugBank
DB00001 BE00002 agonist DrugBank
C00001 GENE-1234 increase CTD
C00001 GENE-5678 decrease CTD
这张表就是药物图谱的“边”数据。可视化时,它就是连接药物和靶点的线条。
3.6 这一章的核心逻辑
说了这么多,我们把整体流程理一下:
我早期做这个流程时,经常直接跳过清洗步骤,觉得“反正数据能跑”。结果可视化时发现节点和连线全是错的。嗯,从那以后我再也不敢跳了。
3.7 数据质量的检查清单
每次建完基础数据,我都跑一遍这个清单:
- 缺失率:关键字段(药物名、靶点名)缺失率 < 5%。
- 重复率:主键去重后,重复 < 2%。
- 类型对齐:inhibitor / agonist 等操作类型已经统一缩写。
- ID 映射:跨库 ID 已经做了映射表。
数据质量决定了可视化的下限。数据烂,再炫的图也是骗人。我一般把检查清单写到自动化脚本里,每次更新数据自动跑一遍。
好了,数据拿到,清洗干净,下一步就该想想怎么用这些数据画图了。