药物重定位研究方法与流程归纳总结

药物重定位,说白了就是给老药找新活。听起来简单,真做起来坑不少。我当年第一次上手重定位项目,天真地以为把几个数据库一合并,跑个网络分析就能出结果。结果呢?数据打架、特征缺失、模型过拟合,折腾了两个月颗粒无收。后来我总结出一套相对稳定的流程,今天分享给你。

整个流程长什么样?

先看整体框架。我个人习惯把重定位拆成五个阶段:数据收集、数据清洗、计算预测、候选筛选、实验验证。每一步都有坑,但走通之后,效率能提升好几个档次。

药物重定位标准流程 ① 数据收集 ② 数据清洗 ③ 计算预测 ④ 候选筛选 ⑤ 实验验证 关键数据源 DrugBank, CTD, STITCH GEO, LINCS, PubChem UniProt, STRING 计算方法 网络扩散 / 随机游走 特征相似度 / 矩阵分解 深度学习 / 图神经网络

我的经验:流程图里的五个阶段并不是严格串行。有时候数据清洗完发现特征太少,你得回头补数据。允许循环迭代,别死磕某一步。

第一步:数据收集与整合

重定位的数据,说白了就是两张大表:一张存药物和它的靶点、副作用、适应症;另一张存疾病和它的基因、通路、表型。然后想办法把这两张表连接起来。

常用的公开数据源,我整理了一份清单:

数据源内容访问方式
DrugBank药物-靶点、药物-适应症官网下载 XML/CSV
CTD (Comparative Toxicogenomics Database)化学物-基因-疾病关联REST API / 批量下载
STITCH化学物-蛋白质互作TSV 文件下载
GEO基因表达谱(疾病 vs 正常)GEOmetadb / Bioconductor
LINCS L1000药物扰动后的基因表达变化CLUE API / 下载
UniProt蛋白质序列、功能、互作SPARQL / 批量下载
STRING蛋白质-蛋白质互作网络API / TSV 导出

我曾经栽过一个跟头:从 DrugBank 下载了 XML 版本,结果解析时没注意英文字段里藏了中文注释,导致靶点名称对不上。后来我改用 TSV 版本,并写了个统一归一化的脚本。建议你一开始就统一用 DrugBank ID 或 ChEMBL ID 做主键,省得后面后悔。

第二步:数据预处理与特征工程

数据收集来的原始数据,你想想看,出自不同数据库,命名规范完全不一样。比如“阿司匹林”在 A 库里叫 Aspirin,在 B 库里叫 Acetylsalicylic acid,甚至在 C 库里给出的是 PubChem CID 2244。第一步就是实体对齐。

我常用的对齐工具:

  • 基于字典匹配:用 UniChem、PubChem 映射表做交叉查询。
  • 文本模糊匹配:对药物名称做 Levenshtein 距离、Jaccard 相似度,阈值设 0.85 以上。
  • 人工校验:对齐后抽样 100 条,肉眼过一遍。我吃过亏,自动映射把 Dopamine 和 Dobutamine 当成同一种药,差点闹出笑话。

特征工程这块,我习惯构建三个维度的特征:

  1. 药物特征:分子指纹(ECFP4、MACCS)、理化性质(LogP、分子量)、副作用标签。
  2. 疾病特征:差异表达基因集、通路富集得分、表型相似性(基于 HP Ontology)。
  3. 互作特征:药物靶点与疾病相关基因在蛋白质互作网络中的最短路径、共表达相关性。

写一个简单的 Python 代码片段,用来从 DrugBank 提取药物-靶点关系:

import pandas as pd
import xml.etree.ElementTree as ET

def extract_drug_targets(xml_file):
    tree = ET.parse(xml_file)
    root = tree.getroot()
    records = []
    for drug in root.findall('.//drug'):
        drug_id = drug.find('drugbank-id').text
        targets = drug.findall('.//target')
        for target in targets:
            target_id = target.find('id').text if target.find('id') is not None else None
            records.append((drug_id, target_id))
    df = pd.DataFrame(records, columns=['drug_id', 'target_id'])
    return df

