Pandas-Filterung für mehrere Teilstrings in Serie
Brauche ich zum filtern von Zeilen in einer pandas
dataframe, so dass eine bestimmte Zeichenfolge-Spalte enthält mindestens eine aus einer Liste von Teilstrings zur Verfügung gestellt. Die Zeichenketten können ungewöhnliche /regex-Zeichen. Der Vergleich sollte nicht mit regex und ist der groß-und Kleinschreibung.
Beispiel:
lst = ['kdSj;af-!?', 'aBC+dsfa?\-', 'sdKaJg|dksaf-*']
Ich derzeit tragen Sie die Maske wie diese:
mask = np.logical_or.reduce([df[col].str.contains(i, regex=False, case=False) for i in lst])
df = df[mask]
Mein dataframe ist groß (~1mio Zeilen) und lst
hat die Länge 100. Gibt es einen effizienteren Weg? Zum Beispiel, wenn das erste Element in lst
gefunden wird, sollten wir Sie nicht haben, um zu testen alle nachfolgenden Zeichenfolgen für diese Zeile.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Sie ' re kleben mit pur-pandas, für Leistung und Funktionalität, die ich denke, Sie sollte Verwendung regex für diese Aufgabe. Allerdings werden Sie brauchen, um richtig zu escape-Sonderzeichen in den Zeichenketten ersten, um sicherzustellen, dass Sie abgestimmt sind buchstäblich (und nicht als regex-meta-Zeichen).
Dies ist einfach zu tun, mit
re.Flucht
:Diese flüchtete Teilstrings können dann verbunden werden mit einem regex-Rohr
|
. Jede der Teilfolgen kann überprüft werden, eine Zeichenfolge, bis einer passt (oder haben Sie alle getestet).Die Maskierung der Bühne ist, dann wird eine low-level-Schleife über die Zeilen:
Hier ist ein einfaches setup, um einen Eindruck zu bekommen, Leistung:
Dem vorgeschlagenen Verfahren dauert etwa 1 Sekunde (also vielleicht bis zu 20 Sekunden für 1 million Zeilen):
Die Methode in der Frage dauerte ungefähr 5 Sekunden lang mit den gleichen Eingabedaten.
Es ist erwähnenswert, dass diese Zeiten 'schlechteste Fall' in dem Sinne, dass es keine übereinstimmungen (so alle Teilstrings überprüft wurden). Wenn es passt, als das timing zu verbessern.
Könnten Sie versuchen, mit dem Aho-Corasick-Algorithmus. In der durchschnittlichen Fall, es ist
O(n+m+p)
won
ist die Länge des such-strings undm
ist die Länge des gesuchten Textes undp
ist die Anzahl der output entspricht.Den Aho-Corasick-Algorithmus ist oft verwendet mehrere Muster (Nadeln) in einen input-text (Heuhaufen).
pyahocorasick ist ein Python-wrapper um eine C-Implementierung des Algorithmus.
Lassen Sie uns vergleichen, wie schnell es ist, gegen einige alternativen. Unten ist ein benchmark
zeigt
using_aho_corasick
werden über 30x schneller als die original-Methode(siehe in der Frage) auf eine 50K-Zeile DataFrame test-Fall:
Hier das setup für die benchmark. Es wird auch überprüft, ob die Ausgabe entspricht dem Ergebnis
orig
:A.iter
ist aufgerufen, in einem Aufrufcol.apply
wocol
ist ein Pandabären-Serie. Das ist nicht sehr viel anders aus (oder vielleicht sogar genau das gleiche wie) was würden Sie tun, mit ein pandas DataFrame. Mitapply
hat etwa die gleiche Leistung wie ein einfaches Python-Schleife, aber Sie würden noch immer der Vorteil der Verwendung von Aho-Corasick-Algorithmus.Mit einem einfacheres Beispiel & Kleinschreibung ignorieren (groß-oder Kleinbuchstabe)
Filtern und bekommen einen binären Vektor:
Will ich zu finden, alle Elemente einer
pd.Series
,v
enthalten, "an" oder "Og". Und erhalten Sie 1, wenn das element enthält das Muster, oder 0, wenn nicht.Ich verwende den
re
:Mein Vektor:
Will ich zu finden, alle Elemente von v, die "auf" oder "Og".
Dies ist, ich kann definieren, meine
pattern
als:Da ich möchte, dass ein Vektor mit 1s, wenn das Element enthält das Muster, oder 0, wenn nicht.
Erstelle ich einen einheitlichen Vektor mit der gleichen Länge wie v:
Ich erhalten boolenean
s
istTrue
wenn ein elementv
enthält diepattern
oderFalse
wenn es nicht enthalten ist.Zu erhalten, die den binären Vektor I multiplizieren Sie die
v_binary
*s
:s.astype(int)
anstatt das ganze den binären Vektor-Logik. Ich sehe keinen grundlegenden Unterschied oder Vorteil im Vergleich zu @AlexRiley Lösung, Sie können sehen?pattern='wiring | media | elect
'v=pd.Series(['electricity fault'])
s=v.str.contains(pattern, flags=re.IGNORECASE, regex=True)
print(s)
Ausgabe:0 False dtype: bool