wake-up-neo.com

pandas erhalten Zeilen, die NICHT in anderen Datenrahmen sind

Ich habe zwei Pandas-Datenrahmen, die einige Zeilen gemeinsam haben.

Angenommen, dataframe2 ist eine Teilmenge von dataframe1.

Wie bekomme ich die Zeilen von dataframe1, die nicht in dataframe2 sind?

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]})
135

Eine Methode wäre, das Ergebnis einer inneren Zusammenführungsform beider DFS zu speichern. Dann können wir einfach die Zeilen auswählen, wenn sich die Werte einer Spalte nicht in diesem Bereich befinden:

In [119]:

common = df1.merge(df2,on=['col1','col2'])
print(common)
df1[(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))]
   col1  col2
0     1    10
1     2    11
2     3    12
Out[119]:
   col1  col2
3     4    13
4     5    14

EDIT

Eine andere Methode ist die Verwendung von isin, die NaN-Zeilen erzeugt, die Sie löschen können:

In [138]:

df1[~df1.isin(df2)].dropna()
Out[138]:
   col1  col2
3     4    13
4     5    14

Wenn df2 jedoch Zeilen nicht auf dieselbe Weise startet, funktioniert dies nicht:

df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11, 12,13]})

produziert die gesamte df:

In [140]:

df1[~df1.isin(df2)].dropna()
Out[140]:
   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14
111
EdChum

Die aktuell ausgewählte Lösung führt zu falschen Ergebnissen. Um dieses Problem richtig zu lösen, können Sie eine Linkverknüpfung von df1 nach df2 ausführen. Dabei müssen Sie zunächst nur die eindeutigen Zeilen für df2 abrufen. 

Zuerst müssen wir den ursprünglichen DataFrame ändern, um die Zeile mit Daten hinzuzufügen [3, 10].

df1 = pd.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 
                           'col2' : [10, 11, 12, 13, 14, 10]}) 
df2 = pd.DataFrame(data = {'col1' : [1, 2, 3],
                           'col2' : [10, 11, 12]})

df1

   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14
5     3    10

df2

   col1  col2
0     1    10
1     2    11
2     3    12

Führen Sie einen Left-Join durch, wobei Duplikate in df2 entfernt werden, sodass jede Zeile von df1 mit genau einer Zeile von df2 verknüpft wird. Verwenden Sie den Parameter indicator, um eine zusätzliche Spalte zurückzugeben, die angibt, aus welcher Tabelle die Zeile stammt.

df_all = df1.merge(df2.drop_duplicates(), on=['col1','col2'], 
                   how='left', indicator=True)
df_all

   col1  col2     _merge
0     1    10       both
1     2    11       both
2     3    12       both
3     4    13  left_only
4     5    14  left_only
5     3    10  left_only

Erstellen Sie eine boolesche Bedingung:

df_all['_merge'] == 'left_only'

0    False
1    False
2    False
3     True
4     True
5     True
Name: _merge, dtype: bool

Warum sind andere Lösungen falsch?

Einige Lösungen machen denselben Fehler - sie überprüfen nur, dass jeder Wert in jeder Spalte unabhängig ist und nicht in derselben Zeile. Das Hinzufügen der letzten Zeile, die eindeutig ist, jedoch die Werte aus beiden Spalten von df2 enthält, macht den Fehler sichtbar:

common = df1.merge(df2,on=['col1','col2'])
(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))
0    False
1    False
2    False
3     True
4     True
5    False
dtype: bool

Diese Lösung erhält das gleiche falsche Ergebnis:

df1.isin(df2.to_dict('l')).all(1)
98
Ted Petrou

Angenommen, die Indizes sind in den Datenrahmen konsistent (wobei die tatsächlichen col-Werte nicht berücksichtigt werden):

df1[~df1.index.isin(df2.index)]
55

Wie bereits angedeutet, erfordert isin, dass Spalten und Indizes für eine Übereinstimmung identisch sind. Wenn Übereinstimmung nur für den Zeileninhalt gelten soll, besteht eine Möglichkeit zum Abrufen der Maske zum Filtern der vorhandenen Zeilen darin, die Zeilen in einen (Multi) Index zu konvertieren:

