033

InChI 与 InChIKey:化合物唯一标识怎么用于数据库检索

InChI 与 InChIKey 提供了化合物的唯一标识。这篇讲清它们的分层结构、检索用法与已知的边界情况。

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 是哈希,不能用于相似性比较——相似性要用指纹。

延伸资源