Python 总人口的相关系数大于人口样本

Python 总人口的相关系数大于人口样本,python,pandas,machine-learning,correlation,Python,Pandas,Machine Learning,Correlation,我创建了两个分类器,一个增强决策树(BDT)和一个神经网络(NN),用于将事件分类为属于信号类或背景类。它们输出属于信号类的0到1之间的连续概率。我想比较这两种方法,并希望找到两者之间的相关性 但是我发现,如果我只计算属于背景类的事件的相关系数,或者只计算属于信号类的事件的相关系数,那么这些相关性比整个数据集的相关性要小。我假设两个分类器在完全相同的数据集上进行测试,总相关性将是两个单独相关性的加权平均值。请注意,整个数据集由大约100000个事件组成 这里,我使用pandas.corr()函数

我创建了两个分类器,一个增强决策树(BDT)和一个神经网络(NN),用于将事件分类为属于信号类或背景类。它们输出属于信号类的0到1之间的连续概率。我想比较这两种方法,并希望找到两者之间的相关性

但是我发现,如果我只计算属于背景类的事件的相关系数,或者只计算属于信号类的事件的相关系数,那么这些相关性比整个数据集的相关性要小。我假设两个分类器在完全相同的数据集上进行测试,总相关性将是两个单独相关性的加权平均值。请注意,整个数据集由大约100000个事件组成

这里,我使用pandas.corr()函数计算整个数据集的相关性,该函数计算Pearson相关性矩阵:

dfBDT = pd.read_csv("BDTResults.csv")
dfNN = pd.read_csv("NNResults.csv")

# not defaulted by Event Number by default
dfBDT = dfBDT.sort_values('EventNumber')
dfNN = dfNN.sort_values('EventNumber')

# Resets index of sorted dataframe so sorted dataframe index begins at 0
dfBDT.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%的相关性。然后我对信号事件重复上述步骤,即用class=1替换class=0,得到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
,这将比拆分前的原始数据帧多行

不匹配的类

此外,如果类在两个数据帧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='internal
”,您也会过滤掉不匹配的数据,但dfTotal不会过滤掉任何行,而是包含所有

排序顺序

在Linux和Windows机器之间使用种子、可复制的示例进行测试表明排序问题,特别是按
先排序,然后再按
事件数
问题排序


这可以通过可复制示例的种子随机数据来证明。下面对代码进行重构,以避免使用
join
(将其默认值调整为
how='outer'
)进行大量的
pd.concat
调用。再往下看,这段代码相当于OP的原始设置

数据

import numpy as np
import pandas as pd

np.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)  

# ALL ROWS (NO FILTER)
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()

# TWO FILTERED DATA FRAMES CLASS (0 FOR BACKGROUND, 1 FOR SIGNAL)
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()
输出(注意它们返回不同的结果)

然而,如果我们将类共享相等(例如两个数据帧中的50%和50%或两个数据帧中的任何等效共享),则输出完全匹配

np.random.seed(2292020)
dfBDT = pd.DataFrame({'EventNumber': np.random.randint(1, 15, 500),
                      'Class': np.concatenate((np.zeros(250), np.ones(250))),
                      'score': np.random.randn(500)
                     })


dfNN = pd.DataFrame({'EventNumber': np.random.randint(1, 15, 500),
                     'Class': np.concatenate((np.zeros(250), np.ones(250))),
                     'score': np.random.randn(500)
                    })

...

dfTotal.corr()
#                 EventNumberBDT  EventNumberNN       BDT        NN
# EventNumberBDT        1.000000       0.992846 -0.026130  0.023623
# EventNumberNN         0.992846       1.000000 -0.023411  0.022093
# BDT                  -0.026130      -0.023411  1.000000 -0.026454
# NN                    0.023623       0.022093 -0.026454  1.000000


dfSub.corr()
#                 EventNumberBDT  EventNumberNN       BDT        NN
# EventNumberBDT        1.000000       0.992846 -0.026130  0.023623
# EventNumberNN         0.992846       1.000000 -0.023411  0.022093
# BDT                  -0.026130      -0.023411  1.000000 -0.026454
# NN                    0.023623       0.022093 -0.026454  1.000000

最后,这已经用OP的原始代码进行了测试:

def op_approach_total():
    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']

    return dfTotal.corr()


