分子相似性度量:Tanimoto系数,Dice系数,余弦相似度
讲完了SMILES的语法规范、标准化校验和哈希编码,下一个核心问题来了——怎么量化两条SMILES的相似程度?我刚开始做分子库聚类的项目时,脑子里全是“结构像不像”这种模糊感觉,可计算机不认识感觉,它只认识数字。
这一章我们就来拆解三个最常用的分子相似性指标:Tanimoto系数、Dice系数和余弦相似度。说白了,它们就是把你手里的分子指纹(比如MACCS、ECFP4)当成两个向量或集合,然后算一个距离或重合度。
Tanimoto系数——分子相似度的“黄金标准”
Tanimoto系数,也叫Jaccard相似系数,起源于生态学。后来被化学信息学“拿来主义”,结果成了目前最主流的分子指纹相似度算法。我个人习惯在大多数场景下优先用Tanimoto,因为它在0到1之间分布均匀,阈值好定。
定义很简单:两个集合A和B,Tanimoto系数 = |A∩B| / |A∪B|。
放到二进制指纹里,就是:
- A与B共同为1的比特数(称为 a)
- A中为1、B中为0的比特数(b)
- B中为1、A中为0的比特数(c)
公式:Tc = a / (a + b + c)
DataStructs.TanimotoSimilarity()出来就用,但没注意指纹的稀疏程度。如果一个分子指纹只有2个1,另一个有200个1,Tanimoto就会偏低。这种情况我会改看Dice或带权重的余弦。
示例代码:
from rdkit import Chem
from rdkit.Chem import MACCSkeys
from rdkit.DataStructs import TanimotoSimilarity
smiles1 = "CC(=O)Oc1ccccc1C(=O)O"
smiles2 = "CC(=O)Oc1ccccc1"
mol1 = Chem.MolFromSmiles(smiles1)
mol2 = Chem.MolFromSmiles(smiles2)
fp1 = MACCSkeys.GenMACCSKeys(mol1)
fp2 = MACCSkeys.GenMACCSKeys(mol2)
tc = TanimotoSimilarity(fp1, fp2)
print(f"Tanimoto系数: {tc:.4f}")
Dice系数——给共同比特更高权重
Dice系数长得和Tanimoto很像,但分子多乘了一个2:Dice = 2a / (2a + b + c)。为什么这么改?你想想看:当两个指纹的共同比特数a很小,但各自独立比特b/c也都很小时,Tanimoto会把它们压得很低,而Dice因为分子翻倍,结果会更高一些。
我在做活性相似性搜索时发现,Dice对“局部高相似”的命中更敏感。比如两个分子都有同一个药效团但却长在不同的骨架上,这时Dice会识别出它们的共同模式,而Tanimoto可能因为骨架差异给出平庸的分数。
不过注意:Dice系数的值域也是[0,1],但它的数学分布偏向高值区域。嗯,这意味着你用0.7做阈值时,Dice过滤出来的分子集会比Tanimoto更膨胀一些。
余弦相似度——向量空间的视角
如果指纹不是二进制的,而是计数指纹(比如ECFP的计数版本),或者用PCA降维后的连续向量,Tanimoto和Dice就有点水土不服了。这时我带出余弦相似度:cosθ = (A·B) / (||A||·||B||)。
余弦相似度其实衡量的是两个向量的方向一致性,而不是模长。这在化学信息学里对应什么场景?我记得有一次做基于分子形状描述符的检索,分子大小差异很大,但我们只关心形状相似,用余弦就比用Tanimoto合理得多——它忽略了总量,只看分布模式。
示例:
import numpy as np
def cosine_similarity(vec1, vec2):
dot = np.dot(vec1, vec2)
norm1 = np.linalg.norm(vec1)
norm2 = np.linalg.norm(vec2)
if norm1 == 0 or norm2 == 0:
return 0.0
return dot / (norm1 * norm2)
# 假设我们已经将指纹转为numpy数组
fp1_arr = np.array([1,0,1,1,0,1,0,0])
fp2_arr = np.array([1,0,0,1,0,0,0,0])
print(cosine_similarity(fp1_arr, fp2_arr))
三个指标怎么选?一张表搞定
| 指标 | 输入类型 | 数学含义 | 适用场景 | 我看过的主要缺点 |
|---|---|---|---|---|
| Tanimoto | 二进制指纹(推荐) | 交并比 | 通用分子相似性搜索,活性虚拟筛选 | 稀疏指纹易偏低;对指纹长度敏感 |
| Dice | 二进制指纹(尤其稀疏场景) | 加权交并比 | 药效团匹配、局部高相似搜索 | 值域偏高,阈值需重新标定;对噪声略敏感 |
| 余弦相似度 | 连续向量 / 计数指纹 | 向量夹角余弦 | 分子形状描述符、PCA降维后比较、词袋模型指纹 | 不反映模长差异;零向量异常 |
实际项目中我通常会同时跑三个指标,然后做交叉验证。比如我的一次经验:用Tanimoto跑出Top 100后,再按Dice重新排序,找出那些Tanimoto中等但Dice很高的分子——候选库里经常能捞到骨架不同但活性相似的“飞跃”结构。
关于阈值怎么设,说实话没有普适答案。我个人习惯先做一批已知相似/不相似的人工标注,然后计算三个指标的ROC曲线,选使你业务指标最优的那个cutoff。比如在虚拟筛选场景,我常用Tanimoto ≥ 0.6配合Dice ≥ 0.7做双层过滤。
好了,这一章的概念到这儿。记住:相似性度量不是你唯一的武器,但它是你分析跨批次SMILES一致性时最基础也最好用的工具。怎么在不同场景下组合使用,就是接下来几讲的实践内容了。