2016-05-19 16 views
5

So entfernen Sie eine Pandas Datenrahmen von einem anderen Datenrahmen, genau wie der Satz Subtraktion entfernen:Wie ein Pandas Datenrahmen von einem anderen Datenrahmen

In [5]: df1=pd.DataFrame([[1,2],[3,4],[5,6]],columns=['a','b']) 
In [6]: df1 
Out[6]: 
    a b 
0 1 2 
1 3 4 
2 5 6 


In [9]: df2=pd.DataFrame([[1,2],[5,6]],columns=['a','b']) 
In [10]: df2 
Out[10]: 
    a b 
0 1 2 
1 5 6 

Dann erwarten wir DF1-df2 Ergebnis sein wird:

In [14]: df 
Out[14]: 
    a b 
0 3 4 

Wie es zu tun?

Vielen Dank.

+0

Mögliches Duplikat von [set difference for pandas] (http://stackoverflow.com/questions/18180763/set-difference-for-pandas) – AKS

+0

@ 176coding Bitte timen Sie unsere Antworten auf Ihre realen Datensätze - es ist interessant für mich welche ist am schnellsten/ – knagaev

Antwort

9

Lösung

Verwenden pd.concat von drop_duplicates(keep=False)

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

gefolgt Es sieht aus wie

a b 
1 3 4 

Erklärung

pd.concat die beiden DataFrame durch ein Anfügen s zusammen addiert direkt nach dem anderen. Wenn es Überlappungen gibt, wird dies mit der Methode drop_duplicates erfasst. drop_duplicates jedoch standardmäßig die erste Beobachtung und entfernt jede andere Beobachtung. In diesem Fall möchten wir, dass jedes Duplikat entfernt wird. Daher der keep=False Parameter, der genau das tut.

Eine besondere Anmerkung zu den wiederholten df2. Mit nur einer df2 wird keine Zeile in df2 nicht in df1 als ein Duplikat betrachtet und bleibt erhalten. Diese Lösung mit nur einer df2 funktioniert nur, wenn df2 eine Teilmenge von df1 ist. Wenn wir jedoch zweimal df2 eingeben, ist es garantiert ein Duplikat und wird anschließend entfernt.

+0

thx, es funktioniert, und wir können 'pd.concat (df1, df2) .drop_duplicates (keep = False)' oder 'df1.append (df2) .drop_duplicates (keep = False) ' – 176coding

+0

@ 176coding hoffentlich Das beantwortet deine Frage. Wenn nicht, lassen Sie mich wissen, was unbeantwortet bleibt und ich werde mein Bestes tun, um es anzugehen. – piRSquared

+1

@piRSquared Ihre Antwort ist nicht korrekt - Sie haben einen symmetrischen Unterschied gemacht, keinen Unterschied (einfach). – knagaev

0

Ein logischer Ansatz. Drehen Sie die Reihen df1 und df2 in Sätze. Dann set Subtraktion verwenden, um neue zu definieren DataFrame

idx1 = set(df1.set_index(['a', 'b']).index) 
idx2 = set(df2.set_index(['a', 'b']).index) 

pd.DataFrame(list(idx1 - idx2), columns=df1.columns) 

    a b 
0 3 4 
0

Ein Maskierungs Ansatz

df1[df1.apply(lambda x: x.values.tolist() not in df2.values.tolist(), axis=1)] 

    a b 
1 3 4 
+0

brauchen nicht '.tolist()'. – Stefan

0

Mein Schuss mit merge DF1 und DF2 von der Frage.

Mit 'Anzeige' Parameter

In [74]: df1.loc[pd.merge(df1, df2, on=['a','b'], how='left', indicator=True)['_merge'] == 'left_only'] 
Out[74]: 
    a b 
1 3 4 
+0

Eine Erklärung dessen, was passiert, würde dies eine reichhaltigere Antwort geben. Sie haben erwähnt, dass der Schlüssel für den Erfolg dieser Methode der Parameter 'indicator' ist. Wenn Sie diesen Wert auf true festlegen, fügt er jeder Zeile Zeileninformationen hinzu, die Ihre Lösung im letzten Schritt zum Filtern verwendet, wobei nur Zeilen beibehalten werden, die nur in den linken Daten angezeigt werden Rahmen (Indikator == 'left_only'). – Dannid

1

Sie .duplicated verwenden können, was den Vorteil hat das Sein ziemlich ausdruck:

%%timeit 
combined = df1.append(df2) 
combined[~combined.duplicated(keep=False)] 

1000 loops, best of 3: 875 µs per loop 

Zum Vergleich:

%timeit df1.loc[pd.merge(df1, df2, on=['a','b'], how='left', indicator=True)['_merge'] == 'left_only'] 

100 loops, best of 3: 4.57 ms per loop 


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

1000 loops, best of 3: 987 µs per loop 


%timeit df2[df2.apply(lambda x: x.value not in df2.values, axis=1)] 

1000 loops, best of 3: 546 µs per loop 

Zusammengefasst mit dem np.array Vergleich ist am schnellsten. Brauche die .tolist() dort nicht.