SMILES表示法基础:分子结构编码规则、线性表示、规范SMILES

做化学信息学,绕不开SMILES。说白了,它就是分子结构的「身份证号」。你想想看,一个二维结构图,在计算机里怎么存?总不能存个图片吧。SMILES就是解决这个问题的——用一串字符,把分子结构描述清楚。

我第一次接触SMILES,是在做化合物库清洗的时候。那时候没注意规范问题,同一分子有人写CCO,有人写OCC,导致去重时漏掉不少。嗯,从那次起我就知道,SMILES看似简单,坑其实不少。

为什么需要SMILES?

有机化学里,分子结构图是最直观的。但计算机不认图。我们需要一种线性的、无歧义的、文本化的表示方式。SMILES(Simplified Molecular Input Line Entry System)正是为此而生。它由David Weininger在1980年代提出,现在已经是化学信息学的事实标准。

  • 紧凑:一个字符串就能表示极其复杂的分子
  • 可读:有经验的化学家能直接“读”出结构
  • 可计算:字符串可以哈希、比较、作为数据库主键
我个人习惯把SMILES比喻成「分子的一维投影」。就像把三维的家具画成二维草图,虽然损失了立体信息,但保留了连通性和原子类型,对大多数计算任务足够用了。

SMILES编码规则

1. 原子表示

原子直接用元素符号。有机常用元素:C、N、O、S、P、F、Cl、Br、I。氢原子通常省略(隐含),除非是特殊情况(如氢同位素)。

举个例子,乙醇的SMILES:CCO。你看,三个碳连在一起?不对。实际上C代表甲基,第二个C是亚甲基,O是羟基。氢全部隐含了。如果你非要写全,可以写成CC(O)C?嗯这里要注意,乙醇正确的SMILES是CCO,因为羟基连在第二个碳上。

曾经有个同事用CC(O)C表示乙醇,结果匹配不上其他来源的CCO。原因:CC(O)C其实是异丙醇(2-丙醇)的写法。SMILES中括号内的原子表示连在前一个原子上,顺序很重要。

2. 键型表示

键型符号示例说明
单键-(可省略)CC 等同 C-C酒精的C-C键
双键=C=C乙烯
三键#C#C乙炔
芳香键:(或小写原子)c1ccccc1苯环(小写c代表芳香碳)

单键可以省略不写,这是SMILES常见缩写。双键和三键必须写。芳香键用冒号,但更常用的是小写原子符号:c代表芳香碳,n代表芳香氮,等等。

3. 分支

用圆括号()表示分支。括号里的内容连接在前一个原子上。比如丙氨酸的SMILES:CC(N)C(=O)O。

来了,常见坑:如果你看到CC(=O)O,这是乙酸。括号表示=O连在第二个碳上,而不是连在氧上。这个顺序我花了好一阵才适应。

4. 环结构

用数字标记环的断开与闭合。比如苯环:c1ccccc1。第一个c1标记环起点,最后一个c1表示闭合回1号位置。如果有两个环,用2、3等数字。

萘的SMILES:c1ccc2ccccc2c1。这有两个环,共用一条边。数字不能重复,每个环用唯一数字。

关键规则:环数字用于标记两个原子之间的连接。同一个数字出现两次,表示第一个原子和第二个原子之间形成键。这两个原子可以是同种元素,也可以是不同元素。

5. 电荷与同位素

电荷用+和-表示。比如铵根:[NH4+]。注意方括号的作用:当原子有特殊状态(如电荷、同位素、非标准价)时,必须用方括号括起来。

同位素在前加数字,比如重氢(氘):[2H]。

正规化与规范SMILES

你知道同一个分子可以写出很多种SMILES吗?乙醇可以是CCO,也可以是OCC,甚至C(O)C。虽然它们对应同一个分子,但在字符串层面不同。如果你的数据库用SMILES做主键,那就悲剧了。

这就是规范SMILES的由来。规范SMILES通过一套规则(比如原子编号顺序、分支优先顺序、环编号最小化等),保证同一个分子只有一个SMILES字符串。最常用的生成工具是RDKit的Chem.MolToSmiles(mol, canonical=True)。

