Python Pandas - найдите разницу между двумя кадрами данных

У меня два кадра данных df1 и df2, где df2 - подмножество df1. Как получить новый фрейм данных (df3), который является разницей между двумя кадрами данных?

Другими словами, кадр данных, который имеет все строки/столбцы в df1, которые не находятся в df2?

enter image description here

Ответ 1

Используя drop_duplicates

pd.concat([df1,df2]).drop_duplicates(keep=False)

Update :

Above method only working for those dataframes they do not have duplicate itself, For example

df1=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
df2=pd.DataFrame({'A':[1],'B':[2]})

Будет выводиться как ниже, что неправильно

Неверный вывод:

pd.concat([df1, df2]).drop_duplicates(keep=False)
Out[655]: 
   A  B
1  2  3

Правильный вывод

Out[656]: 
   A  B
1  2  3
2  3  4
3  3  4

Как этого добиться?

Способ 1: использование isin с tuple

df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
Out[657]: 
   A  B
1  2  3
2  3  4
3  3  4

Способ 2: merge с indicator

df1.merge(df2,indicator = True, how='left').loc[lambda x : x['_merge']!='both']
Out[421]: 
   A  B     _merge
1  2  3  left_only
2  3  4  left_only
3  3  4  left_only

Ответ 2

Для строк попробуйте это, с cols установленным в список столбцов, которые вы хотите сравнить:

m = df1.merge(df2, on=cols, how='outer', suffixes=['', '_'], indicator=True)

Для столбцов попробуйте следующее:

set(df1.columns).symmetric_difference(df2.columns)

Ответ 3

Принятый ответ Метод 1 не будет работать для фреймов данных с NaN внутри, так как pd.np.nan != pd.np.nan. Я не уверен, что это лучший способ, но его можно избежать

df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]

Ответ 4

edit2, я разобрался с новым решением без необходимости установки индекса

newdf=pd.concat[df1,df2].drop_duplicates(keep=False)

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


У меня есть хитрый метод. Во-первых, мы устанавливаем Name как индекс двух фреймов данных, заданных вопросом. Так как у нас одно и то же Name в двух dfs, мы можем просто удалить меньший индекс dfs из большего df. Вот код

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)

Ответ 5

import pandas as pd
# given
df1 = pd.DataFrame({'Name':['John','Mike','Smith','Wale','Marry','Tom','Menda','Bolt','Yuswa',],
    'Age':[23,45,12,34,27,44,28,39,40]})
df2 = pd.DataFrame({'Name':['John','Smith','Wale','Tom','Menda','Yuswa',],
    'Age':[23,12,34,44,28,40]})

# find elements in df1 that are not in df2
df_1notin2 = df1[~(df1['Name'].isin(df2['Name']) & df1['Age'].isin(df2['Age']))].reset_index(drop=True)

# output:
print('df1\n', df1)
print('df2\n', df2)
print('df_1notin2\n', df_1notin2)

# df1
#     Age   Name
# 0   23   John
# 1   45   Mike
# 2   12  Smith
# 3   34   Wale
# 4   27  Marry
# 5   44    Tom
# 6   28  Menda
# 7   39   Bolt
# 8   40  Yuswa
# df2
#     Age   Name
# 0   23   John
# 1   12  Smith
# 2   34   Wale
# 3   44    Tom
# 4   28  Menda
# 5   40  Yuswa
# df_1notin2
#     Age   Name
# 0   45   Mike
# 1   27  Marry
# 2   39   Bolt

Ответ 6

Возможно, более простой однострочный, с одинаковыми или разными именами столбцов. Работает, даже когда df2 ['Name2'] содержит повторяющиеся значения.

newDf = df1.set_index('Name1').drop(df2['Name2'])

Ответ 7

Небольшое отклонение от красивого решения @liangli, которое не требует изменения индекса существующих фреймов данных:

newdf = df1.drop(df1.join(df2.set_index('Name').index))

Ответ 8

Нахождение разницы по индексу. Предполагая, что df1 является подмножеством df2, и индексы переносятся вперед при поднаборе

df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

# Example

df1 = pd.DataFrame({"gender":np.random.choice(['m','f'],size=5), "subject":np.random.choice(["bio","phy","chem"],size=5)}, index = [1,2,3,4,5])

df2 =  df1.loc[[1,3,5]]

df1

 gender subject
1      f     bio
2      m    chem
3      f     phy
4      m     bio
5      f     bio

df2

  gender subject
1      f     bio
3      f     phy
5      f     bio

df3 = df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

df3

  gender subject
2      m    chem
4      m     bio

Ответ 9

В дополнение к принятому ответу я хотел бы предложить еще одно более широкое решение, которое может найти разность двумерных множеств двух кадров данных с любым index/columns (они могут не совпадать для обоих типов данных). Также метод позволяет установить допуск для элементов float для сравнения данных (используется np.isclose)


import numpy as np
import pandas as pd

def get_dataframe_setdiff2d(df_new: pd.DataFrame, 
                            df_old: pd.DataFrame, 
                            rtol=1e-03, atol=1e-05) -> pd.DataFrame:
    """Returns set difference of two pandas DataFrames"""

    union_index = np.union1d(df_new.index, df_old.index)
    union_columns = np.union1d(df_new.columns, df_old.columns)

    new = df_new.reindex(index=union_index, columns=union_columns)
    old = df_old.reindex(index=union_index, columns=union_columns)

    mask_diff = ~np.isclose(new, old, rtol, atol)

    df_bool = pd.DataFrame(mask_diff, union_index, union_columns)

    df_diff = pd.concat([new[df_bool].stack(),
                         old[df_bool].stack()], axis=1)

    df_diff.columns = ["New", "Old"]

    return df_diff

Пример:

In [1]

df1 = pd.DataFrame({'A':[2,1,2],'C':[2,1,2]})
df2 = pd.DataFrame({'A':[1,1],'B':[1,1]})

print("df1:\n", df1, "\n")

print("df2:\n", df2, "\n")

diff = get_dataframe_setdiff2d(df1, df2)

print("diff:\n", diff, "\n")
Out [1]

df1:
   A  C
0  2  2
1  1  1
2  2  2 

df2:
   A  B
0  1  1
1  1  1 

diff:
     New  Old
0 A  2.0  1.0
  B  NaN  1.0
  C  2.0  NaN
1 B  NaN  1.0
  C  1.0  NaN
2 A  2.0  NaN
  C  2.0  NaN