Usuń niechciane części z łańcuchów w kolumnie

Szukam skutecznego sposobu na usunięcie niechcianych części z łańcuchów w kolumnie DataFrame.

Dane wyglądają następująco:

    time    result
1    09:00   +52A
2    10:00   +62B
3    11:00   +44a
4    12:00   +30b
5    13:00   -110a

Muszę przyciąć te dane do:

    time    result
1    09:00   52
2    10:00   62
3    11:00   44
4    12:00   30
5    13:00   110

Próbowałem .str.lstrip('+-') i .str.rstrip('aAbBcC'), ale mam błąd:

TypeError: wrapper() takes exactly 1 argument (2 given)

Wszelkie wskazówki będą bardzo mile widziane!

Author: cs95, 2012-12-03

9 answers

data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
 183
Author: eumiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-03 11:33:51

Jak usunąć niechciane części z napisów w kolumnie?

6 lat po opublikowaniu pierwotnego pytania, pandas ma teraz dużą liczbę" wektoryzowanych " funkcji ciągów, które mogą zwięźle wykonywać te operacje manipulacji ciągami.

Ta odpowiedź przeanalizuje niektóre z tych funkcji ciągów, zaproponuje szybsze alternatywy i przejdzie do porównania czasów na końcu.


.str.replace

Określ podłańcuch / wzorzec do dopasowania i podłańcuch do zastąpienia.

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 wyniku przekonwertowanego 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 in-place, użyj DataFrame.assign:

df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged

.str.extract

Przydatne do wyodrębniania podłańcuchó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

Z extract, konieczne jest określenie co najmniej jednej grupy przechwytywania. expand=False zwróci serię z przechwyconymi przedmiotami z pierwszej grupy przechwytywania.


.str.split oraz .str.get

Dzielenie działa zakładając, że wszystkie Twoje ciągi podążają za 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 zaleca się, jeśli szukasz ogólnego rozwiązania.


Jeśli jesteś zadowolony z zwięzłego i czytelnego str rozwiązania oparte na accessorze powyżej, można zatrzymać tutaj. Jednakże, jeśli jesteś zainteresowanych szybszymi, bardziej wydajnymi alternatywami, Czytaj dalej.


Optymalizacja: Składanie List

W pewnych okolicznościach, składanie list powinno być preferowane w stosunku do funkcji łańcuchowych pandas. Powodem jest to, że funkcje string są z natury trudne do wektoryzacji( w prawdziwym tego słowa znaczeniu), więc większość funkcji string i regex są tylko owijaczami wokół pętli z większym obciążeniem.

Moje zapiski, są dla-pętli w pandy naprawdę źle? Kiedy powinno mnie to obchodzić?, idzie bardziej szczegółowo.

Opcja str.replace może być zapisana ponownie za pomocą re.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

Przykład str.extract może być napisany na nowo przy użyciu listy 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ść Nan lub no-matching, musisz ponownie napisać powyższe, aby uwzględnić pewne 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ż ponownie napisać odpowiedzi @ eumiro i @ MonkeyButter za pomocą listy skład:

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

Tutaj wpisz opis obrazka

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 biorą z niej to, co chcesz. Należy pamiętać, że że każda funkcja rozumienia listy jest albo szybsza, albo porównywalna od jej równoważnego wariantu.

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']])
 121
Author: cs95,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-10 03:31:52

Użyłbym funkcji pandy replace, bardzo prostej i potężnej, ponieważ można użyć regex. Poniżej używam regex \d, aby usunąć wszelkie znaki niecyfrowe, ale oczywiście można uzyskać dość twórczy z regex.

data['result'].replace(regex=True,inplace=True,to_replace=r'\D',value=r'')
 56
Author: Coder375,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-31 14:57:04

W konkretnym przypadku, gdy znasz liczbę pozycji, które chcesz usunąć z kolumny dataframe, możesz użyć indeksowania łańcuchowego wewnątrz funkcji lambda, aby pozbyć się tych części:

Ostatni znak:

data['result'] = data['result'].map(lambda x: str(x)[:-1])

Pierwsze dwa znaki:

data['result'] = data['result'].map(lambda x: str(x)[2:])
 36
Author: prl900,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-06 23:27:54

Jest tu błąd: obecnie nie można przekazać argumentów do str.lstrip i str.rstrip:

Http://github.com/pydata/pandas/issues/2411

EDIT: 2012-12-07 to działa teraz na gałęzi dev:

In [8]: df['result'].str.lstrip('+-').str.rstrip('aAbBcC')
Out[8]: 
1     52
2     62
3     44
4     30
5    110
Name: result
 18
Author: Wes McKinney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-07 16:31:06

Bardzo prostą metodą byłoby użycie metody extract do wybrania wszystkich cyfr. Wystarczy podać Wyrażenie regularne '\d+', które wyciąga dowolną liczbę cyfr.

df['result'] = df.result.str.extract(r'(\d+)', expand=True).astype(int)
df

    time  result
1  09:00      52
2  10:00      62
3  11:00      44
4  12:00      30
5  13:00     110
 11
Author: Ted Petrou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-22 05:58:39

Często używam zestawień list do tego typu zadań, ponieważ często są szybsze.

Mogą występować duże różnice w wydajności pomiędzy różnymi metodami wykonywania takich rzeczy (tj. modyfikowanie każdego elementu serii w ramce danych). Często zrozumienie listy może być najszybsze-zobacz kod wyścigu poniżej dla tego zadania:

import pandas as pd
#Map
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))
10000 loops, best of 3: 187 µs per loop
#List comprehension
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in data['result']]
10000 loops, best of 3: 117 µs per loop
#.str
data = pd.DataFrame({'time':['09:00','10:00','11:00','12:00','13:00'], 'result':['+52A','+62B','+44a','+30b','-110a']})
%timeit data['result'] = data['result'].str.lstrip('+-').str.rstrip('aAbBcC')
1000 loops, best of 3: 336 µs per loop
 7
Author: tim654321,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-11 06:44:43

Załóżmy, że Twój DF ma również te Dodatkowe znaki pomiędzy liczbami.Ostatni wpis.

  result   time
0   +52A  09:00
1   +62B  10:00
2   +44a  11:00
3   +30b  12:00
4  -110a  13:00
5   3+b0  14:00
Możesz spróbować str.zastąp, aby usunąć znaki nie tylko od początku i końca, ale także od środka.
DF['result'] = DF['result'].str.replace('\+|a|b|\-|A|B', '')

Wyjście:

  result   time
0     52  09:00
1     62  10:00
2     44  11:00
3     30  12:00
4    110  13:00
5     30  14:00
 5
Author: Rishi Bansal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-15 04:24:22

Spróbuj użyć wyrażenia regularnego:

import re
data['result'] = data['result'].map(lambda x: re.sub('[-+A-Za-z]',x)
 0
Author: Mr. Prophet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-11 07:08:13