[R] stringr 패키지를 사용한 정규표현식

 이전 장에서 R에서 사용되는 정규표현식에 대해 알아보았었는데요. 오늘은 정규표현식을 텍스트 데이터를 처리할때 사용하는 패키지인 stringr 과 함께 사용하는 방법에 대해 알아보도록 하겠습니다.

stringr

  stringr 패키지는 Rstudio의 Hadley Wickham이 개발한 패키지입니다. dplyr , ggplot2 등과 같이 데이터를 깔끔한(tidy) 방식으로 처리하는것을 지향하는 tidyverse 패키지군에 속해 있으며, 그 중 stringr 은 문자열 처리에 특화된 패키지입니다. ‘string’의 준말인 str_이 함수 머릿말에 항상 등장하는 것이 특징입니다.

  stringr 패키지의 주요 기능을 아래와 같이 분류하였고 각 경우에 맞는 상황을 가정하여 알아보도록 하겠습니다.

1. 특정 패턴과 ‘일치’
2. 특정 패턴의 ‘카운팅’
3. 특정 패턴이 ‘포함’ 된 경우
4. 특정 패턴의 ‘위치’ 를 반환
5. 특정 패턴을 새로운 것으로 ‘바꾸는’ 경우
6. 특정 패턴을 기준으로 ‘나누는’ 경우

예제 문장

 연습용으로 사용할 문장은 stringr 패키지에 내장되어 있는 sentence 데이터를 사용하겠습니다. sentence는 총 1000문장으로 구성되어 있는데, 문장 전체를 사용할 필요가 없으므로 10문장 정도만을 추려서 사용하도록 하겠습니다.

regex_sentences <- stringr::sentences[1:10] # 예제용 문장 생성

regex_sentences
#>  [1] "The birch canoe slid on the smooth planks." 
#>  [2] "Glue the sheet to the dark blue background."
#>  [3] "It's easy to tell the depth of a well."     
#>  [4] "These days a chicken leg is a rare dish."   
#>  [5] "Rice is often served in round bowls."       
#>  [6] "The juice of lemons makes fine punch."      
#>  [7] "The box was thrown beside the parked truck."
#>  [8] "The hogs were fed chopped corn and garbage."
#>  [9] "Four hours of steady work faced us."        
#> [10] "Large size in stockings is hard to sell."

일치

  •  str_detect()함수는 특정 문자 혹은 패턴(정규표현식)과 일치하는 경우를 찾을때 사용하며, 실행 결과로서 boolean(참, 거짓)을 반환합니다.
str_detect(regex_sentences, '[aeiou]') # 알파벳 모음 'a' or 'e' or 'i' or 'o' or 'u'가 있을 경우 TRUE
#>  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
str_detect(regex_sentences, '[easy]')  # 알파벳 'e' or 'a' or 's' or 'y'가 있을 경우 TRUE 
#>  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
str_detect(regex_sentences, '(easy)')  # 알파벳 'easy'가 있을 경우 TRUE 
#>  [1] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
  •  str_extract()함수는 특정 문자 혹은 패턴(정규표현식)과 일치하는 경우, 그것을 반환합니다.
str_extract(regex_sentences, '[aeiou]') # 'a' or 'e' or 'i' or 'o' or 'u' 글자가 존재하면 그것을 반환 
#>  [1] "e" "u" "e" "e" "i" "e" "e" "e" "o" "a"
str_extract(regex_sentences, '[:punct:]') # 문장부호가 존재하면 그것을 반환 
#>  [1] "." "." "'" "." "." "." "." "." "." "."

 함수는 조건에 일치하는 결과를 반환하였는데요. 자세히 보면 조건에 일치한 것이 여러개 있음에도 불구하고 오직 최초 값만을 반환하는 것을 볼 수 있습니다. 그렇다면 조건에 일치하는 모든 값을 찾고자 할 땐 어떻게 해야할까요? 바로 str_extract_all() 함수를 사용하면 됩니다. stringr 패키지 함수 중 몇몇의 함수는 함수 말미에 _all이 붙는 함수들이 있습니다. 이들 함수들은 조건에 맞는 모든 값을 반환하는 특징이 있습니다.

