- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
本文围绕 RDKit 官方文档 rdkit.Chem.Lipinski.rst 所描述的rdkit.Chem.Lipinski模块展开,完整覆盖该模块导出的全部描述符 API、Lipinski 氢键给体/受体的 SMARTS 判定规则,以及可旋转键的多种严格度定义;结合 Lipinski.py、Lipinski.cpp、Lipinski.h 与测试 UnitTestLipinski.py 的源码,帮助读者理解每个参数背后的计数原理,并能直接落地分子筛选中的"类药性"(drug-likeness)计算。
模块定位:Lipinski 参数在 RDKit 中的角色
原始文档 rdkit.Chem.Lipinski.rst 是一个 Sphinxautomodule指令页面:
rdkit.Chem.Lipinski module ========================== .. automodule:: rdkit.Chem.Lipinski :members: :undoc-members: :show-inheritance:它声明了模块的一句话定位(Lipinski.py 第 10 行的模块 docstring):"Calculation of Lipinski parameters for molecules"(计算分子的 Lipinski 参数)。模块被收录在 rdkit.Chem.rst 的rdkit.Chem子模块列表中(第 51 行)。Lipinski 参数即业界常说的"Rule of Five"(五规则)所依赖的四个量:分子量、LogP、氢键给体数(HBD)、氢键受体数(HBA)。其中分子量与 LogP 可由其他描述符(如Crippen.MolLogP)计算,本模块聚焦在氢键给体/受体、杂原子数与可旋转键数这几项图论/SMARTS 可直接计算的指标上,并额外提供环数、SP3 碳分数、立体中心等衍生描述符。
从源码结构看,整个模块的分工是:Python 层 Lipinski.py 负责在导入时一次性预编译 SMARTS 模式(注释写明 "on import build the SMARTS patterns so we only have to do it once"),并把公共 API 委托给 C++ 绑定的rdMolDescriptors;C++ 层 Lipinski.cpp 实现了真正的计数逻辑(SMARTS 子结构匹配 + 原子遍历)。
API 一览:Lipinski 模块导出的全部函数
结合 Lipinski.py 的源码,模块导出函数可分为两组:
| 函数 | 文档字符串 | version | 底层实现 |
|---|---|---|---|
NumHDonors(mol) | Number of Hydrogen Bond Donors | 1.0.0 | rdMolDescriptors.CalcNumHBD |
NumHAcceptors(mol) | Number of Hydrogen Bond Acceptors | 2.0.0 | rdMolDescriptors.CalcNumHBA |
NumHeteroatoms(mol) | Number of Heteroatoms | 1.0.0 | rdMolDescriptors.CalcNumHeteroatoms |
NumRotatableBonds(mol) | Number of Rotatable Bonds | 1.0.0 | rdMolDescriptors.CalcNumRotatableBonds |
NOCount(mol) | Number of Nitrogens and Oxygens | 1.0.0 | rdMolDescriptors.CalcNumLipinskiHBA |
NHOHCount(mol) | Number of NHs or OHs | 2.0.0 | rdMolDescriptors.CalcNumLipinskiHBD |
RingCount(mol) | 环数 | 1.0.0 | rdMolDescriptors.CalcNumRings |
HeavyAtomCount(mol) | Number of heavy atoms | 1.0.1 | mol.GetNumHeavyAtoms() |
FractionCSP3、NumAromaticRings、NumSaturatedRings、NumAromaticHeterocycles、NumAromaticCarbocycles、NumSaturatedHeterocycles、NumSaturatedCarbocycles、NumAliphaticRings、NumAliphaticHeterocycles、NumAliphaticCarbocycles、NumHeterocycles、NumBridgeheadAtoms、NumAmideBonds、NumAtomStereoCenters、NumHeterocycles、NumUnspecifiedAtomStereoCenters、NumSpiroAtoms、Phi | 同 C++ 端 docstring | 批量自动生成 |
最后一组函数值得注意其实现手法:Lipinski.py 第 83-99 行维护了一个_bulkConvert元组,通过getattr(rdMolDescriptors, txt)反射取出 C++ 端的CalcXXX函数,包装成 lambda、拷贝__doc__、从_XXX_version属性读取版本号,最后把Calc前缀去掉后写入模块局部命名空间(例如CalcFractionCSP3→FractionCSP3)。这套机制避免了为每个描述符手写重复代码,也从 C++ 端统一继承了文档与版本信息。
两套"传统/严格"定义的区分是使用时最容易踩坑的一点。Lipinski.py 第 25-27 行的注释明确指出:
NOTE: if you want traditional Lipinski numbers, you should use
NOCounts(below) instead ofHAcceptor
即:
- 传统 Lipinski 计数:HBA = 分子中 N 和 O 原子的总数(
NOCount),HBD = NH 或 OH 的总数(NHOHCount); - 化学语义更精细的计数:
NumHAcceptors/NumHDonors使用 SMARTS 模式识别真正具备氢键能力的原子(例如酰胺 N 不算受体,卤素不算受体,带正电的 N 不算受体等)。
C++ 端 Lipinski.cpp 中对应的实现非常直接——calcLipinskiHBA(第 77-85 行)就是遍历原子统计原子序数为 7 或 8 的个数;calcLipinskiHBD(第 87-95 行)统计 N/O 上的氢原子总数(getTotalNumHs(true),包含隐性氢)。而 Lipinski.h 中lipinskiHBAVersion = "1.0.0"、lipinskiHBDVersion = "2.0.0"表明这两套定义各自独立版本化。
氢键给体与受体的 SMARTS 定义
Lipinski.py 第 19-43 行内置了五组预编译 SMARTS。其中注释部分保留了 Daylight 风格的原始定义供追溯:
HDonor '[!#6;!H0;-0]' HAcceptor '[$([!#6;+0]);!$([F,Cl,Br,I]); !$([o,s,nX3]);!$([Nv5,Pv5,Sv4,Sv6])]' Heteroatom '[B,N,O,P,S,F,Cl,Br,I]'真正被采用的定义(文件第 29-43 行)为:
HDonorSmarts = Chem.MolFromSmarts('[$([N;!H0;v3]),$([N;!H0;+1;v4]),$([O,S;H1;+0]),$([n;H1;+0])]') HAcceptorSmarts = Chem.MolFromSmarts( '[$([O,S;H1;v2]-[!$(*=[O,N,P,S])]),' '$([O,S;H0;v2]),$([O,S;-]),$([N;v3;!$(N-*=!@[O,N,P,S])]),' '$([nH0,o,s;+0])]') HeteroatomSmarts = Chem.MolFromSmarts('[!#6;!#1]') RotatableBondSmarts = Chem.MolFromSmarts('[!$(*#*)&!D1]-&!@[!$(*#*)&!D1]') NHOHSmarts = Chem.MolFromSmarts('[#8H1,#7H1,#7H2,#7H3]') NOCountSmarts = Chem.MolFromSmarts('[#7,#8]')逐条解读这些模式的语义:
- HDonor:三价且带氢的 N(胺)、四价带正电且带氢的 N(铵盐)、带一个氢的中性 O/S(羟基/巯基)、芳香环上带氢的 n(吡咯型 N-H)。核心思想是"与 N/O/S 直接相连且可电离出 H 的位置"。
- HAcceptor:分五支——非羰基化的羟基氧/硫(
[O,S;H1;v2]且不双键连 O/N/P/S)、双键型 O/S(羰基、亚砜等)、带负电荷的 O/S(烷氧负离子)、三价 N 且不形成酰胺(!$(N-*=!@[O,N,P,S])排除了 N 连 C=O/C=N/C=S 的酰胺型 N,其中!@表示"非芳香",因此排除了芳香环中形式上连双键杂原子的 N 的情形)、芳香 n/o/s 中性原子。注释第 31 行记录了修订历史:"v2, 1-Nov-2008, GL : fix amide-N exclusion; remove Fs from definition"——即 2008 年修正了酰胺 N 被误判为受体的缺陷,并把 F 从受体定义中移除。 - RotatableBond:连接两个"非三键原子、度数不为 1"的原子间的非芳香单键。注释(第 37-40 行)特别提示了一个已知细节:该 SMARTS 不区分氘与氢——由于
Chem.MolFromSmiles默认去掉 H 但保留 D(D 留在图中并计入碳的度数),[2H]C([2H])([2H])C([2H])([2H])[2H]中的中心 C-C 键会被判为可旋转,与常规直觉可能不一致。 - NHOH / NOCount:分别对应传统五规则的 HBD/HBA 计数,与 C++ 端
calcLipinskiHBD/calcLipinskiHBA的原子遍历逻辑等价。
C++ 端 Lipinski.cpp 第 195-202 行用SMARTSCOUNTFUNC宏以几乎相同的模式实现了NumHBD(v2.0.1)与NumHBA(v2.0.2):
SMARTSCOUNTFUNC(NumHBD, "[N&!H0&v3,N&!H0&+1&v4,O&H1&+0,S&H1&+0,n&H1&+0]", "2.0.1"); SMARTSCOUNTFUNC(NumHBA, "[$([O,S;H1;v2]-[!$(*=[O,N,P,S])]),$([O,S;H0;v2]),$([O,S;-]),$(" "[N;v3;!$(N-*=!@[O,N,P,S])]),$([nH0X2,o,s;+0])]", "2.0.2"); SMARTSCOUNTFUNC(NumHeteroatoms, "[!#6;!#1]", "1.0.1"); SMARTSCOUNTFUNC(NumAmideBonds, "C(=[O;!R])N", "1.0.0");注意 C++ 端的 HBA 模式尾部是[nH0X2,o,s;+0](要求二价),而 Python 端写的是[nH0,o,s;+0],两者在绝大多数分子上结果一致,但版本号(2.0.2 vs 2.0.0)提示底层定义经历过演进——跨语言端对数值有严格一致性要求时,应以实际测试为准。
该宏(第 66-72 行)同时展示了实现的工程细节:用boost::flyweight缓存 SMARTS 模式,避免每次调用都重新SmartsToMol;且当模式含$(递归查询)时,ss_matcher::countMatches会先拷贝一次 matcher(第 41-46 行注释 "Recursive queries aren't thread safe"),以换取线程安全。
可旋转键:NonStrict / Strict / StrictLinkages 三种定义
可旋转键(rotatable bonds)是构象搜索、柔性分子建模和类药性评估中的关键量,RDKit 为它提供了三档定义,声明于 Lipinski.h 第 35-40 行:
enum NumRotatableBondsOptions { Default = -1, NonStrict = 0, Strict = 1, StrictLinkages = 2, };各档行为在 Lipinski.cpp 第 106-187 行实现:
- NonStrict(0):使用基础模式
[!$(*#*)&!D1]-,:;!@[!$(*#*)&!D1],即排除三键连接、端基原子(度数 1)与芳香键,其余均计入。 - Strict(1):在 NonStrict 基础上进一步排除端基卤素(CF3、CCl3、CBr3)、叔丁基(C(CH3)3)、甲基酯/酰胺(
CH3=O型,双向排除)、CH3型端基等,模式见第 117-122 行。头文件注释概括为:"a stricter definition of rotatable bonds is used, this excludes amides, esters, etc." - StrictLinkages(2):最严格的一档,针对环间连接键做了专门处理(第 126-140 行注释列出相对原始 GPS 计算器的主要变更):脂肪环碳间的单键一律可旋转(如
CC1CCCC(C)C1-C1C(C)CCCC1C中心键);连接环上的杂原子不再影响判定;Cc1cccc(C)c1-c1c(C)cccc1这类芳环间连接键不可旋转。实现上采用"基础匹配 − 对称环匹配 − 末端三键 − 非环酰胺"的减法策略,并对酰胺匹配用boost::dynamic_bitset做去重(第 166-180 行)。
一个值得注意的全局开关:第 98-102 行
#ifdef RDK_USE_STRICT_ROTOR_DEFINITION const NumRotatableBondsOptions DefaultStrictDefinition = Strict; #else const NumRotatableBondsOptions DefaultStrictDefinition = NonStrict; #endif也就是说,Default = -1时的实际行为取决于编译期选项RDK_USE_STRICT_ROTOR_DEFINITION是否开启。这也正是测试代码要做"探测器"式判断的原因(见下节)。
Python 端入口rdMolDescriptors.CalcNumRotatableBonds(mol, options)支持显式传入NumRotatableBondsOptions.NonStrict / Strict / StrictLinkages,而 Lipinski.py 中的NumRotatableBonds走的是 C++ 默认值路径。
实践示例:计算一组分子的 Lipinski 指标
基于上述 API,一段典型的"五规则"筛查代码(基于仓库测试同款数据first_200.props.sdf,其路径由RDConfig.RDDataDir定位)如下:
from rdkit import Chem, RDConfig from rdkit.Chem import Lipinski, Crippen, rdMolDescriptors import os path = os.path.join(RDConfig.RDDataDir, 'NCI', 'first_200.props.sdf') suppl = Chem.SDMolSupplier(path) def rule_of_five(m): logP = Crippen.MolLogP(m) nHBD = Lipinski.NumHDonors(m) nHBA = Lipinski.NumHAcceptors(m) return logP, nHBD, nHBA for m in suppl: if m is None: continue logP, hbd, hba = rule_of_five(m) # 传统 Lipinski 计数可与精细化计数对照 trad_hba = Lipinski.NOCount(m) trad_hbd = Lipinski.NHOHCount(m) rotors = Lipinski.NumRotatableBonds(m) heavy = Lipinski.HeavyAtomCount(m)要点说明:
- 仓库内的参考数据 first_200.props.sdf(NCI 前 200 个分子)带有
NUM_HDONORS、NUM_HACCEPTORS、NUM_HETEROATOMS、NUM_ROTATABLEBONDS、NUM_ROTATABLEBONDS_O、NUM_LIPINSKIHDONORS、NUM_LIPINSKIHACCEPTORS等属性列,可直接用于回归对照。 - 显式切换可旋转键档位时调用:
n_ns = rdMolDescriptors.CalcNumRotatableBonds(m, rdMolDescriptors.NumRotatableBondsOptions.NonStrict) n_st = rdMolDescriptors.CalcNumRotatableBonds(m, rdMolDescriptors.NumRotatableBondsOptions.Strict) - 若只想快速判断分子是否"通过"经典五规则(MW ≤ 500、LogP ≤ 5、HBD ≤ 5、HBA ≤ 10),可直接组合
Lipinski.HeavyAtomCount与Chem.Descriptors.MolWt、Crippen.MolLogP。
回归测试如何验证这些函数
UnitTestLipinski.py 是理解模块行为边界的最佳材料,其结构为:
test1(NCI 200 分子对照):先用一个探测器分子确定当前构建的默认可旋转键档位——m = Chem.MolFromSmiles("CC(C)(C)c1cc(O)c(cc1O)C(C)(C)C") if Lipinski.NumRotatableBonds(m) == 2: rot_prop = NonStrict # "NUM_ROTATABLEBONDS_O" else: rot_prop = Strict # "NUM_ROTATABLEBONDS"然后逐分子断言
NHOHCountvsNUM_LIPINSKIHDONORS、NOCountvsNUM_LIPINSKIHACCEPTORS、NumHDonorsvsNUM_HDONORS、NumHAcceptorsvsNUM_HACCEPTORS、NumHeteroatomsvsNUM_HETEROATOMS、NumRotatableBondsvs 探测出的rot_prop,并额外显式验证CalcNumRotatableBonds的 NonStrict 与 Strict 两档分别与 SDF 中两列属性相等。Java 端存在完全同构的测试 LipinskiTests.java,说明 C++/Python/Java 三层对 Lipinski 数值有一致性要求。testIssue2183420(历史 bug 回归):一组胺/酰胺断言,验证 HBA 定义在甲基胺(NC、CNC、CN(C)C,期望 1)、甲酰胺类(NC(=O)、NC(=O)C、CNC(=O)、CNC(=O)C,期望 1,即酰胺 N 不算受体)、尿烷型(O=CNC(=O)C,期望 2)等边界情形下的行为,正是上述 HAcceptor 模式"amide-N exclusion"修订的直接回归保障。testLipinskiLong(长测试,默认跳过):需-l命令行开关启用,使用 rdkit/Chem/test_data 下的PP_descrs_regress.csv(列 30/31/32 分别为 NumHDonors、NumHeteroatoms、NumRotatableBonds)与Block_regress.Lip.csv(列 1-4 为 NumHAcceptors、NumHDonors、NumHeteroatoms、NumRotatableBonds)做逐行数值回归,缺失数据点以 666.0 占位。
运行方式(在仓库 Python 环境下):
python -m unittest rdkit.Chem.UnitTestLipinski # 默认快速测试 python rdkit/Chem/UnitTestLipinski.py -l # 启用长回归数据集小结:使用本模块的三个关键点
- 选对"传统"还是"精细"计数:做与外部工具历史数值对齐(如 NCI 属性列、文献中原始五规则)时使用
NOCount/NHOHCount;做现代 ADME 特征工程时使用NumHAcceptors/NumHDonors(v2 系列定义,含酰胺 N 排除、卤素/氟不计数等修正)。源码注释(Lipinski.py 第 25-27 行)对此有明确的官方提示。 - 可旋转键的默认值依赖编译选项:
Default在RDK_USE_STRICT_ROTOR_DEFINITION宏开启时为 Strict、否则为 NonStrict;跨环境比较数值时应显式传入rdMolDescriptors.NumRotatableBondsOptions.Strict或.NonStrict,并留意 StrictLinkages 档对环间连接键的专门规则(Lipinski.cpp 第 126-140 行注释)。 - 版本可追溯:每个 Python 端函数都带
version属性(如NumHDonors.version == "1.0.0"、NHOHCount.version == "2.0.0"),C++ 端则以XXXVersion常量(如NumRotatableBondsVersion = "3.2.0",Lipinski.cpp 第 105 行)独立版本化,便于在描述符结果中记录计算定义、复现实验时核对口径。
- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
相关推荐
RDKit FragmentMatcher 模块详解:基于 SMARTS 的片段命中与排除匹配
RDKit FragmentMatcher 模块详解:基于 SMARTS 的片段命中与排除匹配 rdkit.Chem.FragmentMatcher 是 RDK
科学计算科研机器学习网盘直链下载助手新手指南:9 大网盘获取直链并交给下载器的完整流程
网盘直链下载助手新手指南:9 大网盘获取直链并交给下载器的完整流程 网盘直链下载助手(LinkSwift)是一个浏览器用户脚本,不是客户端。它把百度网盘、阿里云
前端RDKit 的 rdMolDraw2DQt 模块:用 Qt/QPainter 绘制 2D 分子结构的完整指南
RDKit 的 rdMolDraw2DQt 模块:用 Qt/QPainter 绘制 2D 分子结构的完整指南 rdkit.Chem.Draw.rdMolDraw
科学计算科研机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考