In [77]: df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 'col2' : [10, 11, 12, 13, 14, 10]})
In [78]: df2 = pandas.DataFrame(data = {'col1' : [1, 3, 4], 'col2' : [10, 12, 13]})
In [79]: df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)]
Out[79]:
   col1  col2
1     2    11
4     5    14
5     3    10

Wenn der Index berücksichtigt werden soll, wird bei set_index ein Schlüsselwortargument angefügt, um Spalten an den vorhandenen Index anzuhängen. Wenn sich die Spalten nicht in einer Reihe befinden, kann list (df.columns) durch Spaltenangaben ersetzt werden, um die Daten auszurichten.

pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())

könnte alternativ zur Erstellung der Indizes verwendet werden, obwohl ich bezweifle, dass dies effizienter ist.

11
Rune Lyngsoe

Angenommen, Sie haben zwei Datenrahmen, df_1 und df_2 mit mehreren Feldern (column_names), und Sie möchten nur die Einträge in df_1 finden, die nicht in df_2 anhand einiger Felder (z. B. fields_x, fields_y) vorhanden sind. Führen Sie die folgenden Schritte aus.

Schritt 1. Fügen Sie einen Spaltenschlüssel1 und einen Schlüssel2 zu df_1 bzw. df_2 hinzu.

Schritt2.Führen Sie die Datenrahmen wie unten gezeigt zusammen. field_x und field_y sind unsere gewünschten Spalten.

Schritt3.Wählen Sie nur die Zeilen aus df_1 aus, bei denen key1 nicht key2 entspricht.

Step4.Drop key1 und key2.

Diese Methode löst Ihr Problem und arbeitet auch mit großen Datenmengen schnell. Ich habe es für Datenrahmen mit mehr als 1.000.000 Zeilen versucht.

df_1['key1'] = 1
df_2['key2'] = 1
df_1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'left')
df_1 = df_1[~(df_1.key2 == df_1.key1)]
df_1 = df_1.drop(['key1','key2'], axis=1)
10

Sie können auch df1, df2 angeben:

x = pd.concat([df1, df2])

und entfernen Sie dann alle Duplikate:

y = x.drop_duplicates(keep=False, inplace=False)
4
Semeon Balagula

etwas spät, aber es lohnt sich vielleicht, den Parameter "indector" von pd.merge zu überprüfen.

In dieser anderen Frage finden Sie ein Beispiel: Vergleichen Sie PandaS DataFrames und geben Sie die Zeilen zurück, die in der ersten Zeile fehlen.

3
jabellcu

sie können es mit isin (dict) tun:

In [74]: df1[~df1.isin(df2.to_dict('l')).all(1)]
Out[74]:
   col1  col2
3     4    13
4     5    14

Erläuterung:

In [75]: df2.to_dict('l')
Out[75]: {'col1': [1, 2, 3], 'col2': [10, 11, 12]}

In [76]: df1.isin(df2.to_dict('l'))
Out[76]:
    col1   col2
0   True   True
1   True   True
2   True   True
3  False  False
4  False  False

In [77]: df1.isin(df2.to_dict('l')).all(1)
Out[77]:
0     True
1     True
2     True
3    False
4    False
dtype: bool
3
MaxU

Hier ist eine andere Möglichkeit, dies zu lösen:

df1[~df1.index.isin(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]

Oder:

df1.loc[df1.index.difference(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]
1
Sergey Zakharov

Wie wäre es damit:

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 
                               'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 
                               'col2' : [10, 11, 12]})
records_df2 = set([Tuple(row) for row in df2.values])
in_df2_mask = np.array([Tuple(row) in records_df2 for row in df1.values])
result = df1[~in_df2_mask]
1
adamwlev

Meine Art, dies zu tun, besteht darin, eine neue Spalte hinzuzufügen, die für einen Datenrahmen eindeutig ist, und dies zu verwenden, um auszuwählen, ob ein Eintrag beibehalten werden soll

df2[col3] = 1
df1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'outer')
df1['Empt'].fillna(0, inplace=True)

Dies hat zur Folge, dass jeder Eintrag in df1 einen Code hat - 0, wenn er für df1 eindeutig ist, 1, wenn er in beiden Datenrahmen enthalten ist. Sie verwenden dies, um sich auf das zu beschränken, was Sie möchten

answer = nonuni[nonuni['Empt'] == 0]
0
r.rz