나는이 DataFrame
두 문자열 값을 포함하는 4 열을. 특정 열과 부분 문자열 일치를 기반으로 행을 선택하는 방법이 있는지 궁금합니다.
즉, 다음과 같은 기능을 수행하는 함수 또는 람다 함수
re.search(pattern, cell_in_question)
부울을 반환합니다. 나는 구문에 익숙 df[df['A'] == "hello world"]
하지만 부분 문자열 일치 say로 동일한 작업을 수행하는 방법을 찾지 못하는 것 같습니다 'hello'
.
누군가 올바른 방향으로 나를 가리킬 수 있습니까?
답변
github issue # 620에 따르면 곧 다음을 수행 할 수있는 것처럼 보입니다.
df[df['A'].str.contains("hello")]
업데이트 : 벡터화 된 문자열 메서드 (예 : Series.str) 는 pandas 0.8.1 이상에서 사용할 수 있습니다.
답변
위의 제안 된 솔루션을 시도했습니다.
df[df["A"].str.contains("Hello|Britain")]
오류가 발생했습니다.
ValueError : NA / NaN 값을 포함하는 배열로 마스크 할 수 없습니다
다음 False
과 같이 NA 값을로 변환 할 수 있습니다 .
df[df["A"].str.contains("Hello|Britain", na=False)]
답변
팬더 DataFrame에서 부분 문자열로 어떻게 선택합니까?
이 게시물은 원하는 독자를 대상으로합니다.
- 문자열 열에서 하위 문자열 검색 (가장 간단한 경우)
- 여러 개의 하위 문자열을 검색합니다 (와 유사
isin
). - 텍스트에서 단어 전체와 일치 (예 : “blue”는 “sky is blue”와 일치해야하지만 “bluejay”는 일치하지 않아야 함)
- 여러 단어 전체와 일치
- “ValueError : NA / NaN 값을 포함하는 벡터로 인덱싱 할 수 없음”의 원인 이해
… 다른 방법보다 어떤 방법을 선호해야하는지 더 알고 싶습니다.
(PS : 비슷한 주제에 대해 많은 질문을 보았습니다. 여기에 두는 것이 좋을 것이라고 생각했습니다.)
기본 부분 문자열 검색
# setup
df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']})
df1
col
0 foo
1 foobar
2 bar
3 baz
str.contains
하위 문자열 검색 또는 정규식 기반 검색을 수행하는 데 사용할 수 있습니다. 명시 적으로 비활성화하지 않는 한 검색은 기본적으로 정규식 기반입니다.
다음은 정규식 기반 검색의 예입니다.
# find rows in `df1` which contain "foo" followed by something
df1[df1['col'].str.contains(r'foo(?!$)')]
col
1 foobar
때로는 정규식 검색이 필요하지 않으므로 regex=False
비활성화 하도록 지정 하십시오.
#select all rows containing "foo"
df1[df1['col'].str.contains('foo', regex=False)]
# same as df1[df1['col'].str.contains('foo')] but faster.
col
0 foo
1 foobar
성능면에서 정규 표현식 검색은 하위 문자열 검색보다 느립니다.
df2 = pd.concat([df1] * 1000, ignore_index=True)
%timeit df2[df2['col'].str.contains('foo')]
%timeit df2[df2['col'].str.contains('foo', regex=False)]
6.31 ms ± 126 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.8 ms ± 241 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
필요하지 않은 경우 정규식 검색을 사용하지 마십시오.
주소 ValueError
들
, 때로는 부분 문자열 검색을 수행하고 결과를 필터링하는 것은에서 발생합니다
ValueError: cannot index with vector containing NA / NaN values
이는 일반적으로 개체 열의 혼합 데이터 또는 NaN 때문입니다.
s = pd.Series(['foo', 'foobar', np.nan, 'bar', 'baz', 123])
s.str.contains('foo|bar')
0 True
1 True
2 NaN
3 True
4 False
5 NaN
dtype: object
s[s.str.contains('foo|bar')]
# ---------------------------------------------------------------------------
# ValueError Traceback (most recent call last)
문자열이 아닌 것은 문자열 메서드를 적용 할 수 없으므로 결과는 NaN (당연히)입니다. 이 경우 na=False
문자열이 아닌 데이터를 무시하도록 지정하십시오 .
s.str.contains('foo|bar', na=False)
0 True
1 True
2 False
3 True
4 False
5 False
dtype: bool
여러 하위 문자열 검색
이것은 정규식 OR 파이프를 사용한 정규식 검색을 통해 가장 쉽게 달성됩니다.
# Slightly modified example.
df4 = pd.DataFrame({'col': ['foo abc', 'foobar xyz', 'bar32', 'baz 45']})
df4
col
0 foo abc
1 foobar xyz
2 bar32
3 baz 45
df4[df4['col'].str.contains(r'foo|baz')]
col
0 foo abc
1 foobar xyz
3 baz 45
용어 목록을 만든 다음 참여할 수도 있습니다.
terms = ['foo', 'baz']
df4[df4['col'].str.contains('|'.join(terms))]
col
0 foo abc
1 foobar xyz
3 baz 45
때로는 정규식 메타 문자 로 해석 될 수있는 문자가있는 경우 용어를 피하는 것이 좋습니다 . 용어에 다음 문자가 포함 된 경우 …
. ^ $ * + ? { } [ ] \ | ( )
그런 다음 사용해야합니다 re.escape
하기 위해 탈출 을 :
import re
df4[df4['col'].str.contains('|'.join(map(re.escape, terms)))]
col
0 foo abc
1 foobar xyz
3 baz 45
re.escape
특수 문자를 이스케이프 처리하여 문자 그대로 처리합니다.
re.escape(r'.foo^')
# '\\.foo\\^'
전체 단어 일치
기본적으로 하위 문자열 검색은 전체 단어인지 여부에 관계없이 지정된 하위 문자열 / 패턴을 검색합니다. 전체 단어 만 일치 시키려면 여기에서 정규 표현식을 사용해야합니다. 특히 패턴에 단어 경계 ( \b
) 를 지정해야합니다 .
예를 들어
df3 = pd.DataFrame({'col': ['the sky is blue', 'bluejay by the window']})
df3
col
0 the sky is blue
1 bluejay by the window
이제,
df3[df3['col'].str.contains('blue')]
col
0 the sky is blue
1 bluejay by the window
v / s
df3[df3['col'].str.contains(r'\bblue\b')]
col
0 the sky is blue
여러 단어 검색
\b
결합 된 패턴에 단어 경계 ( )를 추가한다는 점을 제외하면 위와 유사합니다 .
p = r'\b(?:{})\b'.format('|'.join(map(re.escape, terms)))
df4[df4['col'].str.contains(p)]
col
0 foo abc
3 baz 45
p
이렇게 보이는 곳
p
# '\\b(?:foo|baz)\\b'
훌륭한 대안 : 리스트 이해력 사용 !
넌 할 수 있으니까! 그리고 당신은해야합니다! 문자열 메서드는 벡터화하기 어렵고 일반적으로 루프 구현이 있기 때문에 일반적으로 문자열 메서드보다 약간 빠릅니다.
대신에,
df1[df1['col'].str.contains('foo', regex=False)]
in
목록 작성 도구 내 에서 연산자를 사용하십시오.
df1[['foo' in x for x in df1['col']]]
col
0 foo abc
1 foobar
대신에,
regex_pattern = r'foo(?!$)'
df1[df1['col'].str.contains(regex_pattern)]
목록 re.compile
정규식 내에서 (정규식을 캐시 하려면 ) +Pattern.search
p = re.compile(regex_pattern, flags=re.IGNORECASE)
df1[[bool(p.search(x)) for x in df1['col']]]
col
1 foobar
“col”에 NaN이 있으면 대신
df1[df1['col'].str.contains(regex_pattern, na=False)]
사용하다,
def try_search(p, x):
try:
return bool(p.search(x))
except TypeError:
return False
p = re.compile(regex_pattern)
df1[[try_search(p, x) for x in df1['col']]]
col
1 foobar
부분 문자열 매칭을위한 추가 옵션 : np.char.find
, np.vectorize
, DataFrame.query
.
str.contains
이해력과 목록 이해 뿐만 아니라 다음 대안을 사용할 수도 있습니다.
np.char.find
하위 문자열 검색 (읽기 : 정규식 없음) 만 지원합니다.
df4[np.char.find(df4['col'].values.astype(str), 'foo') > -1]
col
0 foo abc
1 foobar xyz
np.vectorize
이것은 루프를 감싸는 래퍼이지만 대부분의 팬더 str
메소드 보다 오버 헤드가 적습니다 .
f = np.vectorize(lambda haystack, needle: needle in haystack)
f(df1['col'], 'foo')
# array([ True, True, False, False])
df1[f(df1['col'], 'foo')]
col
0 foo abc
1 foobar
정규식 솔루션 가능 :
regex_pattern = r'foo(?!$)'
p = re.compile(regex_pattern)
f = np.vectorize(lambda x: pd.notna(x) and bool(p.search(x)))
df1[f(df1['col'])]
col
1 foobar
DataFrame.query
파이썬 엔진을 통한 문자열 메소드를 지원합니다. 이는 눈에 띄는 성능 이점을 제공하지는 않지만 쿼리를 동적으로 생성해야하는지 아는 것이 유용합니다.
df1.query('col.str.contains("foo")', engine='python')
col
0 foo
1 foobar
pd.eval ()을 사용하여 팬더의 Dynamic Expression Evaluation에서 메소드 에 대한 자세한 정보 query
와 eval
메소드 계열을 찾을 수 있습니다 .
권장 사용 우선 순위
- (첫 번째)
str.contains
, NaN 및 혼합 데이터의 단순성과 취급 용이성 - 성능에 대한 이해도를 나열하십시오 (특히 데이터가 순수한 문자열 인 경우).
np.vectorize
- (마지막)
df.query
답변
관련 문제를 수행하는 방법이 궁금하다면 “부분 문자열로 열 선택”
사용하다:
df.filter(like='hello') # select columns which contain the word hello
부분 문자열 일치로 행을 선택 axis=0
하려면 필터로 전달하십시오 .
# selects rows which contain the word hello in their index label
df.filter(like='hello', axis=0)
답변
빠른 참고 : 색인에 포함 된 부분 문자열을 기반으로 선택하려면 다음을 시도하십시오.
df['stridx']=df.index
df[df['stridx'].str.contains("Hello|Britain")]
답변
다음이 있다고 가정하십시오 DataFrame
.
>>> df = pd.DataFrame([['hello', 'hello world'], ['abcd', 'defg']], columns=['a','b'])
>>> df
a b
0 hello hello world
1 abcd defg
항상 in
람다 식에서 연산자를 사용하여 필터를 만들 수 있습니다.
>>> df.apply(lambda x: x['a'] in x['b'], axis=1)
0 True
1 False
dtype: bool
여기서 트릭은 열 단위가 아닌 요소를 람다 함수에 행 단위로 전달 하는 axis=1
옵션 을 사용하는 것입니다 apply
.
답변
다음은 부분 문자열 일치에 대한 결과입니다. 더 효율적인 방법이 있다면 알려주세요.
def stringSearchColumn_DataFrame(df, colName, regex):
newdf = DataFrame()
for idx, record in df[colName].iteritems():
if re.search(regex, record):
newdf = concat([df[df[colName] == record], newdf], ignore_index=True)
return newdf