열의 문자열에서 원치 않는 부분 제거


129

DataFrame 열의 문자열에서 원하지 않는 부분을 제거하는 효율적인 방법을 찾고 있습니다.

데이터는 다음과 같습니다.

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

이 데이터를 다음과 같이 정리해야합니다.

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

나는 노력했다 .str.lstrip('+-'). str.rstrip('aAbBcC')오류가 발생했습니다.

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

어떤 포인터라도 대단히 감사하겠습니다!

답변:


167
data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))

고마워! 작동합니다. 나는 여전히 내 마음을 map () 주위에 감싸고 있는데, 언제 사용할지 또는 사용하지
않을지

이 방법이 replace 기능과 함께 작동한다는 것을 알게되어 기뻤습니다.
BKay

@eumiro 각 열을 반복하는 경우이 결과를 어떻게 적용합니까?
medev21

이 함수를 사용하여 숫자 12와 같은 숫자를 바꿀 수 있습니까? x.lstrip ('12 ')을 수행하면 1과 2가 모두 제거됩니다.
Dave

76

열의 문자열에서 원하지 않는 부분을 어떻게 제거합니까?

원래 질문이 게시 된 지 6 년이 지난 지금 팬더는 이러한 문자열 조작 작업을 간결하게 수행 할 수있는 "벡터화 된"문자열 함수를 많이 가지고 있습니다.

이 답변은 이러한 문자열 함수 중 일부를 탐색하고 더 빠른 대안을 제안하며 마지막에 타이밍 비교를 시작합니다.


.str.replace

일치시킬 부분 문자열 / 패턴을 지정하고 대체 할 부분 문자열을 지정하십시오.

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

결과를 정수로 변환해야하는 경우 Series.astype,

df['result'] = df['result'].str.replace(r'\D', '').astype(int)

df.dtypes
time      object
result     int64
dtype: object

내부 수정 df을 원하지 않으면 다음을 사용하십시오 DataFrame.assign.

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

.str.extract

유지하려는 부분 문자열을 추출하는 데 유용합니다.

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

를 사용하면 extract하나 이상의 캡처 그룹을 지정해야합니다. expand=False첫 번째 캡처 그룹에서 캡처 한 항목이있는 Series를 반환합니다.


.str.split.str.get

모든 문자열이이 일관된 구조를 따른다고 가정하면 분할 작업이 이루어집니다.

# 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

일반적인 솔루션을 찾고 있다면 권장하지 않습니다.


간결하고 읽기 쉬운 str 액세서 기반 솔루션에 만족 하면 여기서 멈출 수 있습니다. 그러나 더 빠르고 성능이 우수한 대안에 관심이있는 경우 계속 읽으십시오.


최적화 : 목록 이해

어떤 상황에서는 판다 문자열 함수보다 목록 이해력이 선호되어야합니다. 그 이유는 문자열 함수가 본질적으로 벡터화하기가 어렵 기 때문에 (단어의 의미에서) 대부분의 문자열 및 정규식 함수는 오버 헤드가 더 많은 루프를 둘러싸는 래퍼 일뿐입니다.

내 글 은 팬더의 for-loops가 정말로 나쁜가? 언제 걱정해야합니까? 더 자세히 설명합니다.

str.replace옵션을 사용하여 다시 쓸 수있다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

str.extract예는 목록 이해를 사용하여 다시 작성할 수 있습니다 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

NaN 또는 불일치 가능성이있는 경우 일부 오류 검사를 포함하도록 위의 내용을 다시 작성해야합니다. 함수를 사용 하여이 작업을 수행합니다.

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

리스트 이해를 사용하여 @eumiro와 @MonkeyButter의 답변을 다시 쓸 수도 있습니다.

df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]

과,

df['result'] = [x[1:-1] for x in df['result']]

NaN 등을 처리하기위한 동일한 규칙이 적용됩니다.


성능 비교

여기에 이미지 설명을 입력하십시오

perfplot을 사용하여 생성 된 그래프 . 참고로 전체 코드 목록. 관련 기능은 다음과 같습니다.

이러한 비교 중 일부는 OP의 데이터 구조를 이용하기 때문에 불공평하지만 원하는 것을 취합니다. 한 가지 주목할 점은 모든 목록 이해 기능이 동등한 팬더 변형보다 빠르거나 비슷하다는 것입니다.

기능

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']])

copywarning으로 설정을 피하기위한 모든 해결 방법 :Try using .loc[row_indexer,col_indexer] = value instead
PV8

