第3章 RDKit简介:安装与基本使用,从SMILES到分子对象的转换方法
为什么是RDKit?
做ADMET预测,绕不开分子描述符。而描述符的源头,是分子结构。
RDKit就是那把钥匙。它能把一串字符变成活生生的分子对象,然后你才能在上面做各种计算。
我个人习惯把RDKit当成化学界的“瑞士军刀”。切分子、算指纹、画结构图,几乎全用它。
嗯,记住一点:RDKit是开源库,社区活跃,更新快。我用了快六年,没遇到过修不了的bug。
怎么装?
安装其实不难。但不同系统有些小坑,我踩过几次,给你总结一下。
最推荐的方式:conda
conda装RDKit最稳。它会把依赖的numpy、boost什么的都处理好。
conda install -c conda-forge rdkit
如果你有虚拟环境,建议先建一个干净的再装:
conda create -n admet python=3.10
conda activate admet
conda install -c conda-forge rdkit
备用方式:pip
pip也能装,但依赖容易出问题。
pip install rdkit-pypi
验证安装
装好后,跑一下这行命令:
python -c "from rdkit import Chem; print(Chem.__version__)"
如果打印出版本号,就说明装好了。
从SMILES到分子对象
说白了,就是把字符串变成计算机能理解的结构。
基础用法
核心函数是Chem.MolFromSmiles()。它干的事很简单:读入SMILES,返回一个rdkit.Chem.rdchem.Mol对象。
from rdkit import Chem
# 最简单的SMILES
smiles = "CCO" # 乙醇
mol = Chem.MolFromSmiles(smiles)
print(mol) # 会打印对象信息
print(type(mol)) # <class 'rdkit.Chem.rdchem.Mol'>
如果SMILES不合法,函数会返回None:
bad_smiles = "XYZ123"
mol = Chem.MolFromSmiles(bad_smiles)
print(mol) # None
为什么会这样?因为RDKit内部用了一套严格的原子连接规则。你给个乱码,它不会猜,直接返回空。
我在项目中遇到过一个奇怪现象,一个看起来对的SMILES总是返回None。后来发现里面多了一个空格。RDKit对空格和特殊符号很敏感,这也是常见坑。
常用原子操作
拿到分子对象后,你可以做很多事。
- 获取原子数:
mol.GetNumAtoms() - 获取重原子数:
mol.GetNumHeavyAtoms() - 获取分子式:
Chem.MolToSmiles(mol)(规范化的SMILES) 或rdMolDescriptors.CalcMolFormula(mol) - 查看原子列表:
[atom.GetSymbol() for atom in mol.GetAtoms()]
# 举个例子
mol = Chem.MolFromSmiles("c1ccccc1") # 苯
atoms = [atom.GetSymbol() for atom in mol.GetAtoms()]
print(atoms) # ['C', 'C', 'C', 'C', 'C', 'C']
print(mol.GetNumAtoms()) # 6
核心流程:SMILES→描述符的完整路径
为了让整个流程更清晰,我画了一张图。你看着它,就能明白每一步在干什么。
有了这张图,你应该能理解:SMILES输入→解析校验→分子对象→描述符计算是一条直线。
中间任何一个环节断了(比如返回None),后面都跑不下去。
常用检查与避坑指南
这里是我实际项目里遇到过的一些问题,你提前知道,能省很多时间。
| 场景 | 问题 | 解决方法 |
|---|---|---|
| SMILES格式不标准 | 含有空格、制表符、换行符 | 用strip()清洗一下 |
| 手性问题 | SMILES没标注手性,但分子有手性 | 使用Chem.MolFromSmiles(smiles, sanitize=True)自动处理 |
| 多个分子在一个SMILES里 | 如“CCO.O”表示乙醇和水 | 用Chem.MolFromSmiles(smiles)只生成一个分子,但会警告。批量处理需拆开 |
| SMILES含有盐或金属 | 如“CCO.[Na+]” | RDKit会识别,但需要额外处理盐 |
strip()根本去不掉,得用replace('\u200b', '')。这种隐形字符是最难查的。
# 清洗SMILES
def clean_smiles(smiles):
return smiles.strip().replace('\u200b', '').replace('\xa0', ' ')
smiles = " CCO\u200b"
print(repr(clean_smiles(smiles))) # 'CCO'
多分子处理的批量化
做ADMET特征工程,经常要处理成千上万个分子。一个一个写SMILES肯定不行。
批量转换的写法:
from rdkit import Chem
smiles_list = ["CCO", "c1ccccc1", "CC(=O)O", "C1CCCCC1"]
mol_list = []
for s in smiles_list:
mol = Chem.MolFromSmiles(s)
if mol is not None:
mol_list.append(mol)
else:
print(f"警告: SMILES {s} 转换失败")
print(f"成功转换 {len(mol_list)}/{len(smiles_list)} 个分子")
这个循环逻辑很基础,但非常实用。我个人习惯在转换的同时,顺手存一份mol对象和对应的smiles到一个字典里,方便后面回溯。
最后一个小提醒
RDKit的Mol对象很轻量,但不要同时存太多。我在处理百万级分子时,按批次转换并立即释放内存。
为什么要这样做?你想想看,每个对象内部都保存了原子坐标、键连关系,但很多时候你只需要数值描述符。处理完就扔掉,不要抱着对象占内存。
嗯,关于安装和基本转换,今天就到这里。下次你拿到每一条SMILES,都应该先走一遍MolFromSmiles检查一下。这个习惯,能帮你躲开至少一半的坑。