열의 문자열에서 원하지 않는 부분을 어떻게 제거합니까?
원래 질문이 게시 된 지 6 년이 지난 지금 팬더는 이러한 문자열 조작 작업을 간결하게 수행 할 수있는 "벡터화 된"문자열 함수를 많이 가지고 있습니다.
이 답변은 이러한 문자열 함수 중 일부를 탐색하고 더 빠른 대안을 제안하며 마지막에 타이밍 비교를 시작합니다.
일치시킬 부분 문자열 / 패턴을 지정하고 대체 할 부분 문자열을 지정하십시오.
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
유지하려는 부분 문자열을 추출하는 데 유용합니다.
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를 반환합니다.
모든 문자열이이 일관된 구조를 따른다고 가정하면 분할 작업이 이루어집니다.
# 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']])