Ich habe zwei Datenrahmen df1 und df2, wobei df2 eine Teilmenge von df1 ist. Wie bekomme ich einen neuen Datenrahmen (df3), der den Unterschied zwischen den beiden Datenrahmen darstellt?
Mit anderen Worten, ein Datenrahmen, der alle Zeilen / Spalten in df1 enthält, die nicht in df2 enthalten sind?
Antworten:
Durch die Nutzung
drop_duplicates
pd.concat([df1,df2]).drop_duplicates(keep=False)
Update :
df1=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]}) df2=pd.DataFrame({'A':[1],'B':[2]})
Es wird wie unten ausgegeben, was falsch ist
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
Methode 1: Verwenden
isin
mittuple
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))] Out[657]: A B 1 2 3 2 3 4 3 3 4
Methode 2:
merge
mitindicator
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
quelle
pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
float
(weil12.00000000001 != 12
) ist. Eine bessere Vorgehensweise besteht darin, den festgelegten Schnittpunkt der IDs in zwei Datenrahmen zu ermitteln und den Unterschied daraus zu ermitteln.indicator=True
) ist ein sehr vielseitiges und nützliches Werkzeug. Ich würde es gerne oben in dieser Antwort sehen, aber mit 'äußerer' nicht 'linker' Verbindung, um alle 3 Situationen abzudecken.Versuchen Sie dies für Zeilen, wobei
Name
sich die gemeinsame Indexspalte befindet (kann eine Liste für mehrere gemeinsame Spalten sein oder angebenleft_on
undright_on
):m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)
Die
indicator=True
Einstellung ist nützlich, da sie eine Spalte_merge
mit allen Änderungen zwischendf1
und hinzufügtdf2
, die in drei mögliche Arten unterteilt ist: "left_only", "right_only" oder "both".Versuchen Sie für Spalten Folgendes:
set(df1.columns).symmetric_difference(df2.columns)
quelle
merge
withindicator=True
ist die klassische Lösung zum Vergleichen von Datenrahmen nach bestimmten Feldern.Akzeptierte Antwort Methode 1 funktioniert nicht für Datenrahmen mit darin enthaltenen NaNs
pd.np.nan != pd.np.nan
. Ich bin mir nicht sicher, ob dies der beste Weg ist, aber es kann durch vermieden werdendf1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
quelle
edit2, ich habe eine neue Lösung gefunden, ohne den Index setzen zu müssen
newdf=pd.concat([df1,df2]).drop_duplicates(keep=False)
Okay, ich fand, dass die Antwort der höchsten Stimmen bereits das enthält, was ich herausgefunden habe. Ja, wir können diesen Code nur unter der Bedingung verwenden, dass in zwei dfs keine Duplikate vorhanden sind.
Ich habe eine knifflige Methode. Zuerst setzen wir 'Name' als Index für zwei Datenrahmen, die von der Frage angegeben werden. Da wir in zwei dfs denselben 'Namen' haben, können wir einfach den Index des 'kleineren' df aus dem 'größeren' df löschen. Hier ist der Code.
df1.set_index('Name',inplace=True) df2.set_index('Name',inplace=True) newdf=df1.drop(df2.index)
quelle
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
quelle
Vielleicht ein einfacher Einzeiler mit identischen oder unterschiedlichen Spaltennamen. Funktionierte auch dann, wenn df2 ['Name2'] doppelte Werte enthielt.
newDf = df1.set_index('Name1') .drop(df2['Name2'], errors='ignore') .reset_index(drop=False)
quelle
Eine kleine Variation der Lösung von nice @ liangli, bei der der Index vorhandener Datenrahmen nicht geändert werden muss:
newdf = df1.drop(df1.join(df2.set_index('Name').index))
quelle
Differenz anhand des Index ermitteln. Angenommen, df1 ist eine Teilmenge von df2 und die Indizes werden bei der Teilmenge vorgetragen
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
quelle
Zusätzlich zur akzeptierten Antwort möchte ich eine weitere umfassendere Lösung vorschlagen, die einen 2D- Satzunterschied von zwei Datenrahmen mit einem beliebigen
index
/ findetcolumns
(sie stimmen möglicherweise nicht für beide Datenrahmen überein). Die Methode ermöglicht auch das Einrichten der Toleranz fürfloat
Elemente für den Datenrahmenvergleich (verwendetnp.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
Beispiel:
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
quelle
Wie hier erwähnt das
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
ist die richtige Lösung, aber es wird eine falsche Ausgabe erzeugen, wenn
df1=pd.DataFrame({'A':[1],'B':[2]}) df2=pd.DataFrame({'A':[1,2,3,3],'B':[2,3,4,4]})
In diesem Fall gibt die obige Lösung Empty DataFrame an , stattdessen sollten Sie verwenden
concat
Methode verwenden, nachdem Sie Duplikate aus jedem Datenrahmen entfernt haben.Verwenden
concate with drop_duplicates
df1=df1.drop_duplicates(keep="first") df2=df2.drop_duplicates(keep="first") pd.concat([df1,df2]).drop_duplicates(keep=False)
quelle
df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
ist das auch in diesem Fall die richtige Antwort. Wenn Sie Werte erhalten möchten, die entweder in df1 oder df2, aber nicht in beiden vorliegen, ist Ihr vorgeschlagener Ansatz korrekt (mit der Einschränkung, dass Duplikate aus den ursprünglichen Datenrahmen entfernt werden).