InChI(International Chemical Identifier)是 IUPAC 制定的化学结构标识标准。它与 SMILES 的关键差别:InChI 由算法唯一确定,不依赖具体软件的实现——这让它成为跨数据库匹配的可靠工具。
InChI 的分层结构
# 一个完整的 InChI 由多层构成,用 / 分隔
#
# 对乙酰氨基酚:
# InChI=1S/C8H9NO2/c1-6(10)9-7-2-4-8(11)5-3-7/h2-5,11H,1H3,(H,9,10)
#
# 分解:
# 1S 版本 1,标准 InChI(S = Standard)
# /C8H9NO2 【分子式层】
# /c1-6(10)9-... 【连接层】—— 原子如何连接
# /h2-5,11H,... 【氢层】—— 氢原子的分布
# /b, /t, /m, /s 【立体化学层】(如果有)
# /i 同位素层
# /p 质子化层
# /q 电荷层
#
# 【分层的意义】:
# 可以在不同的严格程度上比较分子
# 如:只比较连接层 = 忽略立体化学的比较
#
# 【标准 InChI vs 非标准 InChI】:
# 标准(1S):使用默认选项,【可跨来源比较】
# 非标准(1):可自定义选项(如保留互变异构体)
# → 【数据交换应该用标准 InChI】
InChIKey:固定长度的哈希
# InChI 可能很长,不便于数据库索引
# → InChIKey 是它的 27 字符哈希
#
# 对乙酰氨基酚:RZVAJINKPMORJF-UHFFFAOYSA-N
#
# 结构:AAAAAAAAAAAAAA-BBBBBBBBFV-P
#
# 前 14 位(AAAAAAAAAAAAAA):
# 【骨架层的哈希】—— 连接性
# 【不含立体化学与同位素】
# → 立体异构体的前 14 位【相同】
#
# 接下来 8 位(BBBBBBBB):
# 立体化学、同位素等信息的哈希
#
# 第 25 位(F):
# InChI 版本标志(S = 标准)
#
# 第 26 位(V):
# 质子化状态
#
# 最后 1 位(P):
# 校验位
#
# 【最实用的性质】:
# 前 14 位相同 = 【连接性相同】(可能是立体异构体)
# → 用于「找同一化合物的所有立体异构体」
# 全 27 位相同 = 【完全相同的结构】
from rdkit import Chem
def get_inchi_info(smiles):
mol = Chem.MolFromSmiles(smiles)
if mol is None:
return None
inchi = Chem.MolToInchi(mol)
key = Chem.InchiToInchiKey(inchi)
return {
"inchi": inchi,
"inchikey": key,
"skeleton": key.split("-")[0], # 前 14 位
}
# 比较立体异构体
a = get_inchi_info("N[C@@H](C)C(=O)O") # L-丙氨酸
b = get_inchi_info("N[C@H](C)C(=O)O") # D-丙氨酸
print(a["skeleton"] == b["skeleton"]) # True(连接性相同)
print(a["inchikey"] == b["inchikey"]) # False(立体化学不同)
用于数据库检索与匹配
# 【典型场景:把两个数据源的化合物匹配起来】
#
# 【为什么不能直接用 SMILES】:
# 不同来源的 SMILES 写法不同
# 即使规范化,不同工具的规范算法也可能不同
# → 【InChIKey 由 IUPAC 算法唯一确定,跨工具一致】
import pandas as pd
from rdkit import Chem
from rdkit.Chem.MolStandardize import rdMolStandardize
def to_inchikey(smiles, standardize=True):
mol = Chem.MolFromSmiles(smiles)
if mol is None:
return None
if standardize:
# 【先标准化再算 InChIKey】
mol = rdMolStandardize.Cleanup(mol)
mol = rdMolStandardize.FragmentParent(mol) # 去盐
try:
return Chem.InchiToInchiKey(Chem.MolToInchi(mol))
except Exception:
return None
# 匹配两个数据源
df_a["inchikey"] = df_a["smiles"].apply(to_inchikey)
df_b["inchikey"] = df_b["smiles"].apply(to_inchikey)
merged = df_a.merge(df_b, on="inchikey", how="inner",
suffixes=("_a", "_b"))
print(f"匹配上 {len(merged)} 个化合物")
# 【放宽匹配:忽略立体化学】
df_a["skeleton"] = df_a["inchikey"].str[:14]
df_b["skeleton"] = df_b["inchikey"].str[:14]
loose = df_a.merge(df_b, on="skeleton", how="inner")
# 【也可以用 InChIKey 在网上检索】:
# PubChem、ChemSpider 等都支持 InChIKey 检索
# → 用它查一个未知化合物的信息很方便
已知的边界情况
| 情况 | InChI 的处理 | 注意 |
|---|---|---|
| 互变异构体 | 标准 InChI 会归一化部分互变异构 | 不是全部——某些互变异构体仍是不同的 InChI |
| 盐 | 作为多组分处理 | 与游离形式的 InChIKey 不同 |
| 电荷 | 有专门的层 | 不同质子化态 = 不同 InChIKey |
| 金属配位 | 处理有局限 | 配位键的表示不完善 |
| 聚合物 | 不支持 | — |
| 大分子 | 可行但字符串很长 | 蛋白通常不用 InChI |
| 哈希碰撞 | 理论上可能 | 概率极低,实践中可忽略 |
最需要注意的是盐型问题:CC(=O)[O-].[Na+](乙酸钠)与 CC(=O)O(乙酸)的 InChIKey 完全不同。做数据匹配时,通常应该先用 FragmentParent 去掉抗衡离子(见 047《去盐与去重复》)。
三种标识的分工
| SMILES | InChI | InChIKey | |
|---|---|---|---|
| 可读性 | 好 | 差 | 无 |
| 唯一性 | 需规范化,工具相关 | 算法保证 | 算法保证 |
| 长度 | 变长 | 变长(较长) | 固定 27 字符 |
| 可逆 | 是 | 是(可还原结构) | 否(哈希) |
| 主要用途 | 日常使用、计算 | 标准化交换 | 数据库索引、匹配 |
推荐的做法:数据库中同时存储规范 SMILES(用于计算)与 InChIKey(用于索引和匹配)。
实践建议
- 做数据匹配前先标准化:去盐、中和电荷、规范互变异构体(见 046《分子标准化》);
- 记录标准化的具体策略:不同的策略会得到不同的 InChIKey;
- 用前 14 位做宽松匹配:找立体异构体或忽略立体化学的比较;
- 不要用 InChIKey 做相似性比较:它是哈希,相似的分子哈希完全不同——相似性要用指纹(见 039《Tanimoto 相似性》);
- 注意 RDKit 的 InChI 支持:需要编译时包含 InChI 支持,某些精简安装可能缺失。
关键要点
- InChI 由 IUPAC 算法唯一确定,跨工具一致——这是它优于规范 SMILES 的关键;
- InChIKey 前 14 位是连接性哈希,立体异构体的前 14 位相同;
- 盐型与游离形式的 InChIKey 完全不同——匹配前必须先去盐;
- InChIKey 是哈希,不能用于相似性比较——相似性要用指纹。
延伸资源
- SMILES:031《SMILES 入门》;分子标准化:046《分子标准化》;去盐去重:047《去盐与去重复》;
- Tanimoto 相似性:039《Tanimoto 相似性》;ChEMBL:226《ChEMBL》。