У мене є два кадри даних df1 і df2, де df2 - це підмножина df1. Як отримати новий фрейм даних (df3), який є різницею між двома фреймами даних?
Іншими словами, кадр даних, який містить усі рядки / стовпці в df1, а не в df2?
Відповіді:
За допомогою 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
pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
float(тому що 12.00000000001 != 12). Кращою практикою є знайти перетин перетину ідентифікаторів у двох кадрах даних і отримати різницю на основі цього.
indicator=True) - це дуже універсальний та корисний інструмент, я хотів би бачити його у верхній частині цієї відповіді, але приєднавшись „зовнішнім“, а не „лівим“, щоб охопити всі 3 ситуації.
Для рядків спробуйте це, де Nameстовпець спільного індексу (може бути списком для кількох загальних стовпців, або вказати left_onі right_on):
m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)
indicator=TrueПараметр корисний , оскільки він додає стовпець _mergeз усіма змінами між df1і df2, класифікованих в 3 -х можливих видів: «left_only», «right_only» або «як».
Для стовпців спробуйте наступне:
set(df1.columns).symmetric_difference(df2.columns)
mergewith indicator=Trueє класичним рішенням для порівняння кадрів даних за заданими полями.
Прийнята відповідь Метод 1 не буде працювати для фреймів даних з NaN всередині, як pd.np.nan != pd.np.nan. Я не впевнений, що це найкращий спосіб, але цього можна уникнути
df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
edit2, я знайшов нове рішення без необхідності встановлення індексу
newdf=pd.concat[df1,df2].drop_duplicates(keep=False)
Гаразд, я знайшов, що відповідь на найвищу оцінку вже містить те, що я вже зрозумів. Так, ми можемо використовувати цей код лише за умови, що в кожному з двох dfs немає дублікатів.
У мене є хитрий метод. Спочатку ми встановлюємо `` Name '' як індекс двох кадрів даних, заданий питанням. . Ось код.
df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)
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
Можливо, простіший однолінійний, з однаковими або різними назвами стовпців. Працювало навіть тоді, коли df2 ['Name2'] містив повторювані значення.
newDf = df1.set_index('Name1')
.drop(df2['Name2'], errors='ignore')
.reset_index(drop=False)
Невелика варіація рішення nice @ liangli, яка не вимагає зміни індексу існуючих кадрів даних:
newdf = df1.drop(df1.join(df2.set_index('Name').index))
Знаходження різниці за індексом. Припускаючи, що 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
На додаток до прийнятої відповіді, я хотів би запропонувати ще одне більш широке рішення, яке можна знайти двовимірну різницю в двох кадрах даних з будь-яким 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
Як було згадано тут , що
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
є правильним рішенням, але це призведе до неправильного результату, якщо
df1=pd.DataFrame({'A':[1],'B':[2]})
df2=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
У цьому випадку вищевказане рішення дасть
Empty DataFrame , замість цього ви повинні використовуватиconcat метод після видалення дублікатів з кожного datframe.
Використовуйте concate with drop_duplicates
df1=df1.drop_duplicates(keep="first")
df2=df2.drop_duplicates(keep="first")
pd.concat([df1,df2]).drop_duplicates(keep=False)