[python] 팬더 DataFrame에서 부분 문자열로 선택

나는이 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에서 메소드 에 대한 자세한 정보 queryeval메소드 계열을 찾을 수 있습니다 .


권장 사용 우선 순위

  1. (첫 번째) str.contains, NaN 및 혼합 데이터의 단순성과 취급 용이성
  2. 성능에 대한 이해도를 나열하십시오 (특히 데이터가 순수한 문자열 인 경우).
  3. np.vectorize
  4. (마지막) 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