df = extract_drug_targets('drugbank.xml')
df.to_csv('drug_target_pairs.csv', index=False)

注意:DrugBank XML 文件很大(1.5GB+),解析时别直接一次性加载进内存。建议用迭代式解析 iterparse 或者按分块读取。我当初把内存跑崩了三次才学会这个教训。

第三步:计算预测方法

重定位的计算方法,说白了就是三种套路:相似度匹配、网络传播、机器学习。我各举一个代表性算法:

方法类别典型算法适用场景
基于相似度药物-疾病余弦相似度 + 阈值排序有已知适应症数据,特征维度不高
网络扩散Random Walk with Restart (RWR)蛋白质互作网络较完整,关系稀疏
机器学习XGBoost / 图神经网络(GNN)有足够正负样本,特征工程做得好

我个人最推荐 网络扩散 作为基线方法。为什么?因为它不需要大量训练标签,而且能利用网络的全局拓扑结构。我记得有一个项目,用 RWR 在 STRING 网络上跑一轮,Top-50 候选里验证成功率达到了 12%,远高于随机挑选。

RWR 的核心代码示例(Python + NetworkX):

import networkx as nx
import numpy as np

def random_walk_restart(adj_matrix, seed, restart_prob=0.7, max_iter=100):
    n = adj_matrix.shape[0]
    p = np.zeros(n)
    p[seed] = 1.0
    # 归一化转移矩阵
    M = adj_matrix / adj_matrix.sum(axis=1, keepdims=True)
    M = np.nan_to_num(M)
    for _ in range(max_iter):
        p_new = (1 - restart_prob) * M.T @ p + restart_prob * p_seed
        if np.allclose(p, p_new, atol=1e-6):
            break
        p = p_new
    return p

# 假设 G 是 NetworkX 图,seed_node 是疾病相关基因的节点ID
adj = nx.adjacency_matrix(G).todense()
p_seed = np.zeros(adj.shape[0])
p_seed[seed_idx] = 1.0
scores = random_walk_restart(adj, p_seed)
# 对药物节点(非种子)按得分排序

第四步:优先级排序与候选筛选

计算模型通常会输出几百个候选药物。你不可能全拿去实验验证,太贵了。我经常用三层过滤器来筛选:

  1. 安全性过滤:去掉已知有严重副作用的药。从 SIDER 数据库拉取副作用频率,剔除 hepatotoxicity、cardiotoxicity 高频的药物。
  2. 可及性过滤:优先选已上市、成本低、专利已过期或即将过期的药物。太新的药反而不适合重定位,数据太少。
  3. 一致性过滤:看候选药物的已知适应症与目标疾病是否有共享的生物学机制(比如共通路、共靶点)。如果完全不相关,模型可能是在学噪声。

三层过滤之后,Top-10 或 Top-20 基本就是可验证的候选列表了。我建议把候选列表做成一个表格,包含药物名、打分、安全评级、可及性标记、机制一致性说明。这样给 PI 看的时候,人家一眼就能判断。

第五步:实验验证

这一步是重定位真正的“试金石”。计算模型说行不算,体外细胞实验验证了才算。我通常建议做两组实验:

  • 体外药效:在疾病相关细胞系中测 EC50 / IC50,和已知标准药对比。
  • 安全性快速筛查:测一下对正常细胞的毒性(CC50),算一下选择性指数(SI = CC50 / IC50),SI > 10 才值得推进。

在你做实验之前,记得要先查一下这个药是否已经有人测试过对目标疾病的活性。我有一回白花了两周测一个药,结果发现三个月前刚有论文发表,还是一样的细胞系。真想把那篇论文从脑海里删掉。所以 文献调研要前置,用 PubMed、ClinicalTrials.gov 扫一圈,避免重复造轮子。

核心总结:药物重定位流程没那么神秘,做好数据对齐、选对计算方法、踏实做实验验证。每一步都藏着小坑,但只要按这个框架走,出成果只是时间问题。