- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
导读
rdkit.ML.InfoTheory.entropy是 RDKit 机器学习工具集中用于信息论度量的核心模块,它提供了计算信息熵(informational entropy)、信息增益(information gain)与卡方统计量(chi-squared)的完整实现,并在此基础上支撑起分子指纹位点(bit)的活性排序、特征筛选等典型应用。读完本文,你将掌握该模块的纯 Python 实现与 C 加速实现的双层结构、底层数学定义与输入数据约定,并能直接调用InfoBitRanker对带活性标签的指纹集合进行位点排名。
该模块的官方文档入口位于 Docs/Book/source/rdkit.ML.InfoTheory.entropy.rst,实际源码位于 rdkit/ML/InfoTheory/entropy.py。由于该文档采用 Sphinx 的automodule指令动态提取源码 docstring,因此"文档"的实质内容完全等价于源码模块本身,本文以源码为准展开。
模块定位:一套面向化学信息学的信息论度量
entropy.py的开头明确说明:本模块所采用的定义与 Tom Mitchell 的经典著作《Machine Learning》一致,这意味着模块中的熵与信息增益公式遵循标准的香农信息论框架,而不是某种自定义变体。从源码结构看,模块本身是"薄封装"设计:
- 纯 Python 参考实现
PyInfoEntropy、PyInfoGain; - 尝试导入 C 扩展
rdkit.ML.InfoTheory.rdInfoTheory(即编译产物rdInfoTheory); - 若 C 扩展可用,则将
InfoEntropy/InfoGain指向 C 版本(hascEntropy = 1),否则回退到纯 Python 版本。
# 摘自 rdkit/ML/InfoTheory/entropy.py try: import rdkit.ML.InfoTheory.rdInfoTheory as cEntropy except ImportError: hascEntropy = 0 else: hascEntropy = 1 if hascEntropy: InfoEntropy = cEntropy.InfoEntropy InfoGain = cEntropy.InfoGain else: InfoEntropy = PyInfoEntropy InfoGain = PyInfoGain这一"优先 C、回退 Python"的模式保证了功能可用性与运行性能的平衡:C 扩展缺失时(例如仅安装纯 Python 环境)模块依然可以正常工作,只是计算速度较慢。
核心 API:InfoEntropy 与 InfoGain
InfoEntropy:一维计数数组的熵
InfoEntropy(results)接收一个一维计数数组,数组中每个元素表示"某一结果出现的次数"。例如某个函数有 3 种可能结果,命中次数分别为 5、6、1,则传入[5, 6, 1]。
纯 Python 实现的核心逻辑(entropy.py):
def PyInfoEntropy(results): nInstances = float(sum(results)) if nInstances == 0: return 0 # 空输入返回 0 熵 probs = results / nInstances t = numpy.choose(numpy.greater(probs, 0.0), (1, probs)) return sum(-probs * numpy.log(t) / _log2)其中_log2 = math.log(2),最终结果以bit(以 2 为底)为单位输出。代码中有一段值得注意的细节处理:由于log(0.0)会引发错误,而0 * log(0)在数学约定上应视为 0,模块通过numpy.choose(numpy.greater(probs, 0.0), (1, probs))将概率为 0 的位置替换为 1,从而安全地跳过对数运算。源码注释还保留了一个"更快的风险方案"(给概率加1e-10微小量)作为历史对照,但当前实现选择了数值上完全安全的方案。
InfoGain:二维列联矩阵的增益
PyInfoGain(varMat)接收一个Numeric 二维数组,行对应变量的各个取值,列对应各个可能结果。例如某变量可取 4 个值、结果有 3 种,则varMat为 4×3。内部先按行求和得到variableRes(即 Mitchell 记法中的 $S_v$),按列求和得到overallRes(即 $S$),再计算:
def PyInfoGain(varMat): variableRes = numpy.sum(varMat, 1) # 每行之和 Sv overallRes = numpy.sum(varMat, 0) # 每列之和 S term2 = 0 for i in range(len(variableRes)): term2 = term2 + variableRes[i] * InfoEntropy(varMat[i]) tSum = sum(overallRes) if tSum != 0.0: term2 = 1. / tSum * term2 gain = InfoEntropy(overallRes) - term2 else: gain = 0 return gain即 $Gain(S, A) = Entropy(S) - \sum_{v \in Values(A)} \frac{|S_v|}{|S|} Entropy(S_v)$。当总样本数为 0 时返回 0,避免除零。
底层原理:C++ 模板实现的同一套公式
当 RDKit 以完整方式编译时,InfoEntropy与InfoGain实际调用的是 C++ 模板函数,定义于 Code/ML/InfoTheory/InfoGainFuncs.h。该头文件位于RDInfoTheory命名空间内,提供三个模板函数:
InfoEntropy(T *tPtr, long int dim):先累加nInstances求总样本数,再逐项计算-d * log(d),最后除以log(2.0)换算成以 2 为底的结果;对d == 0的项直接跳过,与 Python 版处理思路一致;InfoEntropyGain(T *dMat, long int dim1, long int dim2):先求行和(variableRes)与列和(overallRes),再按term2 += variableRes[i] * InfoEntropy(该行)累加加权熵,最终gain = InfoEntropy(overallRes) - term2 / tSum,与PyInfoGain数学等价;ChiSquare(T *dMat, long int dim1, long int dim2):按列联表公式 $\chi^2 = \sum_i \frac{N}{R_i}\sum_j \frac{N_{ij}^2}{C_j} - N$ 计算卡方统计量(头文件注释中给出了 3×3 列联表的Nij / Ri / Cj / N符号说明)。
Python 绑定位于 Code/ML/InfoTheory/Wrap/rdInfoTheory.cpp(传统 boost.python 版)与 Code/ML/InfoTheory/nbWrap/rdInfoTheory.cpp(nanobind 版),通过BOOST_PYTHON_MODULE(rdInfoTheory)/NB_MODULE(rdInfoTheory)导出InfoEntropy、InfoGain、ChiSquare三个函数以及InfoBitRanker、BitCorrMatGenerator两个类。模块级 docstring 为:"Module containing bunch of functions for information metrics and a ranker to rank bits"(一组信息度量函数与一个位点排序器)。
输入类型与 dtype 处理
绑定层对输入数组做了严格约束:
InfoEntropy要求一维数组(minDim = maxDim = 1),且维度必须大于 0;InfoGain与ChiSquare要求二维数组(minDim = maxDim = 2);- 非数组对象会抛出
ValueError("Expecting a Numeric array object")。
在 boost.python 版本中,nativeOrDoubleType对NPY_DOUBLE / NPY_FLOAT / NPY_INT / NPY_LONG / NPY_LONGLONG使用原生指针直读,其他类型统一转换为 double;nanobind 版本则显式支持int8/16/32/64、uint8/16/32/64、float、double共 10 种 dtype。这一设计的背景可在 Code/ML/InfoTheory/Wrap/rdInfoTheory.cpp 的注释中看到:NPY_LONG在 Windows 上只有 32 位,而 numpy 默认整数类型是NPY_LONGLONG,历史上曾因类型未被识别而静默返回 0.0(GitHub issue #8421),因此现在对常见 dtype 全覆盖。
实战检验:用测试用例校准你的计算结果
Code/ML/InfoTheory/Wrap/testRanker.py 提供了完整的数值基准,可作为自测标准:
rdit.InfoEntropy(numpy.array([9, 5], float)) # == 0.9403 rdit.InfoEntropy(numpy.array([9, 9], float)) # == 1.0000 rdit.InfoEntropy(numpy.array([5, 0], float)) # == 0.0000(单类别无信息量) rdit.InfoEntropy(numpy.array([5, 5, 5], float)) # == 1.5850(三类别均匀分布) rdit.InfoEntropy(numpy.array([2, 5, 5], float)) # == 1.4834 rdit.InfoGain(numpy.array([[6, 2], [3, 3]], float)) # == 0.0481 rdit.ChiSquare(numpy.array([[6, 2], [3, 3]], float)) # == 0.9333测试还覆盖了边界情况:全零矩阵[[0,0],[0,0]]的InfoGain为 0,两列完全一致时增益为 0;同时验证了从int8到float64的多种 dtype 下结果一致(testGainFunDtypes)。
实战进阶:InfoBitRanker 指纹位点排序
entropy模块最典型的应用场景是对分子指纹的位点(bit)做活性相关度排序。核心类InfoBitRanker定义于 Code/ML/InfoTheory/InfoBitRanker.h,其用法为"三部曲":
- 构造:
InfoBitRanker(nBits, nClasses, infoType),其中nBits为指纹长度,nClasses为类别数(类编号约定为 0 到 nClasses-1); - 累加投票:对每个带标签的指纹调用
AccumulateVotes(bv, label),统计每个位点在各类别中的命中次数; - 取排名:调用
GetTopN(num)返回得分最高的num个位点及其统计量。
四种度量类型
构造器的第三个参数infoType通过枚举InfoType选择度量(InfoBitRanker.h):
| 枚举值 | 含义 | 底层函数 |
|---|---|---|
ENTROPY | 默认,信息熵增益 | InfoEntropyGain |
BIASENTROPY | 偏向指定类别的熵增益 | BiasInfoEntropyGain |
CHISQUARE | 卡方统计量 | ChiSquare |
BIASCHISQUARE | 偏向指定类别的卡方 | BiasChiSquareGain |
四种模式的分发逻辑位于 InfoBitRanker.cpp 的switch (d_type)语句中。每次计算时,代码将每个位点组织成一个 2×nClasses 的列联表:第一行为"该类命中该位点的样本数",第二行为"该类未命中该位点的样本数"(即d_clsCount[j] - d_counts[j][i]),再交给对应的度量函数。
官方示例:4 个 4 位指纹的排序演示
InfoBitRanker.h 的类注释给出了可直接复现的完整示例。设数据为:
指纹位串 活性标签 0001 0 0101 0 0010 1 1110 1默认熵增益排序(InfoBitRanker(4, 2)):
>>> ranker = InfoBitRanker(4, 2) >>> for i, bv in enumerate(bvs): ranker.AccumulateVotes(bv, acts[i]) >>> for bit, gain, n0, n1 in ranker.GetTopN(3): ... print(int(bit), '%.3f' % gain, int(n0), int(n1)) 3 1.000 2 0 2 1.000 0 2 0 0.311 0 1偏向类别 1 的熵增益(InfoBitRanker(4, 2, InfoType.BIASENTROPY)+SetBiasList((1,))):
2 1.000 0 2 0 0.311 0 1 1 0.000 1 1卡方排序(InfoType.CHISQUARE):
3 4.000 2 0 2 4.000 0 2 0 1.333 0 1偏向类别 1 的卡方(InfoType.BIASCHISQUARE+SetBiasList((1,))):
2 4.000 0 2 0 1.333 0 1 1 0.000 1 1GetTopN的返回是一个形状为num × (2 + nClasses)的二维数组,每行依次为:位点 ID、度量得分、各类别命中数。注意num不能超过指纹位数,否则抛出ValueError("attempt to rank more bits than present in the bit vectors")(InfoBitRanker.cpp);若设置了掩码位,num还不能超过掩码位总数。
偏置机制与掩码
SetBiasList(classList)用于 BIASENTROPY / BIASCHISQUARE 模式:只有当偏置类别中至少一个类别的位点命中率不低于所有非偏置类别时,该位点才参与评分。其判定逻辑BiasCheckBit(InfoBitRanker.cpp)先计算各类别命中率fracs[i] = resMat[i] / d_clsCount[i],取非偏置类别的最大命中率maxCor,再检查偏置类别中是否有命中率>= maxCor。这在"活性优先"场景中非常有用——例如你只关心富集活性分子的位点,而不是均衡区分两类。
SetMaskBits(maskBits)则限定只对指定位点集合进行评估,accumulateVotes在计数时会同时检查dp_maskBits掩码(InfoBitRanker.cpp)。此外还提供WriteTopBitsToFile(fileName)将排名结果以表格形式(Bit / InfoContent / class0 / class1 ...)输出到文件。
测试 testRanker.py 中的test1ranker/test2ranker展示了随机指纹上的典型用法:构造 100 个 100 位稀疏指纹,10% 标记为活性(label=1),AccumulateVotes后GetTopN(50);test2ranker验证了SetMaskBits([63, 70, 15, 25, 10])后排名结果恰好是这 5 个位点,且请求GetTopN(10)会抛出异常;test3Issue140/test4Issue237则是针对历史缺陷(如 Linux 上偏置排序导致 core dump)的回归测试。
纯 Python 配套工具:BitRank 模块
如果你的环境没有编译 C 扩展,rdkit/ML/InfoTheory/BitRank.py 提供了纯 Python 的位点分析工具链,它们直接复用entropy.InfoGain:
FormCounts(bitVects, actVals, whichBit, nPossibleActs, nPossibleBitVals=2):为单个位点生成 nPossibleBitVals × nPossibleActs 的计数矩阵(内部使用,默认按二值位处理);CalcInfoGains(bitVects, actVals, nPossibleActs, nPossibleBitVals=2):逐个位点调用FormCounts与entropy.InfoGain,返回每个位点的增益数组;RankBits(bitVects, actVals, nPossibleBitVals=2, metricFunc=CalcInfoGains):按增益降序返回位点排序与度量值;AnalyzeSparseVects(bitVects, actVals)/SparseRankBits(...):面向SparseBitVect的稀疏优化版本,只遍历开位(on bits),返回(bit, gain, nAct, nInact)列表。
这些函数要求bitVects与actVals长度一致,否则抛出ValueError('var and activity lists should be the same length')。
延伸组件:BitCorrMatGenerator 与 BitClusterer
排序出"高价值位点"之后,常需要分析位点之间的相关性。BitCorrMatGenerator(Code/ML/InfoTheory/CorrMatGenerator.h)统计指定位点集合中两两同时出现的次数:
cmg = BitCorrMatGenerator() cmg.SetBitList(blist) # 例如:上一轮 GetTopN 选出的位点 for fp in fpList: cmg.CollectVotes(fp) # 支持 ExplicitBitVect / SparseBitVect corrMat = cmg.GetCorrMatrix() # 一维数组,仅含对称矩阵的下三角元素其 Python 绑定与文档字符串见 Code/ML/InfoTheory/Wrap/BitCorrMatGenerator.cpp。
在此基础上,rdkit/ML/InfoTheory/BitClusterer.py 的BitClusterer类将相关位点聚成簇:ClusterBits(corrMat)先把相关矩阵取倒数转为距离矩阵(distMat = 1 / corrMat),再交给rdSimDivPickers的HierarchicalClusterPicker做层次聚类(默认ClusterMethod.WARD);随后可用MapToClusterScores(fp)把指纹映射为"每个簇命中位数"的实值向量,或用MapToClusterFP(fp)压缩成更小尺寸的簇级指纹——这一步可以把数百个原始位点浓缩成少量去相关特征,供下游建模使用。
小结与使用建议
rdkit.ML.InfoTheory.entropy及其配套的InfoBitRanker、BitCorrMatGenerator构成了 RDKit 中一套完整的信息论特征筛选流水线:
- 用
InfoEntropy/InfoGain/ChiSquare快速评估任意变量与结果的相关强度; - 用
InfoBitRanker对带活性标签的指纹集合做位点排序,通过BIASENTROPY/BIASCHISQUARE实现活性偏向,通过SetMaskBits限定候选集; - 用
BitCorrMatGenerator+BitClusterer消除位点冗余,压缩特征空间。
实际使用时注意三点:输入数组必须为 numpy 数组且维度正确(一维给InfoEntropy,二维给InfoGain/ChiSquare);GetTopN的请求数量不能超过指纹位数或掩码位总数;偏置模式下类 ID 必须在[0, nClasses)范围内且不能重复。如需验证实现正确性,可对照 testRanker.py 中给出的数值基准(如InfoEntropy([9,5]) = 0.9403、InfoGain([[6,2],[3,3]]) = 0.0481)逐项核对。
- 科学计算
- 科研
- 机器学习
【免费下载链接】rdkit
The official sources for the RDKit library
相关推荐
RDKit 信息论模块(rdkit.ML.InfoTheory)完全指南:熵、信息增益、位点排名与位点聚类
RDKit 信息论模块(rdkit.ML.InfoTheory)完全指南:熵、信息增益、位点排名与位点聚类 本文围绕 RDKit 官方文档 rdkit.ML.I
科学计算科研机器学习RDKit 信息论模块 rdkit.ML.InfoTheory.rdInfoTheory 完全指南:信息熵、信息增益、卡方统计与指纹位排名
RDKit 信息论模块 rdkit.ML.InfoTheory.rdInfoTheory 完全指南:信息熵、信息增益、卡方统计与指纹位排名 本篇指南围绕 RDK
科学计算科研机器学习RDKit 信息论位点排名(BitRank)实战指南:用信息增益筛选分子指纹特征位
RDKit 信息论位点排名(BitRank)实战指南:用信息增益筛选分子指纹特征位 本篇技术指南聚焦 RDKit 中 rdkit.ML.InfoTheory.B
科学计算科研机器学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考