str_extract_all(regex_sentences, '[aeiou]')
#> [[1]]
#>  [1] "e" "i" "a" "o" "e" "i" "o" "e" "o" "o" "a"
#> 
#> [[2]]
#>  [1] "u" "e" "e" "e" "e" "o" "e" "a" "u" "e" "a" "o" "u"
#> 
#> [[3]]
#> [1] "e" "a" "o" "e" "e" "e" "o" "a" "e"
#> 
#> [[4]]
#>  [1] "e" "e" "a" "a" "i" "e" "e" "i" "a" "a" "e" "i"
#> 
#> [[5]]
#>  [1] "i" "e" "i" "o" "e" "e" "e" "i" "o" "u" "o"
#> 
#> [[6]]
#>  [1] "e" "u" "i" "e" "o" "e" "o" "a" "e" "i" "e" "u"
#> 
#> [[7]]
#>  [1] "e" "o" "a" "o" "e" "i" "e" "e" "a" "e" "u"
#> 
#> [[8]]
#>  [1] "e" "o" "e" "e" "e" "o" "e" "o" "a" "a" "a" "e"
#> 
#> [[9]]
#>  [1] "o" "u" "o" "u" "o" "e" "a" "o" "a" "e" "u"
#> 
#> [[10]]
#>  [1] "a" "e" "i" "e" "i" "o" "i" "i" "a" "o" "e"
str_extract_all(regex_sentences, '[:punct:]')
#> [[1]]
#> [1] "."
#> 
#> [[2]]
#> [1] "."
#> 
#> [[3]]
#> [1] "'" "."
#> 
#> [[4]]
#> [1] "."
#> 
#> [[5]]
#> [1] "."
#> 
#> [[6]]
#> [1] "."
#> 
#> [[7]]
#> [1] "."
#> 
#> [[8]]
#> [1] "."
#> 
#> [[9]]
#> [1] "."
#> 
#> [[10]]
#> [1] "."

 str_extract_all() 함수의 결과, 조건에 부합하는 모든 값이 list 형으로 반환된 것을 볼 수 있습니다.

카운팅

  •  str_count()함수는 특정 문자 혹은 패턴(정규표현식)과 일치하는 경우를 계산합니다.
str_count(regex_sentences, '[aeiou]') # 'a' or 'e' or 'i' or 'o' or 'u' 문자의 개수를 반환 
#>  [1] 11 13  9 12 11 12 11 12 11 11
str_count(regex_sentences, '^(The)') # 시작이 'The'인 경우를 셈 
#>  [1] 1 0 0 1 0 1 1 1 0 0

포함

  •  str_subset()함수는 특정 문자 혹은 패턴(정규표현식)이 포함된 경우를 반환합니다.
str_subset(regex_sentences, '[aeiou]') # 'a' or 'e' or 'i' or 'o' or 'u' 문자가 포함된 경우를 반환 
#>  [1] "The birch canoe slid on the smooth planks." 
#>  [2] "Glue the sheet to the dark blue background."
#>  [3] "It's easy to tell the depth of a well."     
#>  [4] "These days a chicken leg is a rare dish."   
#>  [5] "Rice is often served in round bowls."       
#>  [6] "The juice of lemons makes fine punch."      
#>  [7] "The box was thrown beside the parked truck."
#>  [8] "The hogs were fed chopped corn and garbage."
#>  [9] "Four hours of steady work faced us."        
#> [10] "Large size in stockings is hard to sell."
str_subset(regex_sentences, '^(The)')  # 시작이 'The'인 문장을 반환
#> [1] "The birch canoe slid on the smooth planks." 
#> [2] "These days a chicken leg is a rare dish."   
#> [3] "The juice of lemons makes fine punch."      
#> [4] "The box was thrown beside the parked truck."
#> [5] "The hogs were fed chopped corn and garbage."
str_subset(regex_sentences, 'p[:alpha:]{1,}\\.$') # 'p'뒤에 알파벳이 한번 이상 나오면서 마침표로 끝나는 경우 
#> [1] "The birch canoe slid on the smooth planks."
#> [2] "The juice of lemons makes fine punch."

위치

  •  str_locate()함수는 특정 문자 혹은 패턴(정규표현식)에 맞는 경우의 시작 지점과 끝 지점을 반환합니다. R에 기본적으로 내장되어 있는 영어 소문자 데이터인 letters를 통해 알아보겠습니다.
