我创建了两个分类器,一个是提升决策树(BDT),另一个是神经网络(NN),用于将事件分类为信号类或背景类。它们输出一个介于0到1之间的连续概率,表示属于信号类的可能性。我想比较这两种方法,并希望找出它们之间的相关性。
然而,我发现如果我只计算属于背景类的事件的相关系数,或者只计算属于信号类的事件的相关系数,这些相关系数都小于整个数据集的相关系数。我原本以为由于两个分类器是在完全相同的数据集上测试的,总体相关性应该是两个独立相关性的加权平均值。请注意,总数据集包含大约100,000个事件。
在这里,我使用pandas的.corr()函数计算整个数据集的相关性,该函数计算皮尔逊相关系数矩阵:
dfBDT = pd.read_csv("BDTResults.csv")dfNN = pd.read_csv("NNResults.csv")# not defaulted by Event Number by defaultdfBDT = dfBDT.sort_values('EventNumber')dfNN = dfNN.sort_values('EventNumber')# Resets index of sorted dataframe so sorted dataframe index begins at 0dfBDT.reset_index(drop=True, inplace=True)dfNN.reset_index(drop=True, inplace=True)dfscore = pd.concat([dfBDT['score'],dfNN['score']], axis = 1)dfnum = pd.concat([dfBDT['EventNumber'],dfNN['EventNumber']], axis = 1)dfTotal = pd.concat([dfnum,dfscore], axis = 1)dfTotal.columns = ['EventNumberBDT', 'EventNumberNN', 'BDT', 'NN']dfTotal.corr()
这给出了97%的相关性。然后,我对背景事件做了同样的事情,我定义背景事件的类别为0:
BDT_back = (dfBDT.loc[dfBDT['Class'] == 0])['score']BDT_back.reset_index(drop=True, inplace=True)BDT_back_num = (dfBDT.loc[dfBDT['Class'] == 0])['EventNumber']BDT_back_num.reset_index(drop=True, inplace=True)NN_back = (dfNN.loc[dfNN['Class'] == 0])['score']NN_back.reset_index(drop=True, inplace=True)NN_back_num = (dfBDT.loc[dfBDT['Class'] == 0])['EventNumber']NN_back_num.reset_index(drop=True, inplace=True)dfBack = pd.concat([BDT_back_num,NN_back_num,BDT_back,NN_back], axis = 1)dfBack.reset_index(drop=True, inplace=True)dfBack.columns = ['EventNumberBDT','EventNumberNN','BDT','NN']dfBack.corr()
这给了我大约96%的相关性。然后,我对信号事件重复上述操作,即将类别=0改为类别=1,我得到的相关性为91%。
然后,如果我尝试重新合并这两个数据框并再次计算总体相关性,我得到的相关性比之前高,达到了98%:
ab = pd.concat([dfBack['BDT'],dfSig['BDT']])ba = pd.concat([dfBack['NN'],dfSig['NN']])abba =pd.concat([ab,ba], axis = 1)abba.corr()
这两个值的不同表明一定是哪里出了问题,但我不知道问题出在哪里。
回答:
归根结底,这取决于基于索引运行的水平合并操作。
未匹配的行
如果两个数据框的行不同,默认执行外连接的concat
会在未匹配的索引处(在行数较少的数据框上)生成NaN
,这将比拆分前的原始数据框多出更多行。
未匹配的类别
此外,如果Class在两个数据框dfBDT和dfNN之间有不同的百分比份额,它们的相应连接将在未匹配的索引处返回NaN
。
例如,假设dfBDT在类别0和1之间保持60%和40%的比例,而dfNN在类别0和1之间保持50%和50%的比例,其中比较包括:
- BDT类别0将有更多行比NN类别0
- BDT类别1将有更少行比NN类别1
在使用pd.concat(..., axis = 1)
进行水平连接后,默认外连接how = 'outer'
,产生的错配将在双方生成NaN
。即使你使用how='inner'
,你也会过滤掉错配,但dfTotal从不过滤掉任何行,而是包括所有行。
排序顺序
在Linux和Windows机器上使用种子可复制的示例进行测试表明,排序很重要,特别是先按Class
然后按EventNumber
排序很重要。
这可以通过种子随机数据进行可复制的示例来演示。下面的代码重构了你的代码,以避免多次调用pd.concat
,而是使用join
(将其默认值调整为how='outer'
)。再往下,这段代码与原始设置等效。
数据
import numpy as npimport pandas as pdnp.random.seed(2292020)dfBDT = pd.DataFrame({'EventNumber': np.random.randint(1, 15, 500), 'Class': np.random.randint(0, 1, 500), 'score': np.random.randn(500) })dfNN = pd.DataFrame({'EventNumber': np.random.randint(1, 15, 500), 'Class': np.random.randint(0, 1, 500), 'score': np.random.randn(500) })
代码
dfBDT = dfBDT.sort_values(['Class', 'EventNumber']).reset_index(drop=True) dfNN = dfNN.sort_values(['Class', 'EventNumber']).reset_index(drop=True) # 所有行(无过滤)dfTotal = (dfBDT.reindex(['EventNumber', 'score'], axis='columns') .join(dfNN.reindex(['EventNumber', 'score'], axis='columns'), rsuffix = '_') .set_axis(['EventNumberBDT', 'BDT', 'EventNumberNN', 'NN'], axis='columns', inplace = False) .reindex(['EventNumberBDT','EventNumberNN','BDT','NN'], axis='columns')) dfTotal.corr()# 两个过滤后的数据框类别(0表示背景,1表示信号)df_list = [(dfBDT.query('Class == {}'.format(i)) .reindex(['EventNumber', 'score'], axis='columns') .join(dfNN.query('Class == {}'.format(i)) .reindex(['EventNumber', 'score'], axis='columns'), rsuffix = '_') .set_axis(['EventNumberBDT', 'BDT', 'EventNumberNN', 'NN'], axis='columns', inplace = False) .reindex(['EventNumberBDT','EventNumberNN','BDT','NN'], axis='columns') ) for i in range(0,2)]dfSub = pd.concat(df_list)dfSub.corr()
输出 (注意它们返回不同的结果)
dfTotal.corr()# EventNumberBDT EventNumberNN BDT NN# EventNumberBDT 1.000000 0.912279 -0.024121 0.115754# EventNumberNN 0.912279 1.000000 -0.039038 0.122905# BDT -0.024121 -0.039038 1.000000 0.012143# NN 0.115754 0.122905 0.012143 1.000000dfSub.corr()# EventNumberBDT EventNumberNN BDT NN# EventNumberBDT 1.000000 0.974140 -0.024121 0.120102# EventNumberNN 0.974140 1.000000 -0.026026 0.122905# BDT -0.024121 -0.026026 1.000000 0.025548# NN 0.120102 0.122905 0.025548 1.000000