如何在sklearn中高效计算二维布尔数组所有特征列间的Matthews相关系数
如何在sklearn中高效计算二维布尔数组所有特征列间的Matthews相关系数
嗨,我完全理解你的困扰——当特征维度很高时,用双重循环逐对调用matthews_corrcoef确实会慢到难以接受,毕竟每一次函数调用都有额外开销,而且Python的循环本身就不适合处理这种大规模的成对计算场景。
其实我们可以直接基于Matthews相关系数(MCC)的数学公式,通过向量化运算一次性计算所有特征对的MCC值,彻底摆脱循环的低效问题。
先回顾MCC的核心公式
对于两个布尔特征列(0/1取值),MCC的计算公式是:
MCC = (TP*TN - FP*FN) / sqrt( (TP+FP)*(TP+FN)*(TN+FP)*(TN+FN) )
其中:
- TP:两个特征都为1的样本数
- TN:两个特征都为0的样本数
- FP:第一个特征为1、第二个为0的样本数
- FN:第一个特征为0、第二个为1的样本数
向量化实现步骤
我们可以利用矩阵运算直接批量计算所有特征对的这些统计量:
- 先计算每个特征的1值总数,以及总样本数:
import numpy as np X = np.array([[1, 0, 0, 0, 0], [1, 0, 0, 1, 0], [1, 0, 0, 0, 1], [1, 1, 0, 0, 0], [1, 1, 0, 1, 0], [1, 1, 0, 0, 1], [1, 0, 1, 0, 0], [1, 0, 1, 1, 0], [1, 0, 1, 0, 1], [1, 0, 0, 0, 0]]) n_samples = X.shape[0] # 每个特征的1值数量 s = X.sum(axis=0)
- 批量计算所有特征对的TP值(两个特征都为1的样本数,等价于X的矩阵乘法):
# X是0/1矩阵,X @ X.T的(i,j)位置就是第i列和第j列的点积,即TP数量 TP = X @ X.T
- 基于TP推导其他统计量:
FP = s[None, :] - TP # 第i列1、第j列0的数量 FN = s[:, None] - TP # 第i列0、第j列1的数量 TN = n_samples - s[None, :] - s[:, None] + TP # 都为0的数量
- 计算MCC矩阵,同时处理分母为0的情况(避免除以0错误):
# 计算分子 numerator = TP * TN - FP * FN # 计算分母 denominator = np.sqrt( (TP + FP) * (TP + FN) * (TN + FP) * (TN + FN) ) # 处理分母为0的情况,直接设为极小值(此时MCC无意义,通常取0) denominator[denominator == 0] = 1e-10 # 计算MCC矩阵,取绝对值 mcc_matrix = np.abs(numerator / denominator)
- 提取上三角区域的非对角线元素(避免重复计算,对应你原来循环里的i<j的情况),然后计算平均值:
# 生成上三角掩码,排除对角线 upper_tri_mask = np.triu(np.ones_like(mcc_matrix), k=1) # 提取所有符合条件的MCC值 rff_all = mcc_matrix[upper_tri_mask == 1] # 计算平均值 rff = np.mean(rff_all)
为什么这个方法更快?
整个过程都是基于numpy的向量化运算,numpy的底层是C实现的,比Python的循环快几个数量级,尤其是当特征数量很大时(比如几百上千个特征),性能提升会非常明显。你可以对比一下原来的循环方法和这个向量化方法的运行时间,对于大规模数据集,差异会非常显著。
备注:内容来源于stack exchange,提问作者Erwin
相关产品推荐
相关产品推荐

