- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
本文介绍 RDKit Python 绑定中的rdkit.Chem.FunctionalGroups模块:如何从一份“标签 + SMARTS + 反应 SMARTS”的文本定义文件构建可定制的官能团层级(hierarchy),以及如何在分子上生成官能团计数指纹。读完本文,你将掌握BuildFuncGroupHierarchy与CreateMolFingerprint的完整参数语义、定义文件的格式规范与错误处理机制,并能结合源码理解指纹位的排布规则与层级缓存行为,把该模块用于分子分类、相似性分析或反应预处理(SimpleEnum)等场景。
该模块的在线文档页由 Sphinxautomodule指令直接生成,见 Docs/Book/source/rdkit.Chem.FunctionalGroups.rst;对应的实现代码是一个约 170 行的纯 Python 文件 rdkit/Chem/FunctionalGroups.py,依赖rdkit.Chem与RDConfig两个组件。
模块组成与核心 API
rdkit/Chem/FunctionalGroups.py对外暴露两类构件:
| 构件 | 类型 | 作用 |
|---|---|---|
BuildFuncGroupHierarchy(fileNm=None, data=None, force=False) | 函数 | 解析官能团定义文件(或内联文本),构建层级树并缓存 |
CreateMolFingerprint(mol, hierarchy) | 函数 | 对分子做子结构匹配,返回各官能团出现次数的列表型指纹 |
FGHierarchyNode | 类 | 层级节点,持有name、pattern(Mol 查询对象)、smarts、label、rxnSmarts、parent、children等属性 |
FuncGroupFileParseError | 异常 | 继承自ValueError,在定义文件不合法(行过短、标签重复、父节点缺失、SMARTS 无法解析)时抛出 |
FGHierarchyNode 的关键设计
节点定义见 rdkit/Chem/FunctionalGroups.py#L40-L64,有三个值得注意的实现细节:
- parent 使用弱引用:构造时
self.parent = weakref.ref(parent),避免了树节点之间的循环引用导致的对象不可回收问题; __len__递归计数:len(node)返回该节点自身加上所有后代节点的数量(前序计数),这个值直接决定了CreateMolFingerprint中该子树占据的指纹位数;pattern与smarts双份保存:解析时立即调用Chem.MolFromSmarts(smarts)生成可反复调用的查询对象,同时保留原始 SMARTS 字符串便于回溯排查。
定义文件格式:Functional_Group_Hierarchy.txt
BuildFuncGroupHierarchy在不传参时的默认数据源是安装数据目录下的 Data/Functional_Group_Hierarchy.txt:
fileNm = os.path.join(RDConfig.RDDataDir, 'Functional_Group_Hierarchy.txt')文件头注释明确了格式约定(见 Data/Functional_Group_Hierarchy.txt#L1-L11):
- 每行由制表符(tab)分隔,字段为:
Label(层级标签)\t SMARTS \t 显示名称 \t 可选的去除反应 Reaction SMARTS; - 以
//开头的行是注释,会被整行剥离后跳过; - SMARTS 的第一个原子必须是该官能团的特征原子(注释原文:“be sure to make the distinguishing feature of the functional group the first atom in the pattern”)——这一点之所以重要,是因为指纹计数的“去重”逻辑以匹配结果中的第一个原子为锚点(后文详述);
- 当某个官能团无法用单一 SMARTS 完整描述时(例如硼酸酯),第四列提供一条去除反应 Reaction SMARTS,供下游枚举模块把该基团从分子上“切掉”。
文件内容示例:
AcidChloride C(=O)Cl Acid Chloride AcidChloride.Aromatic [$(C-!@[a])](=O)(Cl) Aromatic AcidChloride.Aliphatic [$(C-!@[A;!Cl])](=O)(Cl) Aliphatic CarboxylicAcid C(=O)[O;H,-] Carboxylic acid CarboxylicAcid.Aromatic [$(C-!@[a])](=O)([O;H,-]) Aromatic CarboxylicAcid.AlphaAmino [$(C-[C;!$(C=[!#6])]-[N;...])](=O)([O;H,-]) alpha Amino Acid BoronicAcid [$(B-!@[#6])](O)(O) Boronic Acid [#6:1]-!@B:2[O:4]>>[#6:1][X].B:2[O:4]层级关系完全由点号化的标签表达(如AcidChloride.Aromatic表示AcidChloride的子节点),行首的空格缩进仅起可读性作用,解析时line.strip()会把缩进去掉。内置文件共定义了AcidChloride、CarboxylicAcid、SulfonylChloride、Amine、Alcohol、Aldehyde、Halogen、Azide、Nitro、TerminalAlkyne等顶层基团,其中Amine与Halogen的分支最深(三级甚至四级,如Halogen.Bromine.BromoKetone)。
BuildFuncGroupHierarchy:解析流程与错误处理
实现位于 rdkit/Chem/FunctionalGroups.py#L77-L138,逐行处理逻辑如下:
splitter = re.compile('\t+') # 一个或多个制表符作为分隔 line = line.split('//')[0] # 去掉注释 if len(splitL) < 3: raise FuncGroupFileParseError("Input line %d (%s) is not long enough." % (lineNo, repr(line))) label = splitL[0].strip() if label in groupDefns: raise FuncGroupFileParseError("Duplicate label on line %d." % lineNo) # 逐级检查 labelHierarchy(如 A.B.C 需 A、A.B 已存在) parent = groupDefns['.'.join(labelHierarchy[:-1])] # 或 None(顶层) patt = Chem.MolFromSmarts(smarts) if not patt: raise FuncGroupFileParseError('Smarts "%s" (line %d) could not be parsed.' % (smarts, lineNo))即模块会对四类问题显式报错:行字段不足 3 个、标签重复、子标签出现在父标签之前(父级“未定义”)、SMARTS 解析失败。所有报错都携带行号,方便定位坏数据。
缓存语义(容易被忽视的行为)
函数顶部维护模块级全局变量hierarchy / lastData / lastFilename:
if (not force and hierarchy and (not data or data == lastData) and (not fileNm or fileNm == lastFilename)): return hierarchy[:]这带来三条可验证的行为(与 rdkit/Chem/UnitTestFunctionalGroups.py#L69-L105 中的断言一一对应):
- 重复调用返回同一棵树的顶层列表副本:
return hierarchy[:]只复制顶层列表,节点对象本身共享。因此对外部返回值做hierarchy.pop(0)不会影响内部缓存,测试中显式验证了这一点; - 直接修改模块全局变量会“污染”后续结果:测试里执行
FunctionalGroups.hierarchy.pop(0)后再次调用(不 force)只返回 1 个顶层基团; force=True强制重新解析,恢复为完整层级。
CreateMolFingerprint:指纹位排布与计数规则
指纹生成分两步,核心实现见 rdkit/Chem/FunctionalGroups.py#L141-L167:
- 长度计算:
CreateMolFingerprint先对每个顶层节点求len(entry)(自身 + 全部后代),求和后分配res = [0] * totL; - DFS 填位:
_SetNodeBits按“节点 → 子节点们”的前序递归遍历,节点匹配命中时写入res[idx] = count,然后idx += 1再递归处理子节点;若父节点未命中,则整棵子树直接跳过(idx += len(node)),子节点位保持 0。
计数count本身有一个去重规则:
ms = mol.GetSubstructMatches(node.pattern) seen = {} for m in ms: if m[0] not in seen: count += 1 seen[m[0]] = 1即按匹配锚点原子(m[0],对应定义文件中“第一个原子是特征原子”的约定)去重。这样同一个苯环羧基不会因为芳香环上多个原子都能匹配该子结构而被重复计数,也解释了为什么定义文件的注释强制要求把特征原子放在第一位。
一个可直接复现的最小示例来自单元测试test1Basics(rdkit/Chem/UnitTestFunctionalGroups.py#L43-L67):
txt = """ AcidChloride\tC(=O)Cl\tAcid Chloride AcidChloride.Benzoyl\tC(=O)(Cl)c1ccccc1\tBenzoyl Amine\tN\tAmine Amine.Primary\t[N;H2]\tPrimary Amine.Primary.Aromatic\t[N;H2][a]\tPrimary Aromatic Amine.Aromatic\tN[a]\tAromatic """ hierarchy = FunctionalGroups.BuildFuncGroupHierarchy(data=txt) print(len(hierarchy)) # 2 个顶层基团 m = Chem.MolFromSmiles('ClC(=O)CCCNc1ccccc1') fp = FunctionalGroups.CreateMolFingerprint(m, hierarchy) print(fp) # [1, 0, 1, 0, 0, 1]指纹共 6 位:AcidChloride(1), Benzoyl(1), Amine(1), Primary(1), PrimaryAromatic(1), Aromatic(1)按 DFS 顺序排布;该分子同时命中酸氯(非苯甲酰型)与芳伯胺,故第 3、6 位为 1。对照第二个断言:OC(=O)CCC(丁酸)返回全零[0, 0, 0, 0, 0, 0]。
CreateMolFingerprint返回的是普通 Python 列表(而非ExplicitBitVect),元素值可大于 1,因此它本质上是官能团计数向量而非布尔位串;这一点也体现在模块自带的测试test4Hs(rdkit/Chem/UnitTestFunctionalGroups.py#L134-L148)中:它遍历 NCI 5K 数据集,断言加氢分子(Chem.AddHs)与隐式氢分子生成的指纹完全相等,保证该向量可用于分子分类而不受表示方式影响。
test2Comments则验证了注释行(//Amine.Primary.Aromatic...)被正确忽略后基团数从 4 降为 3。
rxnSmarts 的下游用途:SimpleEnum 反应预处理
定义文件第四列的去除反应 SMARTS 并非冗余信息。FGHierarchyNode.rxnSmarts在解析时保留,而实际消费方是简易枚举模块 rdkit/Chem/SimpleEnum/Enumerator.py:
EnumerateReaction(reaction, bbLists, ...)的默认funcGroupFilename就指向RDDataDir/Functional_Group_Hierarchy.txt;PreprocessReaction(reaction, funcGroupFilename, ...)会解析其中的查询定义并交给rdChemReactions.PreprocessReaction对反应做基团级标注。
例如硼酸的三行定义都带反应 SMARTS:[#6:1]-!@B:2[O:4] >> [#6:1][X].B:2[O:4](见 Data/Functional_Group_Hierarchy.txt#L39-L41),其语义是把 C–B 键“切断”,使硼酸基团可以被枚举框架当作独立取代基处理。
与 FragmentCatalog 使用的 FunctionalGroups.txt 的区别
仓库中存在两份易混淆的数据文件,注意区分:
- Data/Functional_Group_Hierarchy.txt:
rdkit.Chem.FunctionalGroups模块所用,含点号层级标签与可选反应 SMARTS,SMARTS 是“特征原子在首”的自含式查询; - Data/FunctionalGroups.txt:
FragmentCatalog(片段目录)所用,格式为Label \t SMARTS \t Notes,其 SMARTS 以通配连接符开头(如*-C(=O)[O;D1]表示羧酸),用于把分子碎片与官能团建立归属关系,官方入门文档 Docs/Book/GettingStartedInPython.rst 的 FragmentCatalog 一节即使用该文件(FragCatParams(1, 6, fName))。
二者用途与解析逻辑均不同,不可互换。
小结:何时选择该模块
从源码结构看,FunctionalGroups模块的定位是“用一份文本文件声明官能团体系,换取一个可计数、可定制、层级感知的分子描述符”。适用场景包括:
- 需要与某套自有官能团分类体系(药企内部基团表、天然产物骨架分类等)对齐的分子画像——只需新写一个同格式定义文件,调用
BuildFuncGroupHierarchy(fileNm=...)即可,无需改代码; - 需要计数而非“有/无”的场景(指纹位值可大于 1);
- 配合 SimpleEnum 做反应位点枚举时需要去除反应 SMARTS 的场景。
使用限制方面需注意:指纹位序随定义文件的行序与缩进层级变化而变化,跨运行比较指纹时务必固定同一份定义文件;模块级缓存意味着修改全局hierarchy后要用force=True才能恢复;返回值为列表而非位向量,做 Tanimoto 类相似度计算时需自行转换。
验证与延伸阅读路径:模块实现、内置定义文件、单元测试、SimpleEnum 消费者,以及由automodule生成 API 说明的 文档源页。
- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
相关推荐
网盘直链下载助手新手指南:9 大网盘获取直链并交给下载器的完整流程
网盘直链下载助手新手指南:9 大网盘获取直链并交给下载器的完整流程 网盘直链下载助手(LinkSwift)是一个浏览器用户脚本,不是客户端。它把百度网盘、阿里云
前端RDKit rdkit.Chem.Fragments 模块深度指南:基于 SMARTS 模式库的基团计数描述符
RDKit rdkit.Chem.Fragments 模块深度指南:基于 SMARTS 模式库的基团计数描述符 本篇技术文章围绕 RDKit 官方文档页 rdk
科学计算科研机器学习RDKit FeatFinderCLI 命令行工具指南:基于 fdef 定义文件批量标注分子药效团特征
RDKit FeatFinderCLI 命令行工具指南:基于 fdef 定义文件批量标注分子药效团特征 导读 rdkit.Chem.FeatFinderCLI
科学计算科研机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考