三、分子表示方法:SMILES、InChI、分子图与分子指纹概念
做药物筛选,第一步就是要把分子结构变成计算机能理解的形式。你想想看,化学家在纸上画一个苯环很容易,但机器不认识。所以,我们需要一套"翻译规则",把分子结构转成字符串、图或者向量。这就是本章要聊的核心:分子表示方法。
我个人习惯把分子表示分成两个层面:文本/图表示(SMILES、InChI、分子图)和特征编码表示(分子指纹)。前者保留完整结构信息,后者牺牲部分信息换取计算效率。两者在筛选流程中各有各的用处。
3.1 SMILES 表示法
SMILES 全称是 Simplified Molecular Input Line Entry System。说白了,就是用一串 ASCII 字符把分子结构"写"出来。它最直观,也是我平时用得最多的表示方法。
基本规则其实不难:
- 原子:用元素符号表示,有机元素可省略氢(如 C、O、N)
- 键:单键省略,双键用
=,三键用# - 分支:用圆括号
()括起来,挂在父原子上 - 环:用数字标记环的起始和结束原子
- 芳香族:小写字母表示芳香原子,如
c表示芳香碳
看几个例子就明白了:
# 水
O
# 乙醇
CCO
# 苯(芳香表示)
c1ccccc1
# 阿司匹林
CC(=O)Oc1ccccc1C(=O)O
我在项目中遇到过一个问题:同一个分子可以写出多个合法的 SMILES。比如乙醇,CCO 和 OCC 都合法,但字符串不同。这会导致数据库里出现"同一个分子多条记录"的尴尬。所以实际使用时,我会配合规范化工具(比如 RDKit 的 CanonSmiles)来保证唯一性。
3.2 InChI 表示法
InChI(International Chemical Identifier)是 IUPAC 推出的标准化标识符。它解决了 SMILES 不唯一的问题。我记得第一次用 InChI 时,觉得它比 SMILES 长得多,但后来发现它在数据交换和跨库匹配场景下真的很好用。
InChI 是分层的,每一层代表一类结构信息:
- 主层:分子式
- 连接层:原子连接顺序
- 氢原子层:氢的位置和数量
- 立体化学层:手性、双键构型等
拿阿司匹林来举例:
InChI=1S/C9H8O4/c1-6(10)13-8-5-3-2-4-7(8)9(11)12/h2-5H,1H3,(H,11,12)
前面 InChI=1S/ 是版本号,后面跟着各层信息。每一层用 / 分隔,层内用 () 分组。
3.3 分子图表示法
从图论角度看,分子就是一个无向图:原子是节点,化学键是边。这是最贴近化学家直觉的表示方法,也是很多分子指纹算法的底层基础。
我建议你从两个维度理解分子图:
- 节点特征:原子类型(C、N、O…)、电荷、价态、是否在环上
- 边特征:键级(单、双、三、芳香)、是否可旋转
举个例子,乙醇的分子图可以用邻接矩阵表示:
# 乙醇:C-C-O,邻接矩阵(简化)
# C1 C2 O
# C1 0 1 0
# C2 1 0 1
# O 0 1 0
当然,真实项目中我们不会手写邻接矩阵。RDKit 的 Chem.MolFromSmiles 返回的就是一个分子图对象,你可以直接查询原子、键、环等信息。
我在做结构相似性分析时,经常用分子图来提取子结构模式。比如识别某个分子是否包含"羧酸片段"或"苯环",用图搜索比用字符串匹配靠谱得多。
3.4 分子指纹概念
分子指纹,本质上就是把分子结构编码成一个固定长度的位向量或计数向量。为什么要这么做?因为计算机处理向量比处理图快得多。在做百万级分子库的相似性搜索时,指纹是绕不开的工具。
常见的分子指纹类型包括:
| 类型 | 编码方式 | 典型长度 | 特点 |
|---|---|---|---|
| MACCS 指纹 | 166 个预定义子结构是否存在 | 166 bit | 可解释性强,但覆盖有限 |
| ECFP(扩展连接指纹) | 从每个原子出发,迭代扩展邻域 | 通常 1024/2048 bit | 捕捉局部结构,应用最广 |
| FCFP(功能连接指纹) | 类似 ECFP,但用原子功能特征 | 通常 1024/2048 bit | 对药效团匹配友好 |
| AtomPairs 指纹 | 所有原子对的距离-特征组合 | 可变长度 | 捕捉全局拓扑 |
ECFP 是我项目里最常用的。它通过迭代扩圈的方式,把每个原子周围的环境编码成一个整数,再映射到固定长度的位向量上。说白了,它关注的是"每个原子的邻居长什么样"。
# 用 RDKit 生成 ECFP4(1024 bit)示例(Python 伪代码)
from rdkit import Chem
from rdkit.Chem import AllChem
mol = Chem.MolFromSmiles('CC(=O)Oc1ccccc1C(=O)O')
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=1024)
# fp 是一个位向量,例如:'0101101010...'(共 1024 位)
你可能会问:分子指纹是"有损编码"吗?答案是肯定的。从分子图到位向量,一定会丢失一些细节。但正因为维度降低、计算变快,我们才能在大型化合物库中做快速筛选。这是一个经典的精度与效率的权衡。
为了让你更直观地理解四种表示方法之间的关系,我画了一张体系结构图:
从这张图可以看得很清楚:化学分子是起点,三种表示方法各有侧重,最终都汇向分子指纹——它是连接化学结构与计算分析的桥梁。
嗯,到这里你应该对这四种分子表示方法有了比较全面的认识。它们不是互相替代的关系,而是针对不同场景设计的工具。选对工具,你的筛选流程才能跑得又快又准。