Wie entferne ich unerwünschte Teile aus Zeichenfolgen in einer Spalte?
6 Jahre nachdem die ursprüngliche Frage veröffentlicht wurde, verfügt Pandas nun über eine gute Anzahl von "vektorisierten" Zeichenfolgenfunktionen, mit denen diese Zeichenfolgenmanipulationsoperationen kurz und bündig ausgeführt werden können.
In dieser Antwort werden einige dieser Zeichenfolgenfunktionen untersucht, schnellere Alternativen vorgeschlagen und am Ende ein Zeitvergleich durchgeführt.
Geben Sie den passenden Teilstring / das passende Muster und den zu ersetzenden Teilstring an.
pd.__version__
# '0.24.1'
df
time result
1 09:00 +52A
2 10:00 +62B
3 11:00 +44a
4 12:00 +30b
5 13:00 -110a
df['result'] = df['result'].str.replace(r'\D', '')
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Wenn Sie das Ergebnis in eine Ganzzahl konvertieren möchten, können Sie Folgendes verwenden Series.astype
:
df['result'] = df['result'].str.replace(r'\D', '').astype(int)
df.dtypes
time object
result int64
dtype: object
Wenn Sie nicht direkt ändern möchten df
, verwenden Sie DataFrame.assign
:
df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged
Nützlich zum Extrahieren der Teilzeichenfolge (n), die Sie behalten möchten.
df['result'] = df['result'].str.extract(r'(\d+)', expand=False)
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Mit extract
muss mindestens eine Erfassungsgruppe angegeben werden. expand=False
gibt eine Serie mit den erfassten Elementen aus der ersten Erfassungsgruppe zurück.
Das Teilen funktioniert unter der Annahme, dass alle Ihre Zeichenfolgen dieser konsistenten Struktur folgen.
# df['result'] = df['result'].str.split(r'\D').str[1]
df['result'] = df['result'].str.split(r'\D').str.get(1)
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Nicht empfehlen, wenn Sie nach einer allgemeinen Lösung suchen.
Wenn Sie mit den oben genannten prägnanten und lesbaren str
Accessor-basierten Lösungen zufrieden sind , können Sie hier aufhören. Wenn Sie jedoch an schnelleren und leistungsfähigeren Alternativen interessiert sind, lesen Sie weiter.
Optimieren: Listenverständnisse
Unter bestimmten Umständen sollte das Listenverständnis den Pandas-String-Funktionen vorgezogen werden. Der Grund dafür ist, dass Zeichenfolgenfunktionen von Natur aus schwer zu vektorisieren sind (im wahrsten Sinne des Wortes), sodass die meisten Zeichenfolgen- und Regex-Funktionen nur Wrapper um Schleifen mit mehr Overhead sind.
Mein Artikel: Sind For-Loops bei Pandas wirklich schlecht? Wann sollte es mich interessieren? geht näher darauf ein.
Die str.replace
Option kann mit neu geschrieben werdenre.sub
import re
# Pre-compile your regex pattern for more performance.
p = re.compile(r'\D')
df['result'] = [p.sub('', x) for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Das str.extract
Beispiel kann unter Verwendung eines Listenverständnisses mit re.search
, neu geschrieben werden .
p = re.compile(r'\d+')
df['result'] = [p.search(x)[0] for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Wenn NaNs oder Nichtübereinstimmungen möglich sind, müssen Sie die obigen Informationen neu schreiben, um eine Fehlerprüfung einzuschließen. Ich mache das mit einer Funktion.
def try_extract(pattern, string):
try:
m = pattern.search(string)
return m.group(0)
except (TypeError, ValueError, AttributeError):
return np.nan
p = re.compile(r'\d+')
df['result'] = [try_extract(p, x) for x in df['result']]
df
time result
1 09:00 52
2 10:00 62
3 11:00 44
4 12:00 30
5 13:00 110
Wir können die Antworten von @ eumiro und @ MonkeyButter auch mithilfe von Listenverständnissen neu schreiben:
df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]
Und,
df['result'] = [x[1:-1] for x in df['result']]
Es gelten die gleichen Regeln für den Umgang mit NaNs usw.
Leistungsvergleich
Mit Perfplot erzeugte Diagramme . Vollständige Codeliste als Referenz. Die relevanten Funktionen sind unten aufgeführt.
Einige dieser Vergleiche sind unfair, weil sie die Struktur der OP-Daten ausnutzen, aber daraus entnehmen, was Sie wollen. Zu beachten ist, dass jede Listenverständnisfunktion entweder schneller oder vergleichbar ist als die entsprechende Pandas-Variante.
Funktionen
def eumiro(df):
return df.assign(
result=df['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC')))
def coder375(df):
return df.assign(
result=df['result'].replace(r'\D', r'', regex=True))
def monkeybutter(df):
return df.assign(result=df['result'].map(lambda x: x[1:-1]))
def wes(df):
return df.assign(result=df['result'].str.lstrip('+-').str.rstrip('aAbBcC'))
def cs1(df):
return df.assign(result=df['result'].str.replace(r'\D', ''))
def cs2_ted(df):
# `str.extract` based solution, similar to @Ted Petrou's. so timing together.
return df.assign(result=df['result'].str.extract(r'(\d+)', expand=False))
def cs1_listcomp(df):
return df.assign(result=[p1.sub('', x) for x in df['result']])
def cs2_listcomp(df):
return df.assign(result=[p2.search(x)[0] for x in df['result']])
def cs_eumiro_listcomp(df):
return df.assign(
result=[x.lstrip('+-').rstrip('aAbBcC') for x in df['result']])
def cs_mb_listcomp(df):
return df.assign(result=[x[1:-1] for x in df['result']])