letters
#>  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
#> [20] "t" "u" "v" "w" "x" "y" "z"

 패턴의 위치를 찾기 위해선 하나로 묶인 데이터가 필요하므로 str_c() 함수를 사용하여 하나의 벡터로 묶었습니다. (str_c()함수는 __stringr__패키지의 문자를 결합하는 함수. R의 내장함수인 paste()와 동일하게 동작)

letters <- str_c(letters, collapse = '');

letters # 하나의 벡터값으로 변환 
#> [1] "abcdefghijklmnopqrstuvwxyz"
str_locate(letters, '[a-z]+') # 'a'부터 'z' 까지의 문자가 한개 이상 나오는 경우의 시작과 끝을 반환 
#>      start end
#> [1,]     1  26
str_locate(letters, '^[a][a-z]+[z]$') # 시작이 'a'이며, 바로 뒤에 'a' 부터 'z'까지의 문자 중 하나가 한번 이상 나오며, 끝이 'z'인 경우의 시작 지점과 끝 지점을 반환.
#>      start end
#> [1,]     1  26
str_locate(letters, '^[a-z][a-z]+$') # 시작이 'a' 부터 'z' 까지의 문자 중 하나이며, 끝이 'a' 부터 'z' 까지의 문자 중 하나인 경우의 시작 지점과 끝 지점.
#>      start end
#> [1,]     1  26
str_locate(letters, '(abcde)') # 'abcde'의 시작 지점과 끝 지점을 반환 
#>      start end
#> [1,]     1   5
str_locate(letters, '(xyz)') # 'xyz'의 시작 지점과 끝 지점을 반환  
#>      start end
#> [1,]    24  26

 str_locate_all()함수를 통해 조건에 맞는 모든 값의 위치를 추출할 수 있습니다.

str_locate(letters, '[aeiou][^aeiou]+') # 'a' or 'e' or 'i' or 'o' or 'u'문자 뒤에 'a' or 'e' or 'i' or 'o' or 'u' 문자가 아닌 문자가 하나 이상 나오는 최초 경우의 시작 지점과 끝 지점을 반환 
#>      start end
#> [1,]     1   4
str_locate_all(letters, '[aeiou][^aeiou]+') # 'a' or 'e' or 'i' or 'o' or 'u'문자 뒤에 'a' or 'e' or 'i' or 'o' or 'u' 문자가 아닌 문자가 하나 이상 나오는 모든 경우의 시작 지점과 끝 지점을 반환 
#> [[1]]
#>      start end
#> [1,]     1   4
#> [2,]     5   8
#> [3,]     9  14
#> [4,]    15  20
#> [5,]    21  26

바꾸기

  •  str_replace()는 특정 문자 혹은 패턴(정규표현식)에 맞는 경우를 새로운 문자로 바꾸는 함수입니다.
str_replace(regex_sentences, 'k', "'K'") # 최초로 매칭되는 문자 k를 'K'로 변환  
#>  [1] "The birch canoe slid on the smooth plan'K's." 
#>  [2] "Glue the sheet to the dar'K' blue background."
#>  [3] "It's easy to tell the depth of a well."       
#>  [4] "These days a chic'K'en leg is a rare dish."   
#>  [5] "Rice is often served in round bowls."         
#>  [6] "The juice of lemons ma'K'es fine punch."      
#>  [7] "The box was thrown beside the par'K'ed truck."
#>  [8] "The hogs were fed chopped corn and garbage."  
#>  [9] "Four hours of steady wor'K' faced us."        
#> [10] "Large size in stoc'K'ings is hard to sell."
str_replace(regex_sentences, '\\.', '\\?') # 최초로 매칭되는 마침표를 물음표로 변환 
#>  [1] "The birch canoe slid on the smooth planks?" 
#>  [2] "Glue the sheet to the dark blue background?"
#>  [3] "It's easy to tell the depth of a well?"     
#>  [4] "These days a chicken leg is a rare dish?"   
#>  [5] "Rice is often served in round bowls?"       
#>  [6] "The juice of lemons makes fine punch?"      
#>  [7] "The box was thrown beside the parked truck?"
#>  [8] "The hogs were fed chopped corn and garbage?"
#>  [9] "Four hours of steady work faced us?"        
#> [10] "Large size in stockings is hard to sell?"

 마찬가지로 str_replace_all()함수를 통해 조건에 맞는 모든 값을 바꿀 수 있습니다.

