3. RDKit入门:安装、分子读写、SMILES与Mol对象转换

好,咱们正式开始接触实战工具。RDKit 这个库,说白了就是化学信息学的瑞士军刀。你在药物数据分析里遇到的大部分分子操作,它都能搞定。我个人习惯把所有跟分子打交道的工作都先过一遍 RDKit,省时省力。

3.1 安装 RDKit:别在这个环节卡住

安装其实很简单,但我见过不少人在这一步就放弃了,太可惜。你想想看,环境都配不好,后面还怎么玩图卷积?

避坑提示:千万不要自己手动编译 RDKit!除非你特别享受解决依赖冲突的快感。直接用 conda 或者 pip 一把梭。

我个人最推荐用 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 是代码看的。两者之间的转换,是 RDKit 入门的第一个门槛。

为什么会这样?因为 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 还支持直接读 CSV 里带 SMILES 的列。用 rdkit.Chem.PandasTools 中的 LoadSDF 或 AddMoleculesToDataFrame,你可以在 Pandas 的 DataFrame 里直接操作 Mol 对象,配合 GCN 数据处理非常丝滑。

3.4 本章知识结构图

下面这张图概括了本章最核心的流程——RDKit 就像一个翻译官,把你能读懂的 SMILES 转成机器能处理的 Mol 对象,最后通过文件读写落地。

RDKit 分子数据核心流程 1. 环境安装 2. SMILES 输入 3. Mol 对象转换 4. 属性/结构操作 5. 文件读写 核心目标: 为 GCN 准备图数据

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 先读进来,再手动清理。

重要提醒:读取 SDF 时,如果文件中包含几十万分子,务必加个计数器或者 tqdm 进度条。我曾有一次没加,跑了一个小时以为自己死机了,结果人家还在慢慢读。

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% 的数据清洗工作就都有着落了。我建议你现在就打开终端,装好环境,把示例代码跑一遍。哪怕只跑通一个分子,这章就算毕业了。