分子预处理:标准化、去重、去除盐离子、质子化状态调整
好,咱们进入正题。分子预处理,说白了就是把原始数据里的“毛刺”都剃干净。你想想看,从公开库、实验室、合作方拿到的分子结构,常常格式混乱、重复、带着乱七八糟的离子……直接扔进虚拟筛选,结果就是一锅粥。我刚开始做这块时,就吃过这个亏——跑了三天的大规模对接,最后发现一半分子根本就是重复的,另一半因为质子化状态不对,结合能全算偏了。嗯,从那以后,我再也不敢跳过预处理了。
下面我会按四个关键步骤展开:标准化、去重、去除盐离子、质子化状态调整。每一步我都给你讲清楚为什么做、怎么做,以及常见坑。
4.1 标准化
标准化的目标只有一个:让所有分子用同一种语言说话。比如硝基,有的文件写 N(=O)=O,有的写 N(=O)O,还有的写成 [N+](=O)[O-]……不同表示方式的硝基,在对接打分函数里会得到完全不同的结果。标准化就是把这些差异统一掉。
我个人习惯用 RDKit 的 SanitizeMol 或者 Open Babel 的 –canonical 选项。下面给个简单的 Python 示例:
from rdkit import Chem
def standardize_smiles(smiles):
mol = Chem.MolFromSmiles(smiles)
if mol is None:
return None
# 标准化:包含芳香性、价键、电荷、同位素等
Chem.SanitizeMol(mol)
# 输出标准 SMILES
return Chem.MolToSmiles(mol)
# 示例
print(standardize_smiles("c1ccccc1")) # 苯
print(standardize_smiles("N(=O)=O")) # 硝基
- 芳香性表示:Kekulé vs 交替单双键
- 显式/隐式氢:比如 CH3 与 C(显式 H)
- 立体化学标记:@ 与 @@ 的规范
- 同位素:有些库会把碳-12 写成 [12C],有些写 [C]
4.2 去重
去重听起来简单,但实际操作时很坑。你以为 SMILES 一样就是同一个分子?不一定。我遇到过同一个化合物,因为不同供应商给的 SMILES 用不同价键方式,导致 RDKit 解析后互相不认为相等。
推荐做法:先用标准化,再用 InChI 或规范 SMILES 做键值去重。 InChI 是一种更稳健的分子标识符,对互变异构体也有处理。
from rdkit import Chem
def deduplicate_mols(mol_list):
seen = set()
unique = []
for mol in mol_list:
inchi = Chem.MolToInchi(mol)
if inchi and inchi not in seen:
seen.add(inchi)
unique.append(mol)
return unique
pandas 的 drop_duplicates 配合 InChI 列,或者直接对 SDF 文件用 obabel 做预处理:obabel input.sdf -O output.sdf -d(-d 表示去重)。4.3 去除盐离子
虚拟筛选的靶点通常只结合中性有机分子。如果一个 SDF 文件里写了“CCO.[Na+]”,对接时对接口袋里嵌着一个钠离子,这个打分能靠谱吗?
所以,必须去掉盐离子、溶剂分子、金属离子。常见做法是用 RDKit 的 SaltRemover,或者手动定义黑名单。
from rdkit.Chem import SaltRemover
remover = SaltRemover.SaltRemover()
mol = Chem.MolFromSmiles("CCO.[Na+]")
cleaned_mol = remover.StripMol(mol)
print(Chem.MolToSmiles(cleaned_mol)) # 输出:CCO
我个人习惯用 Open Babel 做批量处理,特别是处理上百万条记录时:
obabel huge.sdf -O stripped.sdf --remove salt
4.4 质子化状态调整
这个步骤我认为是预处理里最容易翻车的一环。同一个分子,在中性 pH 和酸性 pH 下,谷氨酸可能带负电(去质子化),组氨酸可能带正电(双质子化)……对接打分函数是基于固定 pH 的力场参数,如果电荷状态错了,结合能可以差好几个数量级。
我建议使用 计算 pKa 的方法来确定每个可电离基团在目标 pH(一般设为 7.4)下的主要形式。常用工具包括:
| 工具 | 原理 | 适合场景 |
|---|---|---|
| RDKit + pKa 模型 | 基于局部结构经验规则 | 快速粗筛,几百万分子 |
| Open Babel –p pH | 调用内置 pKa 数据库 | 中等规模,命令行友好 |
| MoKa / ChemAxon | 高精度 QSPR 模型 | 精细筛选,几百到几千分子 |
| Dimorphite-DL | 深度学习 pKa 预测 | 新兴方法,无商业许可问题 |
下面是用 Open Babel 做质子化调整的命令示例(pH 7.4):
obabel input.sdf -O protonated.sdf --correct \
--addpolarhydrogen --ph 7.4
如果用 RDKit + Dimorphite-DL,可以这样:
from dimorphite_dl import DimorphiteDL
from rdkit import Chem
def adjust_protonation(smiles, ph=7.4):
dl = DimorphiteDL()
result = dl.protonate(smiles, ph=ph)
mol = Chem.MolFromSmiles(result[0])
return mol
Draw.MolToImage 看一眼质子化形式是否正确。特别是那些含组氨酸、半胱氨酸、巯基的特殊分子。本章核心流程图
下面这张图总结了分子预处理的标准流程,以及各步骤的主要工具和输出:
parallel 并行处理。Python 脚本虽然灵活,但在 IO 和内存管理上不如原生 C++ 工具快。我曾经用 RDKit 处理 500 万分子,内存直接爆了;换成 obabel 分批处理,只用了 3 小时。总结
这四个步骤——标准化、去重、去除盐离子、质子化调整——是大规模虚拟筛选绝对不能跳过的预处理环节。跳过任何一个,后面的对接结果都可能被污染。我个人经验是,花在预处理上的时间,至少要占整个 pipeline 开发时间的 20%。
最后再啰嗦一句:预处理完一定要做质控。至少跑一次小规模验证——拿几百个分子对接,检查结果是否符合化学直觉。如果发现离谱的得分,大概率是前面某个步骤出了问题。