이번 포스팅에서는 Python pandas의 문자열 Series에서 문자열 패턴 매칭을 통해서 특정 패턴이 포함되어 있는지 여부를 확인하고, 특정 매칭을 포함한 데이터를 가져오는 방법을 소개하겠습니다. 

 

(1) pandas 문자열 Series에서 한개의 문자열 패턴 매칭하기 
     : Series.str.contains(pattern)

(2) pandas DataFrame에서 한개의 문자열 패턴 매칭이 되는 데이터 가져오기

(3) pandas DataFrame에서 여러개의 문자열 패턴 매팅이 되는 데이터 가져오기

 

 

먼저, 예제로 사용할 문자열이 포함된 DataFrame을 만들어보겠습니다. pandas 의 contains() 함수를 사용해서 문자열 뿐만 아니라 NaN 값과 '1004' 숫자도 포함시켜서 문자열 매칭 시 처리방법을 소개하겠습니다. 

 

## importing modules
import numpy as np
import pandas as pd

## creating a pandas DataFrame with strings, NaN, digit
df = pd.DataFrame({
    'id': [1, 2, 3, 4, 5, 6, 7]
    , 'fruit': ['apple', 'PERSIMON', 'grapes', 'mango', 'peach and perl', 
                np.NaN, 
                '1004']
})


print(df)
#    id           fruit
# 0   1           apple
# 1   2        PERSIMON
# 2   3          grapes
# 3   4           mango
# 4   5  peach and perl
# 5   6             NaN
# 6   7            1004

 

 

 

(1) pandas 문자열 Series에서 한개의 문자열 패턴 매칭하기: Series.str.contains(pattern)

 

먼저, 위에서 만든 DataFrame에서 'fruit' 칼럼만 가져와서 's1' 이라는 이름의 Series 를 만들어보겠습니다. 

 

## pandas Series
s1 = df['fruit']

print(type(s1)) # padnas.Series
print(s1)

# <class 'pandas.core.series.Series'>
# 0             apple
# 1          PERSIMON
# 2            grapes
# 3             mango
# 4    peach and perl
# 5               NaN
# 6              1004
# Name: fruit, dtype: object

 

 

pandas 의 contains() 메소드는 '문자열 Series (Series of a string)' 을 대상으로 문자열 매칭을 해서 Boolean Series 를 반환합니다. contains() 메소드의 구문은 아래와 같습니다. 

 

Series.str.contains(pattern, case=True, flags=0, na=None, regex=True)

 

이때 Series.str.contains() 메소드는 문자열 Series에 대하여 패턴 매칭(pattern matching)을 할 때 문자열 그 자체(literal itself)와 함께 정규표현식(regex=True: regular expression)까지도 사용해서 패턴 매칭을 할 수 있으며, '대/소문자 구분 (case=True: case sensitive)하며, 'NaN' 값에 대해서는 'NaN'을 반환(na=None)합니다. 

 

아래 예에서는 문자열 Series 's1'에 대해서 문자열 'pe'가 들어있는 패턴 매칭을 해서 Boolean Series 를 반환한 예입니다. (대소문자 구분, NaN은  NaN 반환) 

 

## returning a Series of Booleans using a literal pattern
s1.str.contains('pe')

# 0    False
# 1    False   # <-- case sensitive
# 2     True
# 3    False
# 4     True
# 5      NaN   # <-- returning NaN for NaN values
# 6    False
# Name: fruit, dtype: object

 

 

 

(2) pandas DataFrame에서 한개의 문자열 패턴 매칭이 되는 데이터 가져오기

 

pandas DataFrame에서 특정 문자열 칼럼에 대해서 문자열 패턴 매칭한 결과인 Boolean Series 를 이용해서 해당 행의 값만 가져올 수 있습니다. 이때 만약 문자열 패턴 매칭 결과 Boolean Seires 에 NaN 값이 포함되어 있을 경우 아래와 같은 ValueError 가 발생합니다. 

 

ValueError: Cannot mask with non-boolean array containing NA / NaN valu

 

 ## ValueError: Cannot mask with non-boolean array containing NA / NaN values
df[df['fruit'].str.contains('pe')]

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-5-ee5e3bc73f2f> in <module>
      1 ## ValueError: Cannot mask with non-boolean array containing NA / NaN values
----> 2 df[s1.str.contains('pe')]

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/frame.py in __getitem__(self, key)
   2890 
   2891         # Do we have a (boolean) 1d indexer?
