В конечном счете, все сводится к горизонтальным слияниям, выполняемым по индексам.
Несопоставленные строки
Если строки различаются по обоим фреймам данных, concat
по умолчанию к внешнему объединению сгенерирует NaN
по несопоставленным индексам (на фрейме данных меньших строк), которые будут больше строк, чем исходный фрейм данных до разделения.
Несопоставленные классы
Кроме того, если Class имеют разные доли% между двумя фреймами данных, dfBDT и dfNN , их соответствующие объединения вернут NaN
по несопоставленным индексам.
Например, скажем, dfBDT поддерживает 60% и 40% между классами 0 и 1, а dfNN поддерживает 50% и 50% между классами 0 и 1, где сравнения включают :
- BDT Class 0 будет иметь больше строк, чем NN Class 0
- BDT Class 1 будет иметь меньше строк, чем NN Class 1
После горизонтального соединения с pd.concat(..., axis = 1)
, по умолчанию внешнее соединение, how = 'outer'
, в результате несоответствия будут генерироваться NaN
с обеих сторон. Даже если вы используете how='inner
', вы будете отфильтровывать несоответствия, но dfTotal никогда не отфильтровывает никакие строки, но включает все строк.
Сортировка Заказ
Тестирование между Linux и Windows машинами с отобранным, воспроизводимым примером указывает на сортировку, особенно по Class
сначала, затем EventNumber
, имеет значение.
Это может быть продемонстрировано с помощью случайных данных с семенами для воспроизводимого примера. Ниже приведен рефакторинг вашего кода, чтобы избежать множества вызовов pd.concat
с использованием join
(с настройкой по умолчанию how='outer'
). Далее этот код эквивалентен исходной настройке 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()
Вывод (обратите внимание, они дают разные результаты)
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.000000
dfSub.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
Однако, если мы приравниваем Class акций (например, 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