1. SMILES基础:表示法、原子符号、键类型、分支、环、芳香性
SMILES 这个东西,我最早接触是在做药物分子筛选的时候。当时看到一堆字符代表一个分子,觉得挺神奇。后来自己写解析器踩了不少坑,才真正理解它到底怎么设计的。说白了,SMILES 就是一种用 ASCII 字符描述分子结构的线性表示法。你给它一串字符串,它就能还原成原子和键的连接关系。核心就几个规则,我来一个一个拆开讲。
1.1 SMILES 表示法:基本思想
SMILES 全称 Simplified Molecular Input Line Entry System。它的思路很简单:按深度优先顺序遍历分子图,把原子符号、键符号和分支括号串起来。遇到环就打个数字标签标记,遇到芳香环就用小写字母。整个字符串就是一棵展开的树。
记住一点:SMILES 本身不保证唯一性。同一个分子可以写出多个合法的 SMILES。但规范化的正则化算法(比如 Weininger 算法)能帮你生成唯一的标准形式。我在项目中遇到过两次重复注册的案例,就是因为没有做规范化。
1.2 原子符号
原子符号直接用元素周期表的缩写。大写字母开头,如果是有机元素(C、N、O、S、P、F、Cl、Br、I),连方括号都不用。其他元素必须用方括号括起来。
| 类型 | 示例 | 说明 |
|---|---|---|
| 常见有机元素 | C, N, O, S |
直接写,不带方括号 |
| 卤素 | F, Cl, Br, I |
直接写,但不含 At |
| 其他元素 | [Fe], [Na], [Au] |
必须用方括号 |
| 同位素或电荷 | [13C], [O-] |
方括号内可加同位素质量和电荷 |
嗯,这里要注意:方括号不是只给过渡金属用的。有机物若带了非默认价态或同位素,也得加。比如 C 默认四价,但如果你要表示一个带正电的碳正离子,就得写 [C+]。
我习惯在写自定义脚本时,先用 from rdkit import Chem 把 SMILES 解析一遍,看看原子符号是否合法。你想想看,万一写了个 [Si] 但忘了大写 S,解析器直接报错,排查半天才发现。
1.3 键类型
SMILES 里键用符号表示,放在两个原子之间。默认不写键时,表示单键(包括分支连接)。
- 单键:不写,或者用
-。例如CC就是乙烷,C-C也是乙烷。 - 双键:
=。例如C=C是乙烯。 - 三键:
#。例如C#N是氢氰酸。 - 四键:
$。极少见,主要用于过渡金属配合物。
我曾经在写合成路线自动生成工具时,遇到过一个坑:SMILES 里默认连接是单键,但很多人写芳香环时漏了上下键,结果解析出来变成了环己烷而不是苯。后来我在代码里加了一个 SanitizeMol 步骤才解决。
1.4 分支
分支用圆括号表示。括号里的内容挂在它前面的原子上。可以嵌套。举个例子:
CC(C)C 异丁烷
C(C)(C)C 也是异丁烷,写法不同
O=C(O)C 乙酸(C=O 连接 OH 和 CH3)
理解分支的关键是:括号里的内容相当于是一个侧链,它从当前原子出发,写完再回到主干继续往下。我记得第一次手动解析分支时,用栈实现深度优先遍历,嵌套层数一多就容易搞混。递归才是正道。
注意括号不能少写半边!我曾经在批量清洗数据时,因为一行 SMILES 缺少右括号导致整个解析中断。建议用 Chem.MolFromSmiles 时包裹 try-except,做好异常处理。
1.5 环
环的表示是 SMILES 的一大亮点。用数字做标签,表示两个相同数字的原子上有一条额外的连接。数字从 1 开始,依次递增。例如:
C1CCCCC1 环己烷(6个碳,1号标签连接首尾)
C1=CC=CC=C1 1,3-环己二烯
c1ccccc1 苯(小写 c 表示芳香碳)
你想想看,为什么不用 C1CCCCC1 而要用 c1ccccc1?因为芳香性影响了电子结构和反应活性。后者是芳香环,前者只是饱和环。
我建议在一个分子里,环的数字标签不要重复使用。如果有多个环,可以用 1, 2, 3…… 直到 9。超过 9 个则需要用 % 加两位数,比如 %10。其实很少碰到超过 9 个环的分子,但写解析器时还是要兼容。
1.6 芳香性
这是最容易混淆的地方。SMILES 里芳香原子用小写字母表示:c、n、o、s 等。注意不是 C 大写才是芳香,而是小写字母本身表示该原子参与芳香体系。例如:
c1ccccc1 苯(6个芳香碳)
c1ncncn1 三嗪(3个碳和3个氮交替)
c1ccoc1 呋喃(氧不芳香,但碳是芳香)
关键在于:小写字母自动隐含了单键/双键交替的凯库勒结构,但实际连接是离域的。在规范化 SMILES 时,RDKit 会把芳香环转成小写,非芳香环转为大写。我在做虚拟筛选时,输入数据里混着大小写混合的 SMILES,比对指纹时发现完全对不上,就是因为芳香性表示不一致。
一个实用技巧:用 Chem.MolFromSmiles 解析后,再用 Chem.MolToSmiles 输出,就能得到标准形式。比如输入 C1=CC=CC=C1 输出的是 c1ccccc1。这就是规范化的效果。
1.7 知识体系结构图
1.8 一个完整的示例
我们拿阿斯匹灵(阿司匹林)来实战。它的结构式是邻乙酰水杨酸。我经常用它作测试用例,因为同时包含芳香环、单键、双键、分支、羧基和酯基。两种合法的 SMILES:
CC(=O)Oc1ccccc1C(=O)O 非规范形式
O=C(C)Oc1ccccc1C(=O)O 另一种顺序
用 RDKit 规范化一下:
from rdkit import Chem
s = "CC(=O)Oc1ccccc1C(=O)O"
mol = Chem.MolFromSmiles(s)
std = Chem.MolToSmiles(mol)
print(std) # 输出类似 O=C(C)Oc1ccccc1C(=O)O
注意:Chem.MolFromSmiles 可能因为括号不匹配或环标签重复而返回 None。写自动化脚本时,一定要做 if mol is None 的检查,否则后续调用会崩掉。我吃过这个亏,跑了三天的数据分析,结果因为一条脏数据导致整个 pipeline 中断。
1.9 小结
SMILES 的基础规则其实不多:原子符号、键类型、分支、环、芳香性。但组合起来能表达几乎任何有机分子。我个人认为,熟练掌握这些规则是后续做数据清洗和增强的前提。你如果能手动写出常见分子的 SMILES,写解析器和生成器就更容易理解。
嗯,最后留个小练习:试试写出咖啡因的 SMILES。它有三个环、双键和多个氮原子。写出来后再用 RDKit 验证一下,看看和你预想的是否一致。