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]})
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
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
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)
Angenommen, die Indizes sind in den Datenrahmen konsistent (wobei die tatsächlichen col-Werte nicht berücksichtigt werden):
df1[~df1.index.isin(df2.index)]
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.
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)
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)
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.
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
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)]
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]
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]