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


448

나는이 DataFrame두 문자열 값을 포함하는 4 열을. 특정 열과 부분 문자열 일치를 기반으로 행을 선택하는 방법이 있는지 궁금합니다.

즉, 다음과 같은 기능을 수행하는 함수 또는 람다 함수

re.search(pattern, cell_in_question) 

부울을 반환합니다. 나는 구문에 익숙 df[df['A'] == "hello world"]하지만 부분 문자열 일치 say로 동일한 작업을 수행하는 방법을 찾지 못하는 것 같습니다 'hello'.

누군가 올바른 방향으로 나를 가리킬 수 있습니까?

답변:


786

github issue # 620에 따르면 곧 다음을 수행 할 수있는 것처럼 보입니다.

df[df['A'].str.contains("hello")]

업데이트 : 벡터화 된 문자열 메서드 (예 : Series.str) 는 pandas 0.8.1 이상에서 사용할 수 있습니다.


1
"OR"상태로 찾으려면 "Hello"및 "Britain"에 대해 어떻게해야합니까?
LonelySoul

56
str. * 메소드는 입력 패턴을 정규식으로 취급하므로 다음을 사용할 수 있습니다.df[df['A'].str.contains("Hello|Britain")]
Garrett

7
api.str.contains 를 사용 하도록 변환 할 수 있습니까? .query()
zyxue


3
df[df['value'].astype(str).str.contains('1234.+')]문자열이 아닌 열을 필터링합니다.
François Leblanc

213

위의 제안 된 솔루션을 시도했습니다.

df[df["A"].str.contains("Hello|Britain")]

오류가 발생했습니다.

ValueError : NA / NaN 값을 포함하는 배열로 마스크 할 수 없습니다

다음 False과 같이 NA 값을로 변환 할 수 있습니다 .

df[df["A"].str.contains("Hello|Britain", na=False)]

54
또는 df [df [ 'A']. str.contains ( "Hello | Britain", na = False)]
joshlk

2
df[df['A'].astype(str).str.contains("Hello|Britain")]뿐만 아니라
Nagabhushan SN

108

팬더 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

두 개 이상의 열에서 문자열을 검색 할 때 사용할 올바른 방법으로 편집 할 수 있습니까? 기본적으로 : any(needle in haystack for needling in ['foo', 'bar'] and haystack in (df['col'], df['col2']))그리고 변형 나는 모든 질식을 시도했다 (그것은 불평 any()하고 올바르게 그렇게 ... ... 의사는 그런 쿼리를 수행하는 방법에 대해 행복하게 불분명합니다.
Denis de Bernardy

@DenisdeBernardydf[['col1', 'col2']].apply(lambda x: x.str.contains('foo|bar')).any(axis=1)
cs95

@ cs95 pandas df에서 + 뒤에 공백이 포함 된 하위 문자열이있는 행 추출 곧 답변되었지만 보려고 할 수도 있습니다.
ankii

@ankiiiiiii 내가 정규식 메타 문자를 언급 한 내 대답의 일부를 놓친 것 같습니다. "때로는 정규식 메타 문자로 해석 될 수있는 문자가있는 경우 용어를 피하는 것이 좋습니다."
cs95

1
이 경우 @ 00schneider r은 원시 문자열 리터럴을 나타내는 데 사용됩니다. 이를 통해 정규식 문자열을보다 쉽게 ​​작성할 수 있습니다. stackoverflow.com/q/2081640
cs95

53

관련 문제를 수행하는 방법이 궁금하다면 "부분 문자열로 열 선택"

사용하다:

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)  

6
이것은 다음과 같이 증류 될 수 있습니다 : df.loc[:, df.columns.str.contains('a')]
elPastor

18
다음으로 더 증류 할 수 있습니다df.filter(like='a')
Ted Petrou

이것은 자체 질문 + 답변이어야하며, 이미 50 명이 검색했습니다 ...
PV8

1
@ PV8 질문이 이미 존재합니다 : stackoverflow.com/questions/31551412/… . 그러나 Google에서 "부분 문자열로 팬더 선택 열"을 검색하면이 스레드가 먼저 나타납니다.
Philipp Schwarz

28

빠른 참고 : 색인에 포함 된 부분 문자열을 기반으로 선택하려면 다음을 시도하십시오.

df['stridx']=df.index
df[df['stridx'].str.contains("Hello|Britain")]

5
df [df.index.to_series (). str.contains ( 'LLChit')]
Yury Bayda

21

다음이 있다고 가정하십시오 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.


x [ 'a']가 x [ 'b']의 시작 부분에만 존재하도록 위의 내용을 어떻게 수정합니까?
ComplexData

1
적용은 성능과 메모리 측면에서 나쁜 생각입니다. 이 답변을 참조하십시오 .
cs95

8

다음은 부분 문자열 일치에 대한 결과입니다. 더 효율적인 방법이 있다면 알려주세요.

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

3
빠른 배에 배 할 필요가있는 경우 당신 루프 전에 컴파일 정규식 : 정규 표현식 = re.compile (정규식) 다음의 경우 regex.search (기록)
MarkokraM

1
@MarkokraM docs.python.org/3.6/library/re.html#re.compile에 따르면 가장 최근의 정규식이 자동으로 캐시되므로 직접 컴파일 할 필요가 없습니다.
Teepeemm

iteritems를 사용하여 DataFrame을 반복하지 마십시오. 그것은 pandorability 및 성능 측면에서 마지막 순위
cs95

5

특수 문자가 포함 된 문자열에 포함을 사용하면 제대로 작동하지 않습니다. 그래도 작동했습니다.

df[df['A'].str.find("hello") != -1]

2

이 전에 질문 된 기능을 수행하는 답변이 있습니다. 어쨌든 가장 일반적으로 보여주고 싶습니다.

df.filter(regex=".*STRING_YOU_LOOK_FOR.*")

이 방법을 사용하면 원하는 방식으로 작성한 열을 얻을 수 있습니다.

(분명히, 각 경우에 적합한 정규 표현식을 작성해야합니다)


1
머리글을 필터링합니다 . 일반적이지 않습니다. 정확하지 않습니다.
cs95

@MicheldeRuiter는 여전히 정확하지 않으며 대신 인덱스 레이블을 필터링합니다!
cs95

질문에 대답하지 않습니다. 그러나 나는 무언가를 배웠다. :)
Michel de Ruiter

2

팬더 데이터 프레임의 모든 열에서 일부 텍스트 만 검색하고 싶을 수도 있습니다. 이 경우 다음 코드가 도움이 될 것입니다.

df[df.apply(lambda row: row.astype(str).str.contains('String To Find').any(), axis=1)]

경고. 이 방법은 비교적 느리지 만 편리합니다.


2

팬더 데이터 프레임 열에서 문자열을 대소 문자를 구분하지 않고 검색 해야하는 경우 :

df[df['A'].str.contains("hello", case=False)]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.