str_replace(regex_sentences, '[:alpha:]{1,}', 'word') # 최초로 매칭되는 알파뱃이 한개 이상인 경우를 'word'로 변환 
#>  [1] "word birch canoe slid on the smooth planks." 
#>  [2] "word the sheet to the dark blue background." 
#>  [3] "word's easy to tell the depth of a well."    
#>  [4] "word days a chicken leg is a rare dish."     
#>  [5] "word is often served in round bowls."        
#>  [6] "word juice of lemons makes fine punch."      
#>  [7] "word box was thrown beside the parked truck."
#>  [8] "word hogs were fed chopped corn and garbage."
#>  [9] "word hours of steady work faced us."         
#> [10] "word size in stockings is hard to sell."
str_replace_all(regex_sentences, '[:alpha:]{1,}', 'word') # 알파뱃이 한개 이상인 모든 값을 'word'라는 단어로 변환 
#>  [1] "word word word word word word word word."          
#>  [2] "word word word word word word word word."          
#>  [3] "word'word word word word word word word word word."
#>  [4] "word word word word word word word word word."     
#>  [5] "word word word word word word word."               
#>  [6] "word word word word word word word."               
#>  [7] "word word word word word word word word."          
#>  [8] "word word word word word word word word."          
#>  [9] "word word word word word word word."               
#> [10] "word word word word word word word word."
str_replace(regex_sentences, '[:space:]', '') # 최초로 매칭되는 띄어쓰기를 공백으로 변환 
#>  [1] "Thebirch canoe slid on the smooth planks." 
#>  [2] "Gluethe sheet to the dark blue background."
#>  [3] "It'seasy to tell the depth of a well."     
#>  [4] "Thesedays a chicken leg is a rare dish."   
#>  [5] "Riceis often served in round bowls."       
#>  [6] "Thejuice of lemons makes fine punch."      
#>  [7] "Thebox was thrown beside the parked truck."
#>  [8] "Thehogs were fed chopped corn and garbage."
#>  [9] "Fourhours of steady work faced us."        
#> [10] "Largesize in stockings is hard to sell."
str_replace_all(regex_sentences, '[:space:]', '') # 매칭되는 모든 띄어쓰기를 공백으로 변환 
#>  [1] "Thebirchcanoeslidonthesmoothplanks." 
#>  [2] "Gluethesheettothedarkbluebackground."
#>  [3] "It'seasytotellthedepthofawell."      
#>  [4] "Thesedaysachickenlegisararedish."    
#>  [5] "Riceisoftenservedinroundbowls."      
#>  [6] "Thejuiceoflemonsmakesfinepunch."     
#>  [7] "Theboxwasthrownbesidetheparkedtruck."
#>  [8] "Thehogswerefedchoppedcornandgarbage."
#>  [9] "Fourhoursofsteadyworkfacedus."       
#> [10] "Largesizeinstockingsishardtosell."

나누기

  •  str_split()는 특정 문자 혹은 패턴(정규표현식)을 기준으로 값을 나누는 함수입니다. 이때 기준이된 문자 혹은 패턴은 제거됩니다.