我在做一个百万级化合物库清洗时,遇到过这样的场景:两个不同供应商的库,明明分子一样,SMILES却不同。后来我强制跑一遍规范SMILES,去重率提高了15%。

from rdkit import Chem

# 不同写法的乙醇
smiles1 = "CCO"
smiles2 = "OCC"
smiles3 = "C(O)C"

mol1 = Chem.MolFromSmiles(smiles1)
mol2 = Chem.MolFromSmiles(smiles2)
mol3 = Chem.MolFromSmiles(smiles3)

# 规范后全部变成 "CCO"
canon1 = Chem.MolToSmiles(mol1)
canon2 = Chem.MolToSmiles(mol2)
canon3 = Chem.MolToSmiles(mol3)

print(canon1, canon2, canon3)  
# 输出: CCO CCO CCO

知识体系:从分子结构到规范SMILES

下面这张SVG图,帮你理清SMILES编码的核心流程:

SMILES编码规则流程 分子结构(二维) 编码核心规则 原子 + 键型 括号 → 分支 数字 → 环闭合 方括号 → 特殊原子 小写 → 芳香性 +/- → 电荷 线性SMILES字符串 ⚠ 规范算法 → 唯一字符串 直接用于数据库匹配、哈希

SMILES的局限与须知

SMILES虽好,但不是万能的。我个人踩过几个坑:

  1. 手性表示:基本的SMILES不支持手性。扩展版用@和@@表示手性中心。比如L-丙氨酸:C[C@H](N)C(=O)O。
  2. 没有立体化学:顺反异构要用/和\表示,比如C/C=C\C表示顺-2-丁烯。
  3. 不能表示多组分混合物:SMILES只有一个分子。混合物需要用.分隔,比如NaCl水溶液:[Na+].[Cl-]。
  4. 歧义风险:如果不对SMILES做规范化处理,很多数据库里同分异构体会被当成不同物质。
千万不要天真地以为你能手动写出一个完美SMILES。我曾在项目中看到有人手写C1=CC=CC=C1表示苯环,但规范工具跑出来是c1ccccc1。除非你写的是骨架测试,否则永远用程序生成规范SMILES。

实践:如何编写与验证SMILES

如果你刚开始接触,建议按这个流程走:

  • 画出分子结构,确定所有重原子(非氢)
  • 确定连接顺序:选择一个起始原子,逐步写出线性链
  • 遇到分支,用括号括起来
  • 遇到环,用数字标记闭合位置
  • 芳香环用对应的小写原子
  • 最后用RDKit或Open Babel验证并转换为规范形式

举个例子,咖啡因的SMILES:Cn1c(=O)c2c(ncn2C)n(C)c1=O。你看,有分支(=O)、有环(数字1和2)、有甲基(用小写c表示芳香碳配合数字)。如果是新手写这个,很容易漏掉某个闭环数字或写错括号。

我强烈建议你养成一个习惯:所有存入数据库的SMILES,都经过一次规范转换。不管是RDKit的MolToSmiles还是CDK的generateSMILES,效果一样。这能节省你后面90%的匹配头疼时间。

常见分子SMILES示例

分子结构描述非规范写法规范写法
水H-O-HOO
乙醇CH3-CH2-OHOCCCCO
苯C6H6(芳香)C1=CC=CC=C1c1ccccc1
乙酸CH3-COOHCC=O(错误!)CC(=O)O
甲烷CH4CC

注意乙酸那个例子:CC=O是乙醛,完全不同的分子。所以写SMILES时一定要小心双键和括号的位置。我见过最多错误就是羧基写成了醛基。

好了,这一章的内容就到这。SMILES是后续所有分析的基础。你只有把这一关搞扎实了,后面做批次差异分析时才能放心地操作字符串、比较子结构、计算相似度。别着急,慢慢来,先从水分子开始,写到苯环,写到咖啡因,你会有感觉的。