Mam dwie ramki danych df1 i df2, gdzie df2 jest podzbiorem df1. Jak uzyskać nową ramkę danych (df3), która jest różnicą między dwiema ramkami danych?
Innymi słowy, ramka danych, która ma wszystkie wiersze / kolumny w df1, których nie ma w df2?
Odpowiedzi:
Używając 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]})
Wyświetli się jak poniżej, co jest błędne
Niewłaściwe wyjście:
pd.concat([df1, df2]).drop_duplicates(keep=False)
Out[655]:
A B
1 2 3
Prawidłowe wyjście
Out[656]:
A B
1 2 3
2 3 4
3 3 4
Jak to osiągnąć?
Metoda 1: używanie isinztuple
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
Out[657]:
A B
1 2 3
2 3 4
3 3 4
Metoda 2: mergezindicator
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(ponieważ 12.00000000001 != 12). Lepszą praktyką jest znalezienie przecięcia zestawu identyfikatorów w dwóch ramkach danych i na tej podstawie obliczenie różnicy.
indicator=True) jest bardzo wszechstronnym i użytecznym narzędziem. Chciałbym zobaczyć ją na początku tej odpowiedzi, ale z łączeniem „zewnętrznym”, a nie „lewym”, obejmującym wszystkie 3 sytuacje.
W przypadku wierszy spróbuj tego, gdzie Namejest kolumna wspólnego indeksu (może to być lista wielu wspólnych kolumn lub określić left_oni right_on):
m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)
To indicator=Trueustawienie jest przydatne, ponieważ dodaje kolumnę o nazwie _merge, zawierającą wszystkie zmiany między df1i df2, podzielone na 3 możliwe rodzaje: „left_only”, „right_only” lub „both”.
W przypadku kolumn spróbuj tego:
set(df1.columns).symmetric_difference(df2.columns)
mergewith indicator=Trueto klasyczne rozwiązanie do porównywania ramek danych według danych pól.
Zaakceptowana odpowiedź Metoda 1 nie będzie działać dla ramek danych z NaNami wewnątrz, as pd.np.nan != pd.np.nan. Nie jestem pewien, czy to najlepszy sposób, ale można tego uniknąć
df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
edit2, wymyśliłem nowe rozwiązanie bez potrzeby ustawiania indeksu
newdf=pd.concat([df1,df2]).drop_duplicates(keep=False)
Okay, znalazłem odpowiedź najwyższego głosowania już zawiera to, co odkryłem. Tak, możemy użyć tego kodu tylko pod warunkiem, że nie ma duplikatów w każdym z dwóch plików df.
Mam trudną metodę. Najpierw ustawiliśmy „Name” jako indeks dwóch ramek danych podanych w pytaniu. Ponieważ mamy tę samą „Nazwę” w dwóch plikach df, możemy po prostu usunąć indeks „mniejszego” pliku df z „większego” pliku df. Oto kod.
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
Być może prostszy jednolinijkowy, z identycznymi lub różnymi nazwami kolumn. Działa nawet wtedy, gdy df2 ['Name2'] zawiera zduplikowane wartości.
newDf = df1.set_index('Name1')
.drop(df2['Name2'], errors='ignore')
.reset_index(drop=False)
Jak wspomniano tutaj, że
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
jest poprawnym rozwiązaniem, ale jeśli
df1=pd.DataFrame({'A':[1],'B':[2]})
df2=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
W takim przypadku powyższe rozwiązanie da
Empty DataFrame , zamiast tego powinieneś użyć concatmetody po usunięciu duplikatów z każdej ramki danych.
Posługiwać się concate with drop_duplicates
df1=df1.drop_duplicates(keep="first")
df2=df2.drop_duplicates(keep="first")
pd.concat([df1,df2]).drop_duplicates(keep=False)
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]nawet w tym przypadku jest to poprawna odpowiedź. Jeśli chciałeś uzyskać wartości, które są w df1 lub df2, ale nie w obu, to sugerowane podejście jest poprawne (z zastrzeżeniem usuwania duplikatów z oryginalnych ramek danych).
Niewielka odmiana rozwiązania nice @ liangli, które nie wymaga zmiany indeksu istniejących ramek danych:
newdf = df1.drop(df1.join(df2.set_index('Name').index))
Znajdowanie różnicy według indeksu. Zakładając, że df1 jest podzbiorem df2, a indeksy są przenoszone do przodu podczas podzbioru
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
Oprócz zaakceptowanej odpowiedzi chciałbym zaproponować jeszcze jedno szersze rozwiązanie, w którym można znaleźć różnicę w zestawie 2D dwóch ramek danych z dowolnym index/ columns(mogą nie pokrywać się dla obu nazw danych). Metoda pozwala również na ustawienie tolerancji dla floatelementów do porównania ramek danych (wykorzystuje 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
Przykład:
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