3. RDKit入门:安装、分子读写、SMILES与Mol对象转换
好,咱们正式开始接触实战工具。RDKit 这个库,说白了就是化学信息学的瑞士军刀。你在药物数据分析里遇到的大部分分子操作,它都能搞定。我个人习惯把所有跟分子打交道的工作都先过一遍 RDKit,省时省力。
3.1 安装 RDKit:别在这个环节卡住
安装其实很简单,但我见过不少人在这一步就放弃了,太可惜。你想想看,环境都配不好,后面还怎么玩图卷积?
我个人最推荐用 conda 安装,它能把底层的 Boost 库一起搞定,省心很多。
# 创建一个新环境,Python 版本建议 3.9 以上
conda create -n drug-gcn python=3.9
# 激活环境
conda activate drug-gcn
# 安装 RDKit
conda install -c conda-forge rdkit
如果网速不好,也可以试试 pip。不过要注意,pip 版有时候会缺一些化学特征计算功能。
pip install rdkit-pypi
装完之后,怎么验证?打开 Python 交互环境,跑一行代码:
from rdkit import Chem
print(Chem.__version__)
你能看到类似 2024.03.1 这样的版本号,那就说明装好了。我曾经有一次装了半天,最后发现没激活 conda 环境,直接在 base 里跑,结果死活 import 不进来。嗯,这种低级错误我犯过一次就够了。
3.2 分子的两种形态:SMILES 与 Mol 对象
搞药物数据,你每天都会跟 SMILES 打交道。它是一个用 ASCII 字符表示分子结构的线性字符串。比如,乙醇长这样:CCO。
但我们的 GCN 模型不认识字符串,它只认图结构。所以 RDKit 的核心工作,就是把字符串转成它内部能操作的 Mol 对象。这个 Mol 对象里,原子、键、坐标、环,全都有。
为什么会这样?因为 SMILES 是线性表示,丢失了空间结构信息。而 Mol 对象是完整的数据结构,可以随时提取邻接矩阵和特征向量——这正是我们后面做 GCN 需要的东西。
看看转换有多简单:
from rdkit import Chem
# SMILES -> Mol
smiles = "CCO"
mol = Chem.MolFromSmiles(smiles)
# Mol -> SMILES
smiles_back = Chem.MolToSmiles(mol)
print(smiles_back) # 输出: CCO
这里有个坑:MolFromSmiles 如果解析失败,它会返回 None。我刚开始学 RDKit 时,有一件事特别糊涂:为什么我给了个看似正确的 SMILES,它却返回空?后来发现是括号少写了半边。所以,每次转换后养成检查的习惯:
if mol is not None:
print("转换成功")
else:
print("SMILES 格式有误")
| 属性 | SMILES | Mol 对象 |
|---|---|---|
| 本质 | 字符串 | 复杂数据结构 |
| 可读性 | 对人友好 | 对机器友好 |
| 包含信息 | 原子、键连接 | 原子、键、坐标、环、属性 |
| 适合 GCN 吗 | 不行 | 可以直接提取邻接矩阵 |
3.3 读写分子文件:从 SDF 到 CSV
真实的数据集,没人会给你纯 SMILES。最常见的格式是 SDF,它能把分子结构、3D 坐标、还有几十个属性字段打包在一起。
读取 SDF 的标准流程:
from rdkit import Chem
# 读取 SDF 文件,得到的是一个列表,每个元素是一个 Mol 对象
supplier = Chem.SDMolSupplier('drugs.sdf')
for mol in supplier:
if mol is not None:
# 提取 SMILES
smiles = Chem.MolToSmiles(mol)
# 提取属性(比如分子 ID)
mol_id = mol.GetProp('_Name')
print(f"ID: {mol_id}, SMILES: {smiles}")
这里有个细节要注意:SDMolSupplier 默认是惰性读取,它不会一下子把整个文件加载到内存。我在处理一个百万级分子的 SDF 时,这个特性特别有用——内存稳稳的。
写入 SDF 同样简单:
from rdkit import Chem
# 创建一个新的 Mol 对象,并设置属性
mol = Chem.MolFromSmiles("C1=CC=CC=C1")
mol.SetProp("_Name", "benzene")
mol.SetProp("alogP", "2.13")
# 写入文件
writer = Chem.SDWriter('output.sdf')
writer.write(mol)
writer.close()
你可能会问,为什么不直接存 SMILES?因为 SDF 能保留分子坐标。后面我们在做分子构象生成、3D 图卷积的时候,坐标是必须的。所以,养成存 SDF 的习惯,是专业选手的做法。
rdkit.Chem.PandasTools 中的 LoadSDF 或 AddMoleculesToDataFrame,你可以在 Pandas 的 DataFrame 里直接操作 Mol 对象,配合 GCN 数据处理非常丝滑。
3.4 本章知识结构图
下面这张图概括了本章最核心的流程——RDKit 就像一个翻译官,把你能读懂的 SMILES 转成机器能处理的 Mol 对象,最后通过文件读写落地。
3.5 常见报错与解决方法
我知道,光讲理论不过瘾。咱们直接看看实际跑代码时会遇到啥问题。
# 错误案例 1:SMILES 语法错误
mol = Chem.MolFromSmiles("C1CC1") # 正确写法是 C1CC1,但这个没问题
mol = Chem.MolFromSmiles("C1CC") # 括号不匹配,返回 None
# 错误案例 2:SDF 文件损坏
supplier = Chem.SDMolSupplier('broken.sdf')
for mol in supplier:
if mol is None:
print("跳过损坏分子")
我遇到过最坑的,是某公司的数据集在 SDF 里用了特殊分隔符。RDKit 的 SDMolSupplier 默认只认标准格式,遇到这种情况,我是用 rdkit.Chem.rdmolfiles.SDMolSupplier 带上参数 sanitize=False 先读进来,再手动清理。
3.6 进阶:Mol 对象到底长什么样?
你可能好奇,Mol 对象内部到底装了什么。咱们可以简单看看:
from rdkit import Chem
mol = Chem.MolFromSmiles("c1ccccc1")
print("原子个数:", mol.GetNumAtoms())
print("键的个数:", mol.GetNumBonds())
print("是否有环:", mol.GetRingInfo().NumRings())
# 遍历原子
for atom in mol.GetAtoms():
print(f"原子索引: {atom.GetIdx()}, 元素: {atom.GetSymbol()}")
# 遍历键
for bond in mol.GetBonds():
print(f"键: {bond.GetBeginAtomIdx()} - {bond.GetEndAtomIdx()}, 类型: {bond.GetBondType()}")
看到没有?原子索引、键连接关系全都有了。这就是 GCN 需要的 邻接关系。这些信息在后面构造图的时候,直接拿过来用就行。
说白了,RDKit 的 Mol 对象就是 GCN 的数据预处理器。你跑通了上面这些代码,后面 90% 的数据清洗工作就都有着落了。我建议你现在就打开终端,装好环境,把示例代码跑一遍。哪怕只跑通一个分子,这章就算毕业了。