str_split(regex_sentences, '[:upper:]') # 대문자를 기준으로 분절 
#> [[1]]
#> [1] ""                                         
#> [2] "he birch canoe slid on the smooth planks."
#> 
#> [[2]]
#> [1] ""                                          
#> [2] "lue the sheet to the dark blue background."
#> 
#> [[3]]
#> [1] ""                                     
#> [2] "t's easy to tell the depth of a well."
#> 
#> [[4]]
#> [1] ""                                       
#> [2] "hese days a chicken leg is a rare dish."
#> 
#> [[5]]
#> [1] ""                                    "ice is often served in round bowls."
#> 
#> [[6]]
#> [1] ""                                    
#> [2] "he juice of lemons makes fine punch."
#> 
#> [[7]]
#> [1] ""                                          
#> [2] "he box was thrown beside the parked truck."
#> 
#> [[8]]
#> [1] ""                                          
#> [2] "he hogs were fed chopped corn and garbage."
#> 
#> [[9]]
#> [1] ""                                   "our hours of steady work faced us."
#> 
#> [[10]]
#> [1] ""                                       
#> [2] "arge size in stockings is hard to sell."
str_split(regex_sentences, '\\b') # 문자의 바운더리를 기준으로 분절 
#> [[1]]
#>  [1] ""       "The"    " "      "birch"  " "      "canoe"  " "      "slid"  
#>  [9] " "      "on"     " "      "the"    " "      "smooth" " "      "planks"
#> [17] "."     
#> 
#> [[2]]
#>  [1] ""           "Glue"       " "          "the"        " "         
#>  [6] "sheet"      " "          "to"         " "          "the"       
#> [11] " "          "dark"       " "          "blue"       " "         
#> [16] "background" "."         
#> 
#> [[3]]
#>  [1] ""      "It"    "'"     "s"     " "     "easy"  " "     "to"    " "    
#> [10] "tell"  " "     "the"   " "     "depth" " "     "of"    " "     "a"    
#> [19] " "     "well"  "."    
#> 
#> [[4]]
#>  [1] ""        "These"   " "       "days"    " "       "a"       " "      
#>  [8] "chicken" " "       "leg"     " "       "is"      " "       "a"      
#> [15] " "       "rare"    " "       "dish"    "."      
#> 
#> [[5]]
#>  [1] ""       "Rice"   " "      "is"     " "      "often"  " "      "served"
#>  [9] " "      "in"     " "      "round"  " "      "bowls"  "."     
#> 
#> [[6]]
#>  [1] ""       "The"    " "      "juice"  " "      "of"     " "      "lemons"
#>  [9] " "      "makes"  " "      "fine"   " "      "punch"  "."     
#> 
#> [[7]]
#>  [1] ""       "The"    " "      "box"    " "      "was"    " "      "thrown"
#>  [9] " "      "beside" " "      "the"    " "      "parked" " "      "truck" 
#> [17] "."     
#> 
#> [[8]]
#>  [1] ""        "The"     " "       "hogs"    " "       "were"    " "      
#>  [8] "fed"     " "       "chopped" " "       "corn"    " "       "and"    
#> [15] " "       "garbage" "."      
#> 
#> [[9]]
#>  [1] ""       "Four"   " "      "hours"  " "      "of"     " "      "steady"
#>  [9] " "      "work"   " "      "faced"  " "      "us"     "."     
#> 
#> [[10]]
#>  [1] ""          "Large"     " "         "size"      " "         "in"       
#>  [7] " "         "stockings" " "         "is"        " "         "hard"     
#> [13] " "         "to"        " "         "sell"      "."
str_split(regex_sentences, '\\s') # 띄어쓰기를 기준으로 분절 
#> [[1]]
#> [1] "The"     "birch"   "canoe"   "slid"    "on"      "the"     "smooth" 
#> [8] "planks."
#> 
#> [[2]]
#> [1] "Glue"        "the"         "sheet"       "to"          "the"        
#> [6] "dark"        "blue"        "background."
#> 
#> [[3]]
#> [1] "It's"  "easy"  "to"    "tell"  "the"   "depth" "of"    "a"     "well."
#> 
#> [[4]]
#> [1] "These"   "days"    "a"       "chicken" "leg"     "is"      "a"      
#> [8] "rare"    "dish."  
#> 
#> [[5]]
#> [1] "Rice"   "is"     "often"  "served" "in"     "round"  "bowls."
#> 
#> [[6]]
#> [1] "The"    "juice"  "of"     "lemons" "makes"  "fine"   "punch."
#> 
#> [[7]]
#> [1] "The"    "box"    "was"    "thrown" "beside" "the"    "parked" "truck."
#> 
#> [[8]]
#> [1] "The"      "hogs"     "were"     "fed"      "chopped"  "corn"     "and"     
#> [8] "garbage."
#> 
#> [[9]]
#> [1] "Four"   "hours"  "of"     "steady" "work"   "faced"  "us."   
#> 
#> [[10]]
#> [1] "Large"     "size"      "in"        "stockings" "is"        "hard"     
#> [7] "to"        "sell."

참고자료

JDW
JDW
Data Analyst
UNIST M.S. Student

Professionalism

관련문서