@ PV8 코드에 대해 잘 모르지만 이것을 확인하십시오. stackoverflow.com/questions/20625582/…
cs95

나와 같은 REGEX를 처음 사용하는 사람이라면 \ D는 [^ \ d] (숫자가 아닌 것) 와 동일합니다 . 따라서 기본적으로 문자열의 숫자가 아닌 모든 숫자를 아무것도 바꾸지 않습니다.
Rishi Latchmepersad

56

팬더 바꾸기 기능을 사용합니다. 정규식을 사용할 수 있으므로 매우 간단하고 강력합니다. 아래에서는 정규 표현식 \ D를 사용하여 숫자가 아닌 문자를 제거하지만 분명히 정규 표현식으로 창의력을 발휘할 수 있습니다.

data['result'].replace(regex=True,inplace=True,to_replace=r'\D',value=r'')

나는 이것을 시도했지만 작동하지 않습니다. 하위 문자열 부분을 바꾸는 대신 전체 문자열을 바꾸고 싶을 때만 작동하는지 궁금합니다.
bgenchel

@bgenchel-pd.Series에서 문자열의 일부를 대체하기 위해이 방법을 사용했습니다 df.loc[:, 'column_a'].replace(regex=True, to_replace="my_prefix", value="new_prefix"). "my_prefixaaa"와 같은 문자열을 "new_prefixaaa"로 변환합니다.
jakub

r은 to_replace = r '\ D'에서 무엇을합니까?
Luca Guarro

python docs의 @LucaGuarro : "이 예제에서는 리터럴을 원시 문자열 리터럴로 만드는 r 접두사가 필요합니다. 정규 표현식과 달리 파이썬에서 인식하지 않는 일반적인"요리 된 "문자열 리터럴의 이스케이프 시퀀스는 이제 DeprecationWarning이 발생하여 SyntaxError가됩니다. "
Coder375

35

데이터 프레임 열에서 제거 할 위치 수를 알고있는 특별한 경우 람다 함수 내에서 문자열 인덱싱을 사용하여 해당 부분을 제거 할 수 있습니다.

마지막 캐릭터 :

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

처음 두 문자 :

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

지리 좌표를 8 자 ((.), (-) 포함)로 자르고 8 자 미만인 경우 마지막에 '0'을 삽입하여 모든 좌표를 8 자로 만들어야합니다. 그렇게하는 가장 간단한 방법은 무엇입니까?
Sitz Blogz

. 형식 (X) : "{.8f 0}"나는 완전히 문제를 이해하지 않습니다하지만 당신은 같은 것을에 람다 함수를 변경해야 할 수도 있습니다
prl900

답장을 보내 주셔서 감사합니다. 간단히 말하면 지리적 좌표가있는 데이터 프레임이 있습니다-위도 및 경도는 두 개의 열입니다. 문자 길이는 8 자 이상이며 처음부터 8 자만 유지했는데 (-) 및 (.)도 포함해야합니다.
Sitz Blogz

18

여기에 버그가 있습니다 : 현재 str.lstripand에 인수를 전달할 수 없습니다 str.rstrip:

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

편집 : 2012-12-07 이것은 이제 dev 분기에서 작동합니다.

In [8]: df['result'].str.lstrip('+-').str.rstrip('aAbBcC')
Out[8]: 
1     52
2     62
3     44
4     30
5    110
Name: result

11

매우 간단한 방법은이 방법을 사용하여 extract모든 숫자를 선택하는 것입니다. '\d+'숫자를 추출 하는 정규식을 간단히 제공하십시오 .

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

7

나는 종종 더 빠르기 때문에 이러한 유형의 작업에 대해 목록 이해력을 종종 사용합니다.

이와 같은 작업을 수행하는 다양한 방법 (예 : DataFrame 내에서 계열의 모든 요소 수정)간에 성능에 큰 차이가있을 수 있습니다. 종종 목록 이해력이 가장 빠를 수 있습니다.이 작업에 대해서는 아래 코드 레이스를 참조하십시오.

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

4

DF에 숫자 사이에 추가 문자가 있다고 가정합니다. 마지막 항목.

  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

str.replace를 사용하여 시작과 끝뿐만 아니라 그 사이의 문자를 제거 할 수 있습니다.

DF['result'] = DF['result'].str.replace('\+|a|b|\-|A|B', '')

산출:

  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

0

정규 표현식을 사용하여 시도하십시오.

import re
data['result'] = data['result'].map(lambda x: re.sub('[-+A-Za-z]',x)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.