Как сравнивать панды DataFrames, используя разность наборов - PullRequest
0 голосов
/ 04 февраля 2019

У меня есть df1 и df2:

df1 = pd.DataFrame([[1,1,1,1],[2,2,1,1],[0,0,1,1],[1,1,1,1],[2,2,1,1],[0,0,4,1]], 
                   columns=['col1','col2','col3','col4'])
df2 = pd.DataFrame([[1,1,1,1],[3,3,1,1],[0,0,1,1],[1,1,5,1],[3,3,1,1],[0,0,1,1]], 
                   columns=['col1','col2','col3','col4'])

Я удалил дублирующиеся строки:

df1 = df1.drop_duplicates(subset=['col1', 'col2', 'col3'])
df2 = df2.drop_duplicates(subset=['col1', 'col2', 'col3'])

, поэтому каждый df состоит из уникальных строк.Но есть некоторые совпадения между двумя dfs:

   col1  col2  col3  col4
0     1     1     1     1
1     2     2     1     1
2     0     0     1     1
5     0     0     4     1

   col1  col2  col3  col4
0     1     1     1     1
1     3     3     1     1
2     0     0     1     1
3     1     1     5     1

Мои фактические df1 и df2 имеют форму приблизительно (5000000, 23) и включают в себя поля int, float и object.


Начать редактирование: Я хочу создать df1_uniq (что в df1, но не в df2) и df2_uniq (что в df2, но не в df1.

df1_iniq:
   col1  col2  col3  col4
1     2     2     1     1
5     0     0     4     1

df2_uniq:
   col1  col2  col3  col4
1     3     3     1     1
3     1     1     5     1

Конец РЕДАКТИРОВАНИЯ


В нотации набора я думаю, что это будет df1.difference(df2) и df2.difference(df1), но я получаю пустые наборы, когда запускаю этот код.Обратите внимание, что сравнение одного столбца в каждом df работает , например,

set(df_1[field].unique()).difference(set(df_2[field].unique()))

возвращает [2].

Другой подход будетциклически проходить через каждый df, и если df1 [i] равняется какой-либо строке в df2, то отбросить строку в обоих dfs, но это подход O (n ** 2), который займет много времени.

Есть ли более питонический, пандастический способ сделать это?

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

Вы можете использовать merge с indicator=True:

u = df1.merge(df2, how='outer', indicator=True)
df3 = u.query('_merge == "left_only"').drop('_merge', 1)
df4 = u.query('_merge == "right_only"').drop('_merge', 1)

df3

   col1  col2  col3  col4
1     2     2     1     1
3     0     0     4     1

df4

   col1  col2  col3  col4
4     3     3     1     1
5     1     1     5     1

Если имена столбцов df1 и df2 различны, убедитесь, что они оба сделаны одинаковыми:

df1.columns = df2.columns

Если индекс также необходимо сохранить, вы можете сначала сбросить его до слияния, а затем установить его после.

u, v = df1.reset_index(), df2.reset_index()
w = (u.merge(v, how='outer', on=df1.columns.tolist(), indicator=True)
      .fillna({'index_x': -1, 'index_y': -1}, downcast='infer'))
w

   index_x  col1  col2  col3  col4  index_y      _merge
0        0     1     1     1     1        0        both
1        1     2     2     1     1       -1   left_only
2        2     0     0     1     1        2        both
3        5     0     0     4     1       -1   left_only
4       -1     3     3     1     1        1  right_only
5       -1     1     1     5     1        3  right_only

Сейчас,

df3 = (w.query('_merge == "left_only"')
        .set_index('index_x')
        .drop(['_merge', 'index_y'], 1)
        .rename_axis([None], axis=0))
df4 = (w.query('_merge == "right_only"')
        .set_index('index_y')
        .drop(['_merge', 'index_x'], 1)
        .rename_axis([None], axis=0))

df3

   col1  col2  col3  col4
1     2     2     1     1
5     0     0     4     1

df4

   col1  col2  col3  col4
1     3     3     1     1
3     1     1     5     1
0 голосов
/ 04 февраля 2019

Предполагая, что вы хотите сравнить наборы строк, вам нужно преобразовать каждую из ваших строк в хешируемый объект, например, tuple:

set([tuple(x) for x in df1.values]) - set([tuple(x) for x in df2.values])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...