Jak usunąć niechciane części z ciągów w kolumnie?
Sześć lat po opublikowaniu pierwotnego pytania pandy mają teraz dużą liczbę „wektoryzowanych” funkcji ciągów, które mogą zwięźle wykonywać te operacje na łańcuchach.
Ta odpowiedź pozwoli zbadać niektóre z tych funkcji ciągów, zasugerować szybsze alternatywy, a na końcu przejdzie do porównania czasów.
Określ podciąg / wzorzec do dopasowania oraz podciąg, którym ma zostać zastąpiony.
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
Jeśli potrzebujesz konwersji wyniku na liczbę całkowitą, możesz użyć Series.astype
,
df['result'] = df['result'].str.replace(r'\D', '').astype(int)
df.dtypes
time object
result int64
dtype: object
Jeśli nie chcesz modyfikować df
w miejscu, użyj DataFrame.assign
:
df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged
Przydatne do wyodrębniania podciągów, które chcesz zachować.
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
W extract
przypadku należy określić przynajmniej jedną grupę przechwytywania. expand=False
zwróci serię z przechwyconymi przedmiotami z pierwszej grupy przechwytywania.
Dzielenie działa przy założeniu, że wszystkie struny mają tę spójną strukturę.
# 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
Nie polecaj, jeśli szukasz ogólnego rozwiązania.
Jeśli podoba Ci się zwięzłe i czytelne str
rozwiązania oparte na akcesoriach powyżej, możesz zatrzymać się tutaj. Jeśli jednak interesują Cię szybsze, bardziej wydajne alternatywy, czytaj dalej.
Optymalizacja: listy składane
W pewnych okolicznościach listy składane powinny być preferowane w stosunku do funkcji łańcuchowych pandy. Powodem jest to, że funkcje łańcuchowe są z natury trudne do wektoryzacji (w prawdziwym tego słowa znaczeniu), więc większość funkcji ciągów i wyrażeń regularnych jest tylko opakowaniem wokół pętli z większym narzutem.
Mój opis: Czy pętle for w pandach są naprawdę złe? Kiedy powinno mnie to obchodzić?, omawia bardziej szczegółowo.
str.replace
Opcja może być ponownie napisane przy użyciure.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
str.extract
Przykładem może być ponownie napisane przy użyciu wyrażeń listowych z re.search
,
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
Jeśli istnieje możliwość uzyskania wartości NaN lub braku dopasowań, konieczne będzie ponowne napisanie powyższego, aby uwzględnić sprawdzanie błędów. Robię to za pomocą funkcji.
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
Możemy również przepisać odpowiedzi @ eumiro i @ MonkeyButter, używając wyrażeń listowych:
df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]
I,
df['result'] = [x[1:-1] for x in df['result']]
Obowiązują te same zasady obsługi NaN itp.
Porównanie wydajności
Wykresy generowane za pomocą perfplot . Pełna lista kodów w celach informacyjnych.Odpowiednie funkcje są wymienione poniżej.
Niektóre z tych porównań są niesprawiedliwe, ponieważ wykorzystują strukturę danych OP, ale wyciągają z nich to, co chcesz. Należy zauważyć, że każda funkcja rozumienia listy jest szybsza lub porównywalna niż jej odpowiednik w wersji pandy.
Funkcje
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']])