def op_approach_split():
    # not defaulted by Event Number by default
    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 = (dfNN.loc[dfNN['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']


    # not defaulted by Event Number by default
    BDT_sig = (dfBDT.loc[dfBDT['Class'] == 1])['score']
    BDT_sig.reset_index(drop=True, inplace=True)

    BDT_sig_num = (dfBDT.loc[dfBDT['Class'] == 1])['EventNumber']
    BDT_sig_num.reset_index(drop=True, inplace=True)

    NN_sig = (dfNN.loc[dfNN['Class'] == 1])['score']
    NN_sig.reset_index(drop=True, inplace=True)

    NN_sig_num = (dfNN.loc[dfNN['Class'] == 1])['EventNumber']
    NN_sig_num.reset_index(drop=True, inplace=True)


    dfSig = pd.concat([BDT_sig_num, NN_sig_num, BDT_sig, NN_sig],
                       axis = 1)
    dfSig.reset_index(drop=True, inplace=True)
    dfSig.columns = ['EventNumberBDT','EventNumberNN','BDT','NN']

    # ADDING EventNumber COLUMNS
    ev_back = pd.concat([dfBack['EventNumberBDT'], dfSig['EventNumberBDT']])
    ev_sig = pd.concat([dfBack['EventNumberNN'], dfSig['EventNumberNN']])


    ab = pd.concat([dfBack['BDT'], dfSig['BDT']])

    ba = pd.concat([dfBack['NN'], dfSig['NN']])

    # HORIZONTAL MERGE
    abba = pd.concat([ev_back, ev_sig, ab, ba], axis = 1)

    return abba.corr()

opTotal = op_approach_total()
opSub = op_approach_split()
输出

opTotal = op_approach_total()
opTotal
#                 EventNumberBDT  EventNumberNN       BDT        NN
# EventNumberBDT        1.000000       0.992846 -0.026130  0.023623
# EventNumberNN         0.992846       1.000000 -0.023411  0.022093
# BDT                  -0.026130      -0.023411  1.000000 -0.026454
# NN                    0.023623       0.022093 -0.026454  1.000000

opSub = op_approach_split()
opSub
#                 EventNumberBDT  EventNumberNN       BDT        NN
# EventNumberBDT        1.000000       0.992846 -0.026130  0.023623
# EventNumberNN         0.992846       1.000000 -0.023411  0.022093
# BDT                  -0.026130      -0.023411  1.000000 -0.026454
# NN                    0.023623       0.022093 -0.026454  1.000000

啊,是的,我错过了那个打字错误。两个分类器都是在相同的数据集上训练的,因此在类之间应该有相同的划分。在我的原始代码中,我确实包含了一个NaN值检查,当我运行
print(len(dfBDT[dfBDT['Class']==1])print(len(dfBDT[dfBDT[dfBDT['Class']==0])print(len(dfNN[dfNN[dfNN['Class']==1]))print(len(dfNN[dfNN['Class']=0]))时,我在每个分类器之间得到了相同的数量,但我仍然得到了这个问题。可能还有一个更深层次的问题我还没有解决,但我似乎还有很多关于操作数据框架的知识需要学习。在
中除了0或1之外还有其他值吗,包括
NaN
?那些
len
调用不会直接检查
NaN
。我还应该指出,两个数据帧中的行数必须相同,而不仅仅是相等的类共享,因为带有外部联接的不匹配索引将生成
NaN
。只有两个类,两个数据帧的长度相同,0类和1类的长度之和等于数据帧的长度。我可能会尝试使用一个简化的数据集,而不是完整的~100000个事件。目前,在Windows机器上(早期的解决方案在Linux上运行)使用相同行和相同类共享的可复制示例进行测试,结果显示还需要一个其他标准:数据必须按类排序(在EventNumber之前)。请参见编辑。另外,使用75%的0和25%的1的类共享来点击run。是的,这解决了这个问题,尽管我不明白为什么必须先按类排序。现在我知道总的相关性是一致的,为了检查分类器是如何相互关联的,我只需要在op_approach_split()的末尾返回dfSig.corr()和dfBack.corr()。这些相关性低于总相关性。我仍然不明白数据如何变得更加相关,这是另一个排序问题吗?
opTotal = op_approach_total()
opTotal
#                 EventNumberBDT  EventNumberNN       BDT        NN
# EventNumberBDT        1.000000       0.992846 -0.026130  0.023623
# EventNumberNN         0.992846       1.000000 -0.023411  0.022093
# BDT                  -0.026130      -0.023411  1.000000 -0.026454
# NN                    0.023623       0.022093 -0.026454  1.000000

opSub = op_approach_split()
opSub
#                 EventNumberBDT  EventNumberNN       BDT        NN
# EventNumberBDT        1.000000       0.992846 -0.026130  0.023623
# EventNumberNN         0.992846       1.000000 -0.023411  0.022093
# BDT                  -0.026130      -0.023411  1.000000 -0.026454
# NN                    0.023623       0.022093 -0.026454  1.000000