RDKit 化学信息学教程
使用环境:Windows + Miniconda,Python 3.14.4,RDKit 2024.09 / 2025.x 稳定版,VS Code,纯
.py脚本本教程所有示例均用 SMILES 字符串构造分子,不依赖任何外部文件,可直接复制运行。
目录
- 简介与定位
- 安装与版本检查
- 分子表示:SMILES / InChI / Mol 构建、读写 SDF / MOL
- 分子处理:加氢去氢、2D 坐标、3D 构象、标准化
- 子结构匹配:SMARTS 找官能团
- 描述符计算:2D / 3D
- 分子指纹:Morgan / MACCS / Tanimoto
- 相似性搜索与聚类
- 可视化:2D 结构图与网格
- 反应处理与化学变换
- 科研实战:特征表、相似配体筛选、官能团匹配
- 常见坑
- 延伸资源
1. 简介与定位
RDKit 是一个开源的化学信息学工具包(C++ 内核 + Python 绑定,BSD 协议)。在你的催化 / 材料研究里,它主要扮演「分子层面数据预处理与表征」的角色:
- 配体 / 底物 / 产物结构处理:加氢去氢、标准化(canonical SMILES)、生成 3D 构象(配体对接、力场优化前处理)。
- 描述符提取供机器学习:从一大批催化底物 / 配体 / MOF 连接体批量计算物理化学描述符(分子量、logP、TPSA、可旋转键等),拼成特征表喂给 sklearn / PyTorch。
- 分子筛选:用指纹相似度从库里挑「像」某个已知活性配体的分子;用 SMARTS 快速统计官能团 / 骨架。
- 反应处理:用反应 SMARTS 定义化学变换,做虚拟产物枚举。
它和你的主力工具(DFT / VASP、力场、机器学习势)互补:RDKit 做「粗、快、结构层」的处理,DFT 做「精、慢、电子层」的计算。一个典型工作流是:RDKit 从 SMILES 建构象 → 交给 xTB / 力场做预优化 → 再交给 DFT。
注意:RDKit 不做量子化学计算、不做分子动力学。3D 构象用内置的 ETKDG(基于距离几何的经验方法)+ MMFF/UFF 力场,速度快但精度是「经验级」,适合初猜结构。
2. 安装与版本检查
2.1 安装(推荐 conda-forge)
你已经装了 Miniconda,直接:
# 推荐:conda-forge 频道(Windows 下最省事,二进制预编译)
conda install -c conda-forge rdkit
# 或者单独建一个环境(推荐,避免污染 base)
conda create -n chem python=3.12
conda activate chem
conda install -c conda-forge rdkit
pip install rdkit 在 Windows 上有时缺二进制 wheel,优先 conda-forge。
Python 3.14 提示:RDKit 的 conda-forge 构建对最新 Python 版本的支持通常有几周到几个月的滞后。如果你的
conda install报「找不到匹配的 rdkit 构建」,就建一个python=3.12的环境装 RDKit,脚本语法与 API 完全一致。
2.2 版本检查与自检
# 版本检查
from rdkit import rdBase
print("RDKit 版本:", rdBase.rdkitVersion)
# 环境自检:能建分子、能算指纹,说明装好了
from rdkit import Chem
from rdkit.Chem import Descriptors
mol = Chem.MolFromSmiles("O=C=O") # CO2
print("CO2 分子量:", Descriptors.MolWt(mol)) # 应为 44.009(约)
3. 分子表示:SMILES / InChI / Mol 构建、读写 SDF / MOL
3.1 从 SMILES 构建分子
from rdkit import Chem
# 从 SMILES 构建 Mol 对象
smiles = "O=Cc1ccccc1" # 苯甲醛
mol = Chem.MolFromSmiles(smiles)
print(mol) # <rdkit.Chem.rdchem.Mol object at ...>
print("原子数:", mol.GetNumAtoms()) # 重原子数(不含 H)
# SMILES 与其它表示互相转换
print("Canonical SMILES:", Chem.MolToSmiles(mol))
print("InChI:", Chem.MolToInchi(mol))
3.2 SMILES 一览(催化 / 材料常用分子)
| 分子 | SMILES | 用途 |
|---|---|---|
| 二氧化碳 | O=C=O | CO2 还原底物 |
| 甲醇 | CO | 还原产物 |
| 甲酸 | O=CO | 还原产物 |
| 甲醛 | C=O | 中间体 |
| 乙烯 | C=C | 加氢底物 |
| 苯甲醛 | O=Cc1ccccc1 | 选择性加氢底物 |
| 苯甲醇 | OCc1ccccc1 | 产物 |
| 苯乙烯 | C=Cc1ccccc1 | 底物 |
| 硝基苯 | O=[N+]([O-])c1ccccc1 | 加氢底物 |
| 苯胺 | Nc1ccccc1 | 产物 |
| 对苯二甲酸 | OC(=O)c1ccc(cc1)C(=O)O | MOF 连接体(MIL / UiO) |
| 2-甲基咪唑 | Cc1ncc[nH]1 | ZIF-8 连接体 |
| 2,2'-联吡啶 | c1ccc(nc1)-c1ccccn1 | 双齿配体 |
| 三苯基膦 | c1ccc(cc1)P(c1ccccc1)c1ccccc1 | 膦配体 |
| 丙酮 | CC(=O)C | 溶剂 / 底物 |
3.3 从 InChI 构建、往返转换
from rdkit import Chem
mol = Chem.MolFromSmiles("O=Cc1ccccc1")
# 输出 InChI(IUPAC 国际化学标识符,独立于 SMILES 的规范化字符串)
inchi = Chem.MolToInchi(mol)
print("InChI:", inchi)
# 从 InChI 重建分子
mol2 = Chem.MolFromInchi(inchi)
print("往返后 SMILES:", Chem.MolToSmiles(mol2)) # 应与原 SMILES 一致(规范化后)
# InChIKey(27 字符哈希,适合做唯一键 / 数据库去重)
from rdkit.Chem.inchi import MolToInchiKey
print("InChIKey:", MolToInchiKey(mol))
InChI / InChIKey 适合做「结构唯一标识」:InChIKey 固定 27 位,可用作 pandas 索引或数据库主键去重。
3.4 读写 SDF / MOL 文件
from rdkit import Chem
mol = Chem.MolFromSmiles("O=Cc1ccccc1")
mol.SetProp("_Name", "benzaldehyde") # 设分子名(写入 SDF 时保留)
# 写单个 MOL 文件(V2000)
Chem.MolToMolFile(mol, "benzaldehyde.mol")
# 得到 MOL 块字符串(适合直接塞进 pandas 单元格或写文本)
block = Chem.MolToMolBlock(mol)
print(block[:60], "...")
# 从 MOL 块 / 文件读回
m = Chem.MolFromMolBlock(block)
m2 = Chem.MolFromMolFile("benzaldehyde.mol")
批量读 SDF(化合物库常见格式):
# 写:SDWriter
w = Chem.SDWriter("library.sdf")
for smi, name in [("O=Cc1ccccc1", "benzaldehyde"), ("Nc1ccccc1", "aniline")]:
m = Chem.MolFromSmiles(smi)
m.SetProp("_Name", name)
w.write(m)
w.close()
# 读:SDMolSupplier(迭代器,解析失败的条目返回 None,务必过滤)
suppl = Chem.SDMolSupplier("library.sdf")
mols = [m for m in suppl if m is not None]
for m in mols:
print(m.GetProp("_Name"), Chem.MolToSmiles(m))
4. 分子处理:加氢去氢、2D 坐标、3D 构象、标准化
4.1 加氢 / 去氢
from rdkit import Chem
mol = Chem.MolFromSmiles("O=Cc1ccccc1")
print("重原子数:", mol.GetNumAtoms())
# AddHs / RemoveHs 都返回【新分子】,不改动原对象
mol_h = Chem.AddHs(mol) # 加显式 H
print("加氢后原子数:", mol_h.GetNumAtoms())
mol_noH = Chem.RemoveHs(mol_h) # 去 H
print("去氢后原子数:", mol_noH.GetNumAtoms())
什么时候加氢:
- 生成 3D 构象、力场优化、对接前,通常要
AddHs(否则价层不完整,几何不合理)。 - 计算某些描述符(如 3D 描述符、氢键)时,显式 H 会影响结果。
4.2 2D 坐标
from rdkit import Chem
from rdkit.Chem import AllChem
mol = Chem.MolFromSmiles("c1ccc(nc1)-c1ccccn1") # 2,2'-联吡啶
AllChem.Compute2DCoords(mol) # 生成 / 刷新 2D 坐标(画图用)
2D 坐标只用于画图,不参与任何化学计算。
4.3 3D 构象生成(ETKDG + MMFF 优化)
这是你配体 / 底物初猜结构最常用的流程:
from rdkit import Chem
from rdkit.Chem import AllChem
mol = Chem.MolFromSmiles("OC(=O)c1ccc(cc1)C(=O)O") # 对苯二甲酸
mol = Chem.AddHs(mol) # 先加 H
# ETKDGv3:基于距离几何的经验构象生成器,随机种子保证可复现
params = AllChem.ETKDGv3()
params.randomSeed = 42
res = AllChem.EmbedMolecule(mol, params)
print("Embed 结果(0=成功,-1=失败):", res)
if res == 0:
# MMFF94 力场优化(返回 (状态码, 能量),状态码 0=收敛)
status, energy = AllChem.MMFFOptimizeMolecule(mol)
print("MMFF 优化状态:", status, "能量:", energy)
生成多个构象并取能量最低的:
mol = Chem.MolFromSmiles("Cc1ncc[nH]1") # 2-甲基咪唑
mol = Chem.AddHs(mol)
params = AllChem.ETKDGv3()
params.randomSeed = 0
cids = AllChem.EmbedMultipleConfs(mol, numConfs=10, params=params)
print("成功生成的构象数:", len(cids))
# 批量 MMFF 优化所有构象,返回 [(状态, 能量), ...]
results = AllChem.MMFFOptimizeMoleculeConfs(mol)
print("各构象能量:", [round(e, 3) for _, e in results])
print("构象总数:", mol.GetNumConformers())
金属配合物特别注意:MMFF94 没有过渡金属参数。处理单原子催化 / 金属配合物时,用
AllChem.MMFFHasAllMoleculeParams(mol)检查;若返回False,改用AllChem.UFFOptimizeMolecule(mol)(UFF 覆盖更多元素,但精度一般)。ETKDG 本身面向有机物,金属中心配位几何的初猜需要谨慎,必要时用 DFT / xTB 重优化。
4.4 分子标准化(canonical SMILES)
标准化是「同一分子不同 SMILES 写法 → 同一字符串」,用于去重和建表:
from rdkit import Chem
def canonical_smiles(smi):
"""把任意合法 SMILES 转成规范形式;解析失败返回 None"""
m = Chem.MolFromSmiles(smi)
if m is None:
return None
# canonical=True 生成规范 SMILES;isomericSmiles=True 保留立体信息
return Chem.MolToSmiles(m, canonical=True, isomericSmiles=True)
# 不同写法 -> 同一规范结果
print(canonical_smiles("O=Cc1ccccc1"))
print(canonical_smiles("c1ccccc1C=O")) # 与上一条相同分子
print(canonical_smiles("not_a_smiles")) # None
进阶:去盐(去除反离子,如盐酸盐的 Cl-)、去电荷等。Chem.SaltRemover 可以剥离常见无机盐:
remover = Chem.SaltRemover.SaltRemover()
stripped = remover.StripMol(Chem.MolFromSmiles("Nc1ccccc1.Cl")) # 苯胺盐酸盐 -> 苯胺
print(Chem.MolToSmiles(stripped))
5. 子结构匹配:SMARTS 找官能团
SMARTS 是「带查询语义的 SMILES」,用来描述模式(如「任何羰基」「任何伯胺」)。RDKit 用它做子结构匹配。
from rdkit import Chem
mol = Chem.MolFromSmiles("O=Cc1ccccc1") # 苯甲醛
# 定义一个 SMARTS 模式:羰基 C(=O)
carbonyl = Chem.MolFromSmarts("[CX3]=[OX1]")
# 是否存在该子结构
print("含羰基?", mol.HasSubstructMatch(carbonyl)) # True
# 返回所有匹配的原子索引(每个匹配是一个原子索引元组)
print("匹配原子索引:", mol.GetSubstructMatches(carbonyl)) # ((1, 7),)
5.1 常用官能团 SMARTS
patterns = {
"羰基 C=O": "[CX3]=[OX1]",
"羧酸 COOH": "[CX3](=O)[OX2H1]",
"伯胺 NH2": "[NX3;H2]",
"硝基 NO2": "[N+](=O)[O-]",
"碳碳双键": "[CX3]=[CX3]",
"碳碳三键": "[CX2]#[CX2]",
"卤素": "[F,Cl,Br,I]",
"芳香环": "a1aaaaa1", # 任意六元芳环
"腈基 CN": "[CX2]#[NX1]",
"酰胺键": "[NX3][CX3](=[OX1])",
}
mol = Chem.MolFromSmiles("O=[N+]([O-])c1ccccc1") # 硝基苯
for name, sma in patterns.items():
q = Chem.MolFromSmarts(sma)
print(f"{name:12s} 匹配数: {len(mol.GetSubstructMatches(q))}")
5.2 批量统计官能团
def count_frag(mol, smarts):
"""统计某个官能团出现的次数"""
q = Chem.MolFromSmarts(smarts)
return len(mol.GetSubstructMatches(q))
substrates = ["O=Cc1ccccc1", "O=[N+]([O-])c1ccccc1", "Nc1ccccc1", "C=Cc1ccccc1"]
for smi in substrates:
m = Chem.MolFromSmiles(smi)
print(smi, "| 羰基:", count_frag(m, "[CX3]=[OX1]"),
"| 硝基:", count_frag(m, "[N+](=O)[O-]"),
"| 胺:", count_frag(m, "[NX3;H2]"))
GetSubstructMatches返回的是原子索引;对同一模式可能出现多个重叠匹配,默认uniquify=True会去重。需要「键匹配」可用GetSubstructMatches(query, uniquify=False)查看完整返回。
6. 描述符计算
6.1 常用 2D 描述符(Descriptors 模块)
from rdkit import Chem
from rdkit.Chem import Descriptors
mol = Chem.MolFromSmiles("O=Cc1ccccc1") # 苯甲醛
print("分子量 MW:", Descriptors.MolWt(mol)) # 平均分子量 g/mol
print("精确分子量:", Descriptors.ExactMolWt(mol)) # 单同位素精确质量
print("logP (Crippen):", Descriptors.MolLogP(mol)) # 脂水分配系数(无量纲)
print("TPSA (Ų):", Descriptors.TPSA(mol)) # 拓扑极性表面积
print("可旋转键数:", Descriptors.NumRotatableBonds(mol))
print("氢键供体数:", Descriptors.NumHDonors(mol))
print("氢键受体数:", Descriptors.NumHAcceptors(mol))
print("杂原子数:", Descriptors.NumHeteroatoms(mol))
print("环数:", Descriptors.RingCount(mol))
print("芳香环数:", Descriptors.NumAromaticRings(mol))
print("sp3 碳比例 (Fsp3):", Descriptors.FractionCSP3(mol))
print("摩尔折射率 MR:", Descriptors.MolMR(mol))
print("QED 类药性:", Descriptors.qed(mol))
6.2 一次性计算全部描述符
# CalcMolDescriptors 返回一个 dict,含 200+ 描述符
d = Descriptors.CalcMolDescriptors(mol)
print("描述符总数:", len(d))
for k in ["MolWt", "MolLogP", "TPSA", "NumRotatableBonds", "FractionCSP3", "qed"]:
print(f"{k:20s} = {d[k]}")
把
CalcMolDescriptors的结果直接转成 pandas 的一行,就是 ML 特征表的一行(见第 11 节)。
6.3 3D 描述符(需要先 embed)
3D 描述符基于原子 3D 坐标,必须先 EmbedMolecule 生成构象,否则会抛错。
from rdkit import Chem
from rdkit.Chem import AllChem, Descriptors3D
mol = Chem.MolFromSmiles("OC(=O)c1ccc(cc1)C(=O)O")
mol = Chem.AddHs(mol)
params = AllChem.ETKDGv3(); params.randomSeed = 42
if AllChem.EmbedMolecule(mol, params) == 0:
AllChem.MMFFOptimizeMolecule(mol)
# 回转半径(Å)
print("回转半径:", Descriptors3D.RadiusOfGyration(mol))
# 主惯性矩 PMI1 <= PMI2 <= PMI3(衡量分子「扁/长/球形」程度)
print("PMI1/2/3:", Descriptors3D.PMI1(mol), Descriptors3D.PMI2(mol), Descriptors3D.PMI3(mol))
# 归一化主惯性矩比(0~1,1=球形)
print("NPR1/2:", Descriptors3D.NPR1(mol), Descriptors3D.NPR2(mol))
# 惯性形状因子
print("惯性形状因子:", Descriptors3D.InertialShapeFactor(mol))
# 偏心度 / 非球性
print("偏心度:", Descriptors3D.Eccentricity(mol))
print("非球性:", Descriptors3D.Asphericity(mol))
3D 描述符依赖构象,数值会随 randomSeed / 优化状态变化。若用于 ML,务必固定 randomSeed 并统一「加氢—embed—优化」流程,保证可复现。
7. 分子指纹:Morgan / MACCS / Tanimoto
指纹把分子结构压缩成固定长度的比特向量,用于相似度比较和机器学习输入。
7.1 Morgan / ECFP 指纹(推荐新 API)
from rdkit import Chem
from rdkit.Chem import rdFingerprintGenerator
# 新 API(2020.09+,推荐):先建生成器,再对分子生成指纹
morgan_gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
mol1 = Chem.MolFromSmiles("O=Cc1ccccc1") # 苯甲醛
mol2 = Chem.MolFromSmiles("OCc1ccccc1") # 苯甲醇
fp1 = morgan_gen.GetFingerprint(mol1) # 位向量(ExplicitBitVect)
fp2 = morgan_gen.GetFingerprint(mol2)
print("fp1 长度:", fp1.GetNumBits(), "| 置 1 位数:", fp1.GetNumOnBits())
# 计数指纹(保留每个特征出现次数,有时信息更丰富)
fp1_count = morgan_gen.GetCountFingerprint(mol1)
print("计数指纹:", fp1_count)
参数说明:
radius:Morgan 半径(半径 2 即「ECFP4」,考虑每个原子周围 2 键以内的环境)。fpSize:指纹长度(比特数),通常 1024 / 2048。includeChirality=True:是否纳入手性信息(研究立体选择性时建议开启)。
# 含手性信息的 Morgan 指纹
gen_chiral = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048, includeChirality=True)
旧 API
AllChem.GetMorganFingerprintAsBitVet(mol, 2, nBits=2048)已弃用,建议统一换到rdFingerprintGenerator以兼容未来版本。
7.2 其它指纹
from rdkit import Chem
from rdkit.Chem import MACCSkeys, rdMolDescriptors
mol = Chem.MolFromSmiles("O=Cc1ccccc1")
# MACCS 键指纹(167 位,基于预定义的 166 个结构键,解释性强)
maccs = MACCSkeys.GenMACCSKeys(mol)
print("MACCS 长度:", maccs.GetNumBits())
# RDKit 拓扑指纹(类 Daylight,2048 位)
rdk = Chem.RDKFingerprint(mol)
# 原子对 / 拓扑扭转指纹(补充 2D 拓扑信息)
ap = rdMolDescriptors.GetHashedAtomPairFingerprintAsBitVect(mol, nBits=2048)
tt = rdMolDescriptors.GetHashedTopologicalTorsionFingerprintAsBitVect(mol, nBits=2048)
7.3 Tanimoto 相似度
from rdkit import Chem
from rdkit.Chem import DataStructs, rdFingerprintGenerator
gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
fp1 = gen.GetFingerprint(Chem.MolFromSmiles("O=Cc1ccccc1")) # 苯甲醛
fp2 = gen.GetFingerprint(Chem.MolFromSmiles("OCc1ccccc1")) # 苯甲醇
fp3 = gen.GetFingerprint(Chem.MolFromSmiles("C=Cc1ccccc1")) # 苯乙烯
print("苯甲醛 vs 苯甲醇:", DataStructs.TanimotoSimilarity(fp1, fp2))
print("苯甲醛 vs 苯乙烯:", DataStructs.TanimotoSimilarity(fp1, fp3))
# 其它相似度
print("Dice:", DataStructs.DiceSimilarity(fp1, fp2))
print("Cosine:", DataStructs.CosineSimilarity(fp1, fp2))
Tanimoto(Jaccard)系数 = 交集 / 并集,取值 0~1,1 表示完全相同。
8. 相似性搜索与聚类
8.1 批量相似性搜索
from rdkit import Chem
from rdkit.Chem import DataStructs, rdFingerprintGenerator
gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
# 一个小型「配体库」
ligands = [
"c1ccc(cc1)P(c1ccccc1)c1ccccc1", # 三苯基膦
"CP(C)C", # 三甲基膦
"c1ccc(nc1)-c1ccccn1", # 2,2'-联吡啶
"c1ccncc1", # 吡啶
"Cc1ncc[nH]1", # 2-甲基咪唑
]
fps = [gen.GetFingerprint(Chem.MolFromSmiles(s)) for s in ligands]
query = gen.GetFingerprint(Chem.MolFromSmiles("c1ccc(cc1)P(c1ccccc1)c1ccccc1"))
# 一次算查询分子与所有库分子的相似度
sims = DataStructs.BulkTanimotoSimilarity(query, fps)
for smi, s in zip(ligands, sims):
print(f"{smi:40s} Tanimoto = {s:.3f}")
8.2 聚类(Butina,简要)
Butina 是一种快速的球心聚类,常用于把大库拆成若干「骨架簇」:
from rdkit import Chem
from rdkit.Chem import DataStructs, rdFingerprintGenerator
from rdkit.ML.Cluster import Butina
gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
smis = ["O=Cc1ccccc1", "OCc1ccccc1", "C=Cc1ccccc1", "CCc1ccccc1",
"O=CO", "CO", "O=C=O", "CC(=O)C"]
fps = [gen.GetFingerprint(Chem.MolFromSmiles(s)) for s in smis]
# 1. 计算两两距离(1 - Tanimoto)
dists = []
nfps = len(fps)
for i in range(1, nfps):
sims = DataStructs.BulkTanimotoSimilarity(fps[i], fps[:i])
dists.extend([1 - x for x in sims])
# 2. Butina 聚类(阈值 0.3 表示簇内两两距离 <= 0.3,即相似度 >= 0.7)
clusters = Butina.ClusterData(dists, nfps, 0.3, isDistData=True, reordering=False)
for cid, cluster in enumerate(clusters):
print(f"簇 {cid}:", [smis[i] for i in cluster])
9. 可视化:2D 结构图与网格
RDKit 的画图函数返回 PIL 图像。在纯 .py 脚本(非 Jupyter)里,通常用 .save() 存 PNG 再看。
from rdkit import Chem
from rdkit.Chem import Draw
mol = Chem.MolFromSmiles("OC(=O)c1ccc(cc1)C(=O)O") # 对苯二甲酸
# 单个分子:返回 PIL Image
img = Draw.MolToImage(mol, size=(300, 300))
img.save("terephthalic_acid.png")
# 或者直接写文件
Draw.MolToFile(mol, "terephthalic_acid.png", size=(300, 300))
多个分子网格(带图注,很适合批量查看底物 / 配体):
smis = ["O=C=O", "CO", "O=CO", "C=O",
"O=Cc1ccccc1", "OCc1ccccc1", "C=Cc1ccccc1", "CCc1ccccc1"]
names = ["CO2", "甲醇", "甲酸", "甲醛",
"苯甲醛", "苯甲醇", "苯乙烯", "乙苯"]
mols = [Chem.MolFromSmiles(s) for s in smis]
img = Draw.MolsToGridImage(mols, molsPerRow=4, subImgSize=(300, 220), legends=names)
img.save("substrates_grid.png")
Draw默认给每个结构自动计算 2D 坐标;如果之前已经Compute2DCoords,画图会复用已有坐标。
10. 反应处理与化学变换
用「反应 SMARTS」定义化学变换(原子映射用 :1、:2 标记),可对反应物做虚拟产物枚举。
from rdkit import Chem
from rdkit.Chem import AllChem
# SN2 胺化:卤代烷 + 胺 -> 胺(原子映射标记了连接点)
rxn = AllChem.ReactionFromSmarts("[C:1][Br].[N:2]>>[C:1][N:2]")
print("反应物模板数:", rxn.GetNumReactantTemplates())
print("产物模板数:", rxn.GetNumProductTemplates())
# 运行反应:RunReactants 输入 (反应物1, 反应物2)
reactants = (Chem.MolFromSmiles("CCBr"), Chem.MolFromSmiles("NC"))
products = rxn.RunReactants(reactants)
# products 是「每套产物的元组」的元组;一套可能含多个产物
for prod_set in products:
for p in prod_set:
print("产物:", Chem.MolToSmiles(p))
典型用途:
- 虚拟组合化学:一批卤代物 × 一批胺 → 枚举全部产物,再算描述符筛选。
- 定义某类反应的「骨架变换」,检查底物是否匹配反应模板(
rxn.IsMoleculeReactantOf/ 手动HasSubstructMatch)。
反应 SMARTS 只做拓扑层的原子重排,不涉及机理 / 能量;得到的是「可能的产物」,是否热力学 / 动力学可行要靠实验或 DFT 判断。
11. 科研实战示例
11.1 从一组催化底物批量算描述符,构建 ML 特征表
from rdkit import Chem
from rdkit.Chem import Descriptors
import pandas as pd
# 一组 CO2 还原 / 加氢相关底物与产物
substrates = {
"CO2": "O=C=O",
"甲醇": "CO",
"甲酸": "O=CO",
"甲醛": "C=O",
"苯甲醛": "O=Cc1ccccc1",
"苯甲醇": "OCc1ccccc1",
"苯乙烯": "C=Cc1ccccc1",
"乙苯": "CCc1ccccc1",
"硝基苯": "O=[N+]([O-])c1ccccc1",
"苯胺": "Nc1ccccc1",
}
rows = []
for name, smi in substrates.items():
mol = Chem.MolFromSmiles(smi)
if mol is None:
print(f"跳过无法解析的: {name} ({smi})")
continue
# 手工挑选的关键描述符 + 全量描述符二选一
row = {
"name": name,
"SMILES": Chem.MolToSmiles(mol),
"MW": Descriptors.MolWt(mol),
"logP": Descriptors.MolLogP(mol),
"TPSA": Descriptors.TPSA(mol),
"RotBond": Descriptors.NumRotatableBonds(mol),
"HDonors": Descriptors.NumHDonors(mol),
"HAcceptors": Descriptors.NumHAcceptors(mol),
"Fsp3": Descriptors.FractionCSP3(mol),
}
rows.append(row)
df = pd.DataFrame(rows)
print(df.to_string(index=False))
# df.to_csv("substrate_features.csv", index=False) # 存成 ML 特征表
用全量描述符建表(200+ 列,适合「先全量再特征选择」的 ML 流程):
all_rows = []
for name, smi in substrates.items():
mol = Chem.MolFromSmiles(smi)
if mol is None:
continue
d = Descriptors.CalcMolDescriptors(mol) # dict
d["name"] = name
all_rows.append(d)
df_all = pd.DataFrame(all_rows).set_index("name")
print("特征表维度:", df_all.shape)
11.2 基于指纹的相似配体筛选
从配体库里找「像三苯基膦」的候选(比如做膦配体虚拟筛选的第一步):
from rdkit import Chem
from rdkit.Chem import DataStructs, rdFingerprintGenerator
gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)
ligands = [
"c1ccc(cc1)P(c1ccccc1)c1ccccc1", # 三苯基膦(query)
"CP(C)C", # 三甲基膦
"CCP(CC)CC", # 三乙基膦
"c1ccc(cc1)N(c1ccccc1)c1ccccc1", # 三苯胺
"c1ccc(nc1)-c1ccccn1", # 联吡啶
"c1ccncc1", # 吡啶
"Cc1ncc[nH]1", # 2-甲基咪唑
]
query = gen.GetFingerprint(Chem.MolFromSmiles(ligands[0]))
results = []
for smi in ligands:
fp = gen.GetFingerprint(Chem.MolFromSmiles(smi))
results.append((smi, DataStructs.TanimotoSimilarity(query, fp)))
# 按相似度降序,给出阈值筛选
results.sort(key=lambda x: -x[1])
for smi, s in results:
flag = " <-- 命中" if s >= 0.5 else ""
print(f"Tanimoto={s:.3f} {smi}{flag}")
11.3 官能团 / 骨架匹配(分子分类器)
from rdkit import Chem
# 关键官能团 SMARTS
SMARTS = {
"羰基": "[CX3]=[OX1]",
"羧酸": "[CX3](=O)[OX2H1]",
"硝基": "[N+](=O)[O-]",
"胺基": "[NX3;H2]",
"双键": "[CX3]=[CX3]",
"芳环": "a1aaaaa1",
}
def fingerprint_by_frag(smi):
"""把分子转成「官能团 one-hot」向量"""
mol = Chem.MolFromSmiles(smi)
if mol is None:
return None
vec = {}
for name, sma in SMARTS.items():
q = Chem.MolFromSmarts(sma)
vec[name] = 1 if mol.HasSubstructMatch(q) else 0
return vec
for smi in ["O=Cc1ccccc1", "O=[N+]([O-])c1ccccc1", "Nc1ccccc1", "OC(=O)c1ccc(cc1)C(=O)O"]:
print(smi, "->", fingerprint_by_frag(smi))
这个「官能团 one-hot 向量」本身也能作为 ML 特征(可解释性强,比指纹更直观)。
12. 常见坑
-
MolFromSmiles返回None:SMILES 解析失败不会抛异常,而是返回None。任何批量处理的第一步都要判空,否则后续.GetNumAtoms()之类会AttributeError。 -
默认 sanitize 可能「纠正」你的分子:RDKit 默认对输入做规范化(价态、芳香性、电荷),有时会把不标准结构改掉。想拿到原始解析结果再手动处理:
mol = Chem.MolFromSmiles(smi, sanitize=False) try: Chem.SanitizeMol(mol) except Exception as e: print("sanitize 失败:", e) -
AddHs / RemoveHs 不是原地操作:它们返回新对象,原
mol不变。常见的 bug 是写了Chem.AddHs(mol)却忘了接返回值。 -
描述符单位与含义:
MolWt是平均分子量(同位素加权),ExactMolWt是单同位素精确质量(谱图对应用 Exact)。TPSA单位是 Ų。NumHDonors/NumHAcceptors基于隐式 H 即可,不必先AddHs;但加氢后数值语义不变、不要重复加。- 加氢会改变
NumHeteroatoms、NumAtoms等计数类描述符——描述符表要统一「加不加 H」的约定。
-
canonical SMILES 不是万能唯一:它能统一「写法」差异,但互变异构、质子化状态、立体异构会被当作不同分子。去重时若关心这些,先做标准化(去盐、中和、选规范互变异构)。
-
立体化学:
- 用
Chem.MolToSmiles(mol, isomericSmiles=True)保留立体信息(@/@@手性、//\双键)。 - 解析后某些手性中心可能是「未指定」,可用
Chem.AssignStereochemistry(mol, cleanIt=True, force=True)让 RDKit 依据 CIP 规则标注。
- 用
-
3D 描述符必须先 embed:
Descriptors3D.*在无 3D 坐标时直接抛ValueError。而且 3D 描述符依赖构象,务必固定randomSeed。 -
MMFF 不覆盖过渡金属:处理金属配合物 / 单原子催化时,
AllChem.MMFFHasAllMoleculeParams(mol)会返回False,改用UFFOptimizeMolecule。ETKDG 对金属中心配位几何的初猜也不可靠。 -
指纹 API 新旧混用:旧
AllChem.GetMorganFingerprintAsBitVect已弃用,新代码统一用rdFingerprintGenerator.GetMorganGenerator,避免版本升级后报错。
13. 延伸资源
- 官方文档(权威):https://www.rdkit.org/docs/index.html
- Getting Started in Python(官方入门,强烈建议通读):https://www.rdkit.org/docs/GettingStartedInPython.html
- RDKit Cookbook(大量即用配方):https://www.rdkit.org/docs/Cookbook.html
- SMARTS 语法(Daylight 手册):https://www.daylight.com/dayhtml/doc/theory/theory.smarts.html
- RDKit UGM(用户组会议)演讲:涵盖 3D 构象、描述符、指纹的进阶用法。
- 论文引用:RDKit 开源论文见 RDKit 官网(若你用它处理数据并发表,记得在论文里引用 RDKit)。
推荐学习顺序:先跑通本教程第 3–7 节(构建、处理、描述符、指纹),再用第 11 节模板套到自己的底物 / 配体库上;遇到 API 报错,优先查官方 Getting Started 和 Cookbook,别信过时博客。
本教程基于 RDKit 2024.09 / 2025.x 稳定版 API 编写,全部示例可直接运行。
评论交流
欢迎留下你的想法