-> 2892         if com.is_bool_indexer(key):
   2893             return self._getitem_bool_array(key)
   2894 

~/opt/anaconda3/lib/python3.8/site-packages/pandas/core/common.py in is_bool_indexer(key)
    132                 na_msg = "Cannot mask with non-boolean array containing NA / NaN values"
    133                 if isna(key).any():
--> 134                     raise ValueError(na_msg)
    135                 return False
    136             return True

ValueError: Cannot mask with non-boolean array containing NA / NaN values

 

 

'ValueError: Cannot mask with non-boolean array containing NA/NaN values' 를 해결하기 위해서는 Series.str.contains(pattern, na=False) 처럼 NaN 값을 Boolean의 'False'로 설정해주면 됩니다. 

 

## Specifying na to be False instead of NaN replaces NaN values with False.
df[df['fruit'].str.contains('pe'
                   , na=False) # specifying NA to be False
  ]

#  id	fruit
# 2	3	grapes
# 4	5	peach and perl

 

 

만약 문자열 매칭을 할 때 '대/소문자 구분없이 (case insensitive)' 하려면 'case=False' 옵션을 설정해주면 됩니다. 

아래 예에서는 case=False 로 설정한 상태에서 'pe' 문자열 매칭을 했더니 'PERSIMON' 대문자도 매칭이 되어서 가져오기가 되었습니다. 

 

## Specifying case sensitivity using case.
df[df['fruit'].str.contains('pe'
                   , na=False
                   , case=False) # case = False
  ] 

#   id	fruit
# 1	2	PERSIMON   # <-- case insensitive
# 2	3	grapes
# 4	5	peach and perl

 

 

Series.str.contains() 함수에는 정규표현식(regex=True: Regular Expression)을 사용해서 문자열 매칭을 할 수 있습니다.  아래의 예에서는 정규표현식을 이용해서 '숫자가 포함된 ('\\d' : returning any digits)' 문자열을 가져와보겠습니다. 

 

## returning any digit using regular expression
df[df['fruit'].str.contains(
    '\\d'        # returning any digit
    , regex=True # using regular expression
    , na=False
    )
  ]

#   id	fruit
# 6	7	1004

 

 

 

(3) pandas DataFrame에서 여러개의 문자열 패턴 매팅이 되는 데이터 가져오기

 

이번에는 문자열 매칭을 할 때 '여러개의 문자열 패턴 (multiple strings of pattern)' 과 매칭되는 문자열을 확인하고, pandas DataFrame으로 부터 해당 행의 데이터를 가져와보겠습니다. 

 

여러개의 문자열 패턴을 표현할 때 '|' 가 'or' 를 나타냅니다. 아래의 예의 경우, ['ap' or 'ma' or 'gr'] 이 포함된 문자열을 매칭해서 Boolean String을 반환하고 싶을 때 ['ap'|'ma'|'gr'] 을 패턴으로 입력해주면 됩니다. Python의 내장함수(built-in function) 중에서 join() 메소드를 이용하면 여러개의 문자열을 '|' 구분자(separator)를 넣어서 하나의 문자열로 묶어줄 수 있습니다. ('|'.join(['ap', 'ma', 'gr']) 은 ==> 'ap|ma|gr' 을 반환하며, ==> ['ap' or 'ma' or 'gr'] 을 의미함)

 

## join() method joins all itmes in a tuple into a string with a separartor
'|'.join(['ap', 'ma', 'gr'])

# 'ap|ma|gr'


## Returning ‘apple’ or ‘mango’ or 'grapes' 
## when either expression occurs in a string.
s1.str.contains(
    '|'.join(['ap', 'ma', 'gr']) # 'ap|ma|gr', ie. 'ap' or 'ma' or 'gr'
    , na=False
    , case=False
)

# 0     True
# 1    False
# 2     True
# 3     True
# 4    False
# 5    False
# 6    False
# Name: fruit, dtype: bool

 

 

이제 pandas DataFrame 에서 'fruit' 칼럼에서 'ap' or 'ma' or 'gr' 문자열이 포함되어 있는 모든 행을 가져와보겠습니다. 

 

## indexing data using a Series of Booleans 
df[df['fruit'].str.contains(
    '|'.join(['ap', 'ma', 'gr'])
    , na=False
    , case=False
    )
  ]

#   id	fruit
# 0	1	apple
# 2	3	grapes
# 3	4	mango

 

 

[ Reference ]

[1] pandas.Series.str.contains()
    : https://pandas.pydata.org/docs/reference/api/pandas.Series.str.contains.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요