정규 표현식 연습

지금까지 메타 문자를 이스케이프 (탈출)하여 리터럴 문자로서 매칭하는 방법에 대해 학습하였습니다. 이제부터는 문자 집합을 정의하는데에 사용되는 메타 문자인 대괄호 []에 대해 알아보도록 하겠습니다.

가령, 우리가 “t”로 시작되어 “e”로 끝나는 세글자 단어 모두를 찾아내서 지워주고 싶다고 가정해 봅시다. 이 경우 우리는 중간에 올 수 있는 모든 알파벳 경우를 가정해 가능한 단어 하나 하나씩 원하는 문자열에 매칭하는 방식으로 단어를 삭제할 수 있겠죠. 하지만, 그러나 이러한 방식으로 우리가 원하는 모든 문자열을 매칭하는 것은 매우 소모적이고 불필요한 일일 것입니다. 이 경우에 우리는 문자 하나 하나를 모두 정해줄 필요없이 원하는 문자의 범위를 지정해주는 방법을 택할 수 있습니다. 이러한 정규 표현식을 우리는 문자 집합이라 합니다.

문자 집합

문자 집합은 집합 안에 있는 속한 문자 중 어떤 것이라도 그 중 하나와 일치하는 문자를 매칭합니다. 가령, "[abc]"라는 문자 집합은 문자 “a”, “b”, “c” 중 어떤 것과도 일치하는 문자를 찾아내죠. 따라서 "t[abc]e라는 정규 표현은 “tae”, “tbe”, “tce” 등과 같은 문자열과 모두 매칭됩니다. 여기서 대괄호 []는 문자 집합을 정의하는 표현입니다.

한가지 유의할 점은, 문자 집합 내에서의 문자 순서는 중요하지 않다는 점입니다. 중요한 것은 대괄호 안에 있는 문자의 종류입니다. 따라서 문자 집합 "[abc]""[cba]"와 같은 결과를 도출합니다.

문자 집합의 적용

"f[aeiou]n"라는 알파벳 모음 문자 집합의 정규 표현 패턴으로 “fan”, “fin”, “fun”라는 단어 등이 속해있는 문자열 벡터로 부터 해당 단어를 매칭하여 추출해 봅시다.

library(stringr)
fns <- c("fan","fen","fin","fon","fun")
unlist(str_extract_all(fns, "f[aeiou]n"))
## [1] "fan" "fen" "fin" "fon" "fun"

“f[aeiou]n”으로 구성된 정규 표현은 fns의 모든 요소와 일치합니다. 이제 같은 문자 집합 패턴을 다른 벡터 fnx에 적용해 봅시다.

fnx <- c("fan","fin","fun","f0n","f.n","f1n","fain")
unlist(str_extract_all(fnx, "f[aeiou]n"))
## [1] "fan" "fin" "fun"

보다시피 fnx 안에 모음 문자가 있는 처음 세 요소만 일치합니다. 그리고 마지막 요소 “fain”은 일치하지 않습니다. 문자 집합은 하나의 문자만을 매칭하기 때문에 “a” 또는 “i” 에만 일치하고 “ai”는 매칭하지 않습니다.

문자 범위

위에서 설명한 문자 집합은 찾고자 하는 문자 모두를 지정하는 방식을 사용합니다. 그런데 만약 우리가 가능한 모든 문자열과 일치하지만, 구체적으로 어떠한 문자가 올 지 모르는 상황에서 문자열을 매칭하려면 어떻게 해야 할까요? 위에서 우리는 “t” 로 시작하고 “e”로 끝나는 세글자 단어를 모두 매칭하는 경우를 생각해봤습니다. 이 경우 우리는 어떻게 해야 할까요? 가능한 문자를 일일히 지정해주지 않더라도, 특정 문자들의 전체 범위를 설정하는 방법을 이용하면 효율적인 문자열 처리가 가능하겠죠.

이를 위해 메타 문자 하이픈을(hyphen) "-" 사용해서 문자 범위를 구성하는 방법을 소개해 드리겠습니다. 이 표현 방식은 매칭하고자 하는 문자들의 범위를 하이픈 "-" 기호로 지정하는 것입니다.

이러한 방식으로 다음과 같이 대문자, 소문자, 그리고 숫자 문자 집합을 정의할 수 있습니다.

uppercase <- "[A-Z]"

lowercase <- "[a-z]"

number <- "[0-9]"

*하이픈 기호는 문자 집합 안에 있을 때만 메타 문자입니다. 문자 집합 이외에서 쓰이는 하이픈은 리터럴 문자입니다.

이렇게 문자 범위로 구성된 정규 표현은 매우 유용합니다. 다음의 예를 함께 보실까요.

3개의 문자로 구성된 문자열을 가진 다음의 벡터 객체, triplets,를 상대로 문자 범위를 사용해 구성한 정규표현으로 매칭한 결과를 살펴봅시다.

triplets <- c("bts","the","BTS","The","010","070",":~)","^^;")
unlist(str_extract_all(triplets, "[a-z][a-z][a-z]")) # 3개의 연속된 소문자 
## [1] "bts" "the"
unlist(str_extract_all(triplets, "[A-Z]{3}")) # 3개의 연속된 대문자 
## [1] "BTS"
unlist(str_extract_all(triplets, "[A-Z][a-z]+")) # 대문자로 시작되고 그 다음 소문자 1회 이상 
## [1] "The"
unlist(str_extract_all(triplets, "[0-9]+$")) # 연속된 숫자 1회 이상이고 숫자로 종결
## [1] "010" "070"

이렇게 문자 범위로 구성된 문자 집합을 이용하면 편리하게 원하는 문자열을 매칭할 수 있습니다. 하지만, 위의 문자 범위로는 구두점으로 구성된 웃음 표시 ":~)""^^;"를 매칭할 수는 없었네요.

부정 문자 집합

정규 표현식으로 텍스트를 처리할 때, 문자 집합에서 벗어나는 즉, 문자 집합의 일부가 아닌 문자를 찾아내야 하는 상황은 빈번하게 발생합니다.

위키피디아 페이지의 전처리 과정을 예로 들어 볼까요. 이 때 우리는 알파벳 이외의 문자 즉, 한글 또는 한자로 표시된 문자를 매칭해서 지워줘야 할 필요가 있었습니다. 이 경우, 부정 문자 집합을 사용한다면, 우리가 원하는 문자 집합에 없는 문자를 찾아낼 수 있습니다. 이 부정 집합은 위해 메타 문자 캐럿 "^"으로 구성할 수 있습니다.

캐럿 "^"은 정규 표현식의 패턴에 하나 이상의 의미를 가지는 메타 문자 중 하나입니다. 그 중, 문자 집합 안의 첫번째 위치에 캐럿을 사용하면 : 예 : "[^a-z]", 다음의 문자 이외의 문자 즉, 부정 기능을 수행합니다. 따라서 알파벳 소문자를 제외한 모든 문자를 매칭하는 데 사용할 수 있죠.

이렇게 부정 문자 범위를 사용하면 우리는 다음과 같은 "[^a-zA-Z0-9]{3}" 패턴을 정의함으로써 문자와 숫자가 아닌 기호로 표시된 문자열인 웃음 표시 ":~)", "^^;"와 매칭시킬 수 있습니다.

unlist(str_extract_all(triplets, "[^a-zA-Z0-9]{1,}")) # 문자 및 숫자 이외의 문자가 1회 이상 매칭되는 모든 요소
## [1] ":~)" "^^;"

캐럿은 문자 집합 안에 처음으로 오는 경우에만 부정을 의미하며, 그렇지 않으면 부정 문자 집합이 아닙니다. 가령,

unlist(str_extract_all(triplets, "[a-zA-Z0-9^]+")) # 문자/숫자/캐럿 1회 이상 연결되는 문자열 매칭
## [1] "bts" "the" "BTS" "The" "010" "070" "^^"

이 경우, 정규 표현 패턴인 "[a-zA-Z0-9^]"은 부정 집합 "[^a-zA-Z0-9]"과 완전히 다른 “문자 또는 숫자 또는 리터럴 캐럿 문자”를 의미합니다.

문자 집합 안의 메타 문자

지금까지 문자 집합이 무엇인지, 그리고 문자 범위를 정의하는 방법과 부정 문자 집합을 지정하는 방법에 대해 알아보았습니다. 그렇다면, 문자 집합 안에 캐럿 이외에 다른 메타 문자를 포함시키면 어떻게 될까요?

문자 집합을 사용할 경우 첫 번째 위치에 있는 캐럿, 그리고 문자 범위를 지정하는 하이픈, 그리고 문자 집합 기호인 대괄호, 그리고 백슬래시를 제외하고는, 문자 집합 내의 메타 문자는 리터럴 문자로 이용됩니다! 즉, 문자 집합 안에서는 이중 백 슬래시를 사용하여 메타문자를 이스케이프 처리할 필요가 없습니다.

예를 들어, fnx 문자열 벡터를 다시 볼까요. 이 벡터의 문자열 요소들은 “f” 와 “n” 사이에 다양한 문자로 구성되어 있고, 여기에는 리터럴 문자로서의 마침표도 포함되어 있죠. 이 때 우리가 “f.n”이라는 문자열을 매칭하기 위해서 구성할 수 있는 정규 표현은 메타문자를 이스케이프하거나 문자집합을 사용하는 것입니다.

unlist(str_extract_all(fnx, "f\\.n")) 
## [1] "f.n"
unlist(str_extract_all(fnx, "f[.]n")) 
## [1] "f.n"

문자 클래스

문자 범위 이외에도 문자 클래스는 특정 문자 집단을 매칭하는데 유용한 정규 표현 구조를 제공합니다. 가령, 전체 알파벳 또는 숫자 클래스로 어떠한 알파벳 또는 숫자와도 매칭시킬 수 있죠. 이러한 문자 클래스는 이중 백슬래시와 이니셜 문자로 구성됩니다.

그리고 대부분의 정규 표현식 엔진에서 사용되는 가장 일반적인 문자 클래스는 다음과 같습니다.

문자 일치 같은 표현
\\d 임의의 숫자 [0-9]
\\D 임의의 숫자 이외의 문자 [^0-9]
\\w 밑줄 문자 “_“를 포함하여 영어 단어의 일부로 간주되는 문자 [a-zA-Z0-9_]
\\W 영어 단어의 일부로 간주되지 않는 문자 [^a-zA-Z0-9_]
\\s 공백 문자 [\f\n\r\t\v]
\\S 비공백 문자 [^\f\n\r\t\v]

문자 클래스는 문자 집합을 대체하여 편리하게 정규표현식을 구성할 수 있게 해줍니다.

unlist(str_extract_all(triplets, "\\d{3}")) # 3개의 임의의 숫자 연결된 패턴
## [1] "010" "070"
unlist(str_extract_all(triplets, "\\D+")) # 1개 이상 연속된 비 숫자 패턴
## [1] "bts" "the" "BTS" "The" ":~)" "^^;"
unlist(str_extract_all(triplets, "\\w+")) # 1개 이상 연속된 문자/숫자
## [1] "bts" "the" "BTS" "The" "010" "070"
unlist(str_extract_all(triplets, "\\W+")) # 1개 이상 연속된 문자/숫자
## [1] ":~)" "^^;"
unlist(str_extract_all(triplets, "\\s+")) # 1개 이상 연속된 공백
## character(0)
unlist(str_extract_all(triplets, "\\S+")) # 1개 이상 연속된 비공백 문자
## [1] "bts" "the" "BTS" "The" "010" "070" ":~)" "^^;"

텍스트 사전처리를 하다 보면 텍스트 공백이 다양한 방식의 문자 표현으로 구성되어 있다는 것을 발견하게 될 것입니다. 다음은 공백을 나타내는 문자를 보여주는 표입니다.

문자 정의
\f 페이지 넘김 (다음 페이지 또는 섹션을 구분하여 하단으로 넘어가는 기호)
\n 줄바꿈 for Mac OS (Windows: \r\n)
\r 캐리지 리턴 (줄의 처음으로 돌아가는 기호)
\t
\v 수직 탭

또 가끔은 공백 문자가 텍스트에 제대로 표시되어 있지 않는 경우도 있습니다. (\t, \n, \r\n): 이러한 공백문자는 그 기능이 각기 다르고, 단순 공백 문자로는 매칭되지 않습니다. 그렇기 때문에 모든 공백 문자 유형을 찾아내기 위해서는 공백 문자 클래스 \\s를 사용해야합니다.

POSIX (Portable Operating System Interface) 문자 클래스

정규 표현식을 구성 방법을 마무리하기 위해 POSIX로 알려진 또 다른 유형의 문자 클래스를 소개합니다. POSIX는 위의 문자 클래스와 같은 기능을 하지만 다른 방식으로 표현되고, 보다 다양한 문자 집합을 구성할 수 있게 해줍니다.

다음은 R에서 정규식 엔진이 지원하는 POSIX 문자 클래스 구조입니다.

문자 일치 같은 구조
[[:alnum:]] 영숫자 [a-zA-Z0-9]
[[:alpha:]] 알파벳 문자 [a-zA-Z]
[[:digit:]] 숫자 [0-9]
[[:lower:]] 소문자 [a-z]
[[:upper:]] 대문자 [A-Z]
[[:word:]] 단어 (문자, 숫자, 언더스코어) [a-zA-Z0-9_]
[[:blank:]] 공백과 탭 [ \t]
[[:space:]] 모든 공백 문자(줄 바꿈 포함) [ \f\n\r\t\v]
[[:punct:]] 구두점과 기호
[[:graph:]] 공백을 제외한 모든 인쇄 가능한 문자 [:alnum:][:punct:]
[[:print:]] 인쇄 가능한 모든 문자 [:alnum:][:punct:][:space:]
[[:ascii:]] 모든 ASCII 문자 (알파벳)

POSIX 문자 클래스는 여는 대괄호 [, 뒤에 콜론 :, 키워드 뒤에 이어 콜론 :, 닫는 대괄호 ]로 구성됩니다.

R에서 POSIX 문자 클래스를 사용하려면 문자 집합 안에 POSIX 클래스를 위치시켜야 합니다. 즉, POSIX 클래스는 이중 대괄호로 묶이는 형식이죠. POSIX 클래스를 사용하여 triplets라는 문자 벡터의 요소를 매치시켜 봅시다.

triplets
## [1] "bts" "the" "BTS" "The" "010" "070" ":~)" "^^;"
unlist(str_extract_all(triplets, "[[:lower:]]+")) # 1개 이상의 연속된 문자
## [1] "bts" "the" "he"
unlist(str_extract_all(triplets, "[[:alpha:]]+"))
## [1] "bts" "the" "BTS" "The"
unlist(str_extract_all(triplets, "[[:digit:]]{1,3}")) # 1개 이상 3개 이하로 연속된 숫자
## [1] "010" "070"
unlist(str_extract_all(triplets, "[[:punct:]~^]+")) # [:punct:] 리터럴 문자 캐럿 "^" "~"을 매칭하지 않습니다.
## [1] ":~)" "^^;"
unlist(str_extract_all(triplets, "[[:lower:][:punct:]]")) # 모든 단일 소문자/구두점
##  [1] "b" "t" "s" "t" "h" "e" "h" "e" ":" ")" ";"

위키피디아 텍스트의 어휘 빈도수 재분석

자 지금까지 배운 정규표현식으로 지난 시간에 수행했던 위키피디아 페이지의 텍스트를 다시 한번 처리해 보도록 하겠습니다.

library(stringr)
load("D:/Dropbox/2018_Class_Teaching/KMOOC/bts_text.RData")
bts_string <- str_c(bts_text, collapse = " ") # 문자 벡터 bts_text를 단일 문자열로 축소

이제 BTS에 관한 위키피디아 페이지의 텍스트가 하나로 연결된 단일 문자열을 가지게 되었습니다.

문자열을 전처리해봅시다.

첫 번째로, References 섹션의 모든 것을 제거합시다.

str_locate_all(bts_string, "References") # 문자열에서 "References" 패턴의 위치를 찾습니다.
## [[1]]
##      start   end
## [1,]  6937  6946
## [2,] 31829 31838
bts_trunc <- str_trunc(bts_string, 31828, "right")
str_trunc(bts_trunc, 1000)
## [1] "BTS (band)\nBTS (Hangul:                  ; RR: Bangtan Sonyeondan), also known as\n                                                                                                   BTS\nthe Bangtan Boys, is a seven-member South Korean boy band formed\nby Big Hit Entertainment. They debuted on June 12, 2013 with the\nsong \"No More Dream\" from their first album 2 Cool 4 Skool. They\nwon several New Artist of the Year awards for the track, including at\nthe 2013 Melon Music Awards and Golden Disc Awards and the 2014\nSeoul Music Awards. The band continued to rise to widespread\nprominence with their subsequent albums Dark & Wild (2014), The\nMost Beautiful Moment in Life, Part 2 (2015) and The Most Beautiful\nMoment in Life: Young Forever (2016), with the latter two entering the\n                                                                               BTS at the 32nd Golden Disk Awards on\nU.S. Billboard 200.[4] The Most Beautiful Moment in Life: Young\n                                      ..."

이제 문자 “bts_string”에 "References"라는 정규 표현식이 어디에 나타나는지 알아보고 정규 표현식 패턴의 위치 이후의 모든 것을 제거하여 잘라냅니다.

다음으로는 공백(/n 또는 /r/n 또는 여러 공백)을 처리해야합니다.

줄 바꿈을 포함하여 모든 공백 문자를 나타내는 POSIX 문자 클래스를 기억하세요. : [[:space:]]

bts_nospace <- str_replace_all(bts_trunc, "[[:space:]]{1,}", " ") # 하나 이상의 공백을 하나의 공백 문자로 바꾸십시오.
# {1,}이 정규 표현식에서 어떤 역할을 하는지 생각해봅시다.

이전보다 나아보일 것입니다. 문자열 객체로 이제 무엇을 해야할까요? 문자열에 영어 이외의 문자가 있는지 확인해 봅시다. POSIX 문자 클래스 [:ascii:]를 사용하여 모든 비알파벳 문자를 제거해주어야 합니다. 자 그리고, 남은 모든 알파벳 문자를 표준화(소문자 또는 대문자로 표준화) 처리해야 합니다. 따라서 문자열의 문자를 소문자로 변환하는데 ‘tolower’ 함수를 사용해야합니다.

str_extract_all(bts_nospace, "[^[:ascii:]]{1,}") # 모든 영어 이외의 문자 추출(선행 문자 집합과 적어도 1번 이상 일치)
## [[1]]
##  [1] "·"                     "·"                    
##  [3] "·"                     "·"                    
##  [5] "·"                     "·"                    
##  [7] "<U+2013>"               "·"                    
##  [9] "·"                     "·"                    
## [11] "彈少年團"               "<U+2013>"              
## [13] "<U+01D2>"               "<U+5F3E>少年<U+56E3>"  
## [15] "ぼうだんしょうねんだん" "<U+014D>"              
## [17] "<U+014D>"               "o"                     
## [19] "o"                      "<U+2013>"              
## [21] "<U+2013>"               "<U+2013>"              
## [23] "彈少年團"               "’"                    
## [25] "<U+014D>"               "<U+014D>"              
## [27] "<U+5F3E>少年<U+56E3>"   "<U+2013>"              
## [29] "<U+2013>"               "<U+2013>"              
## [31] "<U+2013>"               "<U+2013>"              
## [33] "”"                     "’"                    
## [35] "“"                     "”"                    
## [37] "’"                     "“"                    
## [39] "”"                     "<U+2013>"              
## [41] "“"                     "’"                    
## [43] "”"                     "\\"                    
## [45] "\\"                     "\\"                    
## [47] "\\"                     "\\"                    
## [49] "<U+2013>"               "<U+2013>"              
## [51] "<U+2013>"               "<U+2013>"              
## [53] "<U+2013>"               "<U+2013>"              
## [55] "<U+2013>"               "<U+2013>"              
## [57] "<U+2013>"               "<U+2013>"              
## [59] "<U+2013>"               "<U+2013>"              
## [61] "<U+2013>"               "<U+2013>"              
## [63] "<U+2013>"               "<U+2013>"              
## [65] "<U+2013>"               "<U+2013>"              
## [67] "<U+2013>"               "<U+2013>"              
## [69] "<U+2013>"               "<U+2013>"              
## [71] "<U+2013>"               "<U+2013>"              
## [73] "<U+2013>"
bts_eng <- str_replace_all(bts_nospace, "[^[:ascii:]]", "") # 영어 이외의 문자를 "" 문자로 바꾸세요.(즉, 제거하세요.)
bts_eng_lower <- tolower(bts_eng) # Translate all characters into lower-case letters
# 오류 메세지가 뜨면 str_to_lower 문자열 함수를 사용하세요. 

bts_tidy <- str_to_lower(bts_tidy)

이제 구두점과 숫자를 처리하는 방법에 대해 생각해봅시다.

# 제거할 구두점을 확인하세요.
unlist(str_extract_all(bts_eng_lower, "[[:punct:]]+"))[1:100] # '+'가 정규식에서 어떤 역할을 하는지 생각해봅시다.
##   [1] "("    ")"    "("    ":"    ";"    ":"    "),"   ","    "-"    "."   
##  [11] ","    "\""   "\""   "."    ","    "."    "&"    "("    "),"   ","   
##  [21] "("    ")"    ":"    "("    "),"   "."    "."    ".["   "]"    ":"   
##  [31] ","    ":"    ","    ","    ","    ","    ","    ".["   "]"    "-"   
##  [41] ","    "("    "),["  "]"    ","    "-"    ".["   "]"    ","    ".["  
##  [51] "]"    "."    ","    "'"    ","    "\""   "\",[" "]"    ".["   "]"   
##  [61] "'"    "-"    "["    "]"    "&"    ","    ":"    "("    "),["  "]"   
##  [71] ",["   "]"    "."    "["    "]"    "'"    ","    "\""   "\","  "["   
##  [81] "]"    ".["   "]"    "."    "."    ","    "\""   "\","  "."    ","   
##  [91] ".["   "]"    "-"    "."    "'"    ","    "'"    ","    "."    "."

텍스트에서 모든 구두점이 추출된 것 같습니다.

그러나 단어를 형성하는데 포함되어 있는 구두점은 어떻게 해야할까요? 무엇일까요? 예를 들면…

# 제거할 구두점을 확인하세요. 
unlist(str_extract_all(bts_eng_lower, "[[:graph:]]*[[:punct:]]{1,}[[:graph:]]+"))[1:100] # "*" 는 선행 패턴과 0번 이상 일치합니다.
##   [1] "(band)"               "(hangul:"             "sonyeondan),"        
##   [4] "seven-member"         "\"no"                 "(2014),"             
##   [7] "(2015)"               "(2016),"              "u.s."                
##  [10] "200.[4]"              "awards.[5]"           "j-hope"              
##  [13] "(2016),[6]"           "k-pop"                "ever.[7]"            
##  [16] "time.[8]"             "1.5"                  "\"million"           
##  [19] "seller\",[9]"         "awards.[10]"          "group's"             
##  [22] "k-pop"                "hop[1]"               "r&b"                 
##  [25] "(2017),[11]"          "200,[12]"             "japan[2]"            
##  [28] "album's"              "\"dna\","             "columbia[3]"         
##  [31] "67.[13]"              "bts.ibighit.com"      "\"mic"               
##  [34] "drop\","              "ever.[14]"            "j-hope"              
##  [37] "1.2"                  "korea's"              "chart's"             
##  [40] "g.o.d's"              "2001.[15]"            "2017.[16]"           
##  [43] "(2018),"              "200,[17]"             "k-pop"               
##  [46] "worldwide.[18][19]"   "2016.[20]"            "k-pop"               
##  [49] "pangt'an"             "may.[21]"             "chart.[22]"          
##  [52] "chart.[23]"           "youtube's"            "chart.[24]"          
##  [55] "abbma.[25][26]"       "kunrei-shiki"         "internet.[27]"       
##  [58] "\"having"             "world's"              "group\".[28]"        
##  [61] "\"liked"              "(502"                 "million)\""          
##  [64] "u.s."                 "combined.[29]"        "[30]"                
##  [67] "korea's"              "group's"              "(hangul:"            
##  [70] "),"                   "\"bulletproof"        "scouts\"."           
##  [73] "adolescents.[31][32]" "),"                   "similarly.[33]"      
##  [76] "\"beyond"             "identity.[34]"        "\"growing"           
##  [79] "forward.\"[35]"       "j-hope,"              "soundcloud.[36][37]" 
##  [82] "group's"              "\"school"             "\"no"                
##  [85] "2013.[38][39]"        "105,000"              "copies,[40][41]"     
##  [88] "\"no"                 "\"we"                 "hits.[42][43]"       
##  [91] "\"no"                 "re-recorded"          "2014.[44]"           
##  [94] "\"school"             "o!rul8,2?,"           "120,000"             
##  [97] "four.[45][46]"        "\"n.o\""              "\"attack"            
## [100] "(korean:"

위의 단어 목록을 참고하여 보면, 구두점을 삭제할 때 주의해야 할 점을 알 수 있습니다. 첫째, “’”만 제거하는 것보다는 “’s”를 같이 제거해주는 것이 좋습니다. 둘째, “u.s”, “r&b”는 “usa”나 “rnb”로 바꾸어 주는 것이 좋습니다. 마지막으로, "o!rul8,2?"는 그 자체로는 의미를 가지지만 편의상 여기서는 구두점과 숫자의 기능을 무시합니다.

unlist(str_extract_all(bts_eng_lower, "[']s ")) # 패턴을 바꾸기 전에 원하는 패턴과 일치하는지 항상 먼저 확인하세요. 
##  [1] "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s "
## [12] "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s "
## [23] "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s "
## [34] "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s " "'s "
bts_noapos <- str_replace_all(bts_eng_lower, "[']s ", " ") # 패턴을 하나의 공백 문자로 바꾸세요. 
str_extract_all(bts_noapos, " u\\.s\\. | r\\&b ")
## [[1]]
## [1] " u.s. " " r&b "  " u.s. " " r&b "
bts_usa <- str_replace_all(bts_noapos,  " u\\.s\\. ", " usa ")
bts_rnb <- str_replace_all(bts_usa,  " r\\&b ", " rnb ")
str_extract_all(bts_rnb, " usa | rnb ")
## [[1]]
## [1] " usa " " rnb " " usa " " rnb "

위키피디아 페이지에는 [digits] 또는 (digits) 형태의 인용 표시가 많습니다.

str_trunc(bts_rnb, 1000)
## [1] "bts (band) bts (hangul: ; rr: bangtan sonyeondan), also known as bts the bangtan boys, is a seven-member south korean boy band formed by big hit entertainment. they debuted on june 12, 2013 with the song \"no more dream\" from their first album 2 cool 4 skool. they won several new artist of the year awards for the track, including at the 2013 melon music awards and golden disc awards and the 2014 seoul music awards. the band continued to rise to widespread prominence with their subsequent albums dark & wild (2014), the most beautiful moment in life, part 2 (2015) and the most beautiful moment in life: young forever (2016), with the latter two entering the bts at the 32nd golden disk awards on usa billboard 200.[4] the most beautiful moment in life: young january 10, 2018 forever went on to win the album of the year award at the 2016 from left to right: v, suga, jin, jungkook, rm, melon music awards.[5] jimin and j-hope their second full album, wings (2016),[6] peaked at number 26 on t..."

이러한 인용 표시를 제거해보겠습니다.

str_extract_all(bts_rnb, "\\[\\d+\\]|\\(\\d+\\)")
## [[1]]
##   [1] "(2014)" "(2015)" "(2016)" "[4]"    "[5]"    "(2016)" "[6]"   
##   [8] "[7]"    "[8]"    "[9]"    "[10]"   "[1]"    "(2017)" "[11]"  
##  [15] "[12]"   "[2]"    "[3]"    "[13]"   "[14]"   "[15]"   "[16]"  
##  [22] "(2018)" "[17]"   "[18]"   "[19]"   "[20]"   "[21]"   "[22]"  
##  [29] "[23]"   "[24]"   "[25]"   "[26]"   "[27]"   "[28]"   "[29]"  
##  [36] "[30]"   "[31]"   "[32]"   "[33]"   "[34]"   "[35]"   "[36]"  
##  [43] "[37]"   "[38]"   "[39]"   "[40]"   "[41]"   "[42]"   "[43]"  
##  [50] "[44]"   "[45]"   "[46]"   "[47]"   "[48]"   "[49]"   "(2014)"
##  [57] "[50]"   "[51]"   "[52]"   "[53]"   "[54]"   "[55]"   "[56]"  
##  [64] "[57]"   "[58]"   "[59]"   "(2014)" "[60]"   "[61]"   "[62]"  
##  [71] "[63]"   "[64]"   "[65]"   "(2015)" "[66]"   "[67]"   "[68]"  
##  [78] "[69]"   "[70]"   "[71]"   "[71]"   "[72]"   "[73]"   "[74]"  
##  [85] "[63]"   "[75]"   "[76]"   "[77]"   "[78]"   "[79]"   "[80]"  
##  [92] "(2016)" "[81]"   "[82]"   "[83]"   "[84]"   "[5]"    "[85]"  
##  [99] "[86]"   "[87]"   "[88]"   "[89]"   "[90]"   "[91]"   "[10]"  
## [106] "[92]"   "[93]"   "[94]"   "[95]"   "[96]"   "[97]"   "[98]"  
## [113] "[99]"   "[100]"  "[101]"  "[102]"  "[103]"  "[104]"  "[105]" 
## [120] "[106]"  "[107]"  "[108]"  "[109]"  "[110]"  "[111]"  "[112]" 
## [127] "[113]"  "[114]"  "[115]"  "[116]"  "[117]"  "[118]"  "[119]" 
## [134] "[120]"  "[121]"  "[122]"  "[123]"  "[124]"  "[125]"  "[14]"  
## [141] "[126]"  "[127]"  "[128]"  "[129]"  "[130]"  "[131]"  "[132]" 
## [148] "[133]"  "[134]"  "[135]"  "[136]"  "[137]"  "[138]"  "[139]" 
## [155] "[140]"  "[141]"  "[142]"  "[143]"  "[144]"  "[145]"  "[146]" 
## [162] "[147]"  "[148]"  "[149]"  "[150]"  "[151]"  "[152]"  "[153]" 
## [169] "(2013)" "(2013)" "(2014)" "[154]"  "[155]"  "(2016)" "[156]" 
## [176] "[154]"  "[157]"  "[158]"  "[156]"  "[159]"  "[160]"  "[161]" 
## [183] "[25]"   "[26]"   "[162]"  "[29]"   "[163]"  "[164]"  "[165]" 
## [190] "[166]"  "[167]"  "[168]"  "[169]"  "[170]"  "[171]"  "[172]" 
## [197] "[173]"  "[174]"  "[175]"  "[176]"  "[177]"  "[178]"  "[179]" 
## [204] "[180]"  "[181]"  "[182]"  "[183]"  "[184]"  "[185]"  "[186]" 
## [211] "[187]"  "[188]"  "[189]"  "[190]"  "[191]"  "[192]"  "[193]" 
## [218] "[194]"  "[195]"  "[196]"  "[197]"  "[198]"  "[199]"  "[200]" 
## [225] "[201]"  "[202]"  "[203]"  "[204]"  "[205]"  "[206]"  "[207]" 
## [232] "[206]"  "[208]"  "[206]"  "[209]"  "[206]"  "[210]"  "[206]" 
## [239] "[211]"  "[206]"  "[212]"  "[206]"  "[213]"  "(2014)" "(2014)"
## [246] "(2016)" "(2016)" "(2018)" "(2018)" "(2014)" "(2015)" "(2015)"
## [253] "(2015)" "(2016)" "(2017)" "(2018)" "[214]"  "[215]"  "[216]"
bts_nocite <- str_replace_all(bts_rnb, "\\[[[:digit:]]+\\]|\\([[:digit:]]+\\)", "")

이제 문자열에서 모든 구두점을 제거할 준비가 되었습니다.

unlist(str_extract_all(bts_nocite, "[[:graph:]]*[[:punct:]]{1,}[[:graph:]]*"))[1:100]
##   [1] "(band)"          "(hangul:"        ";"              
##   [4] "rr:"             "sonyeondan),"    "boys,"          
##   [7] "seven-member"    "entertainment."  "12,"            
##  [10] "\"no"            "dream\""         "skool."         
##  [13] "track,"          "awards."         "&"              
##  [16] ","               "life,"           "life:"          
##  [19] ","               "200."            "life:"          
##  [22] "10,"             "right:"          "v,"             
##  [25] "suga,"           "jin,"            "jungkook,"      
##  [28] "rm,"             "awards."         "j-hope"         
##  [31] "album,"          ","               "200,"           
##  [34] "k-pop"           "ever."           "korea,"         
##  [37] "time."           "1.5"             "copies,"        
##  [40] "bts'"            "seoul,"          "\"million"      
##  [43] "seller\","       "awards."         "k-pop"          
##  [46] "release,"        "yourself:"       ","              
##  [49] "200,"            "history."        "track,"         
##  [52] "\"dna\","        "67."             "bts.ibighit.com"
##  [55] "album,"          "\"mic"           "drop\","        
##  [58] "100."            "america,"        "ever."          
##  [61] "j-hope"          "1.2"             "month,"         
##  [64] "years,"          "g.o.d"           "2001."          
##  [67] "2017."           "yourself:"       ","              
##  [70] "200,"            "k-pop"           "far."           
##  [73] "debut,"          "worldwide."      "presence,"      
##  [76] "2016."           "that,"           "k-pop"          
##  [79] "pangt'an"        "may."            "2016,"          
##  [82] "chart,"          "chart."          "date,"          
##  [85] "chart."          "year,"           "100:"           
##  [88] "chart,"          "chart."          "2017,"          
##  [91] "awards,"         "abbma."          "2017,"          
##  [94] "kunrei-shiki"    "internet."       "20,"            
##  [97] "\"having"        "group\"."        "december,"      
## [100] "2017,"
bts_nopunct <- str_replace_all(bts_nocite, "[[:punct:]^]+", "")
str_trunc(bts_nopunct, 1000)
## [1] "bts band bts hangul  rr bangtan sonyeondan also known as bts the bangtan boys is a sevenmember south korean boy band formed by big hit entertainment they debuted on june 12 2013 with the song no more dream from their first album 2 cool 4 skool they won several new artist of the year awards for the track including at the 2013 melon music awards and golden disc awards and the 2014 seoul music awards the band continued to rise to widespread prominence with their subsequent albums dark  wild  the most beautiful moment in life part 2  and the most beautiful moment in life young forever  with the latter two entering the bts at the 32nd golden disk awards on usa billboard 200 the most beautiful moment in life young january 10 2018 forever went on to win the album of the year award at the 2016 from left to right v suga jin jungkook rm melon music awards jimin and jhope their second full album wings  peaked at number 26 on the background information billboard 200 which marked the highest cha..."

str_extract_all 함수를 사용하여 수치 표현식을 제거하는 방법을 살펴봅시다.

# 제거할 숫자 확인 
# 단어를 형성하기 위해 숫자 뒤에 오는 문자를 제거하지 않겠습니다. (ex> 2nd)
# 따라서 숫자는 공백이나 문자가 뒤따라야 합니다. 
unlist(str_extract_all(bts_nopunct, " [[:digit:]]+[[:space:]]?[[:alpha:]]*"))[1:100] # 정규 표현식에서 "?"는 어떤 역할을 할까요? 
##   [1] " 12 "                 " 2 cool"              " 4 skool"            
##   [4] " 2013 melon"          " 2014 seoul"          " 2 "                 
##   [7] " 32nd"                " 200 the"             " 10 "                
##  [10] " 2016 from"           " 26 on"               " 200 which"          
##  [13] " 15 million"          " 2016 mnet"           " 2013present"        
##  [16] " 200 marking"         " 100 canyon"          " 85 and"             
##  [19] " 67 another"          " 28 on"               " 100 both"           
##  [22] " 12 million"          " 16 jimin"            " 2001 bts"           
##  [25] " 2017 their"          " 200 making"          " 7 million"          
##  [28] " 2016 revised"        " 2016 billboard"      " 50 chart"           
##  [31] " 78 weeks"            " 50 chart"            " 100 "               
##  [34] " 6th"                 " 14th"                " 2017 they"          
##  [37] " 2017 kunreishiki"    " 25 most"             " 20 "                
##  [40] " 2018 edition"        " 2017 being"          " 502 million"        
##  [43] " 2018 a"              " 20102014 debut"      " 20152016 mainstream"
##  [46] " 2017present"         " 2017 bts"            " 20102014 debut"     
##  [49] " 2010 and"            " 2011 by"             " 2012 six"           
##  [52] " 2 cool"              " 4 skool"             " 12 "                
##  [55] " 2014 performing"     " 105000 copies"       " 2 were"             
##  [58] " 4 "                  " 11 "                 " 120000 copies"      
##  [61] " 2013melon"           " 2014 seoul"          " 200000 copies"      
##  [64] " 200000 copies"       " 28000 copies"        " 2014 bts"           
##  [67] " 20152016 mainstream" " 2015 bts"            " 1 bts"              
##  [70] " 1 "                  " 27 best"             " 2015 so"            
##  [73] " 2016 in"             " 2 "                  " 44 on"              
##  [76] " 100 million"         " 1have"               " 300000 copies"      
##  [79] " 42000 copies"        " 2 later"             " 30 "                
##  [82] " 171 on"              " 200 chart"           " 5000 copies"        
##  [85] " 2015 mnet"           " 1 and"               " 2 were"             
##  [88] " 40 hit"              " 10 hit"              " 20 hit"             
##  [91] " 7 "                  " 44000 copies"        " 500000 copies"      
##  [94] " 6 million"           " 24 hours"            " 24 hours"           
##  [97] " 2016 mnet"           " 2017present"         " 2017 bts"           
## [100] " 700000 copies"
bts_nonum <- str_replace_all(bts_nopunct, "[[:digit:]]+", "") # 모든 숫자 제거
str_trunc(bts_nonum, 1000)
## [1] "bts band bts hangul  rr bangtan sonyeondan also known as bts the bangtan boys is a sevenmember south korean boy band formed by big hit entertainment they debuted on june   with the song no more dream from their first album  cool  skool they won several new artist of the year awards for the track including at the  melon music awards and golden disc awards and the  seoul music awards the band continued to rise to widespread prominence with their subsequent albums dark  wild  the most beautiful moment in life part   and the most beautiful moment in life young forever  with the latter two entering the bts at the nd golden disk awards on usa billboard  the most beautiful moment in life young january   forever went on to win the album of the year award at the  from left to right v suga jin jungkook rm melon music awards jimin and jhope their second full album wings  peaked at number  on the background information billboard  which marked the highest chart ranking for a kpop also known as b..."

자 이제, 영어가 아닌 문자, 모든 숫자 및 구두점이 제거하는 방식으로 텍스트를 전처리 했습니다. 그러나 텍스트 사전처리에서 생성된 여러 개의 공백을 여전히 볼 수 있습니다.

bts_nospace <- str_replace_all(bts_nonum, "[[:space:]]{1,}", " ") # 공백 삭제 과정을 반복할 수 있습니다. 

마지막으로 문자열 객체인 bts_tidy를 " "로 구분된 단어로 토큰화할 준비가 되었습니다.

bts_tidy_word <- unlist(str_split(bts_nospace, " "))
bts_tidy_word_freq <- sort(table(bts_tidy_word), decreasing = TRUE) # 각 단어 출현 빈도 수 세서, 내림차순 정렬
bts_tidy_word_freq[1:50]
## bts_tidy_word
##       the       and        in        on        to       bts        of 
##       305       108       106        98        73        72        65 
##       for     their        at     first         a      that        as 
##        63        61        50        46        45        42        39 
##     group     album     chart     music    number      they      with 
##        39        38        34        33        33        32        30 
##    korean       was    awards        by      also    artist billboard 
##        28        28        27        24        23        22        22 
##       top      year      love      over  released    single        an 
##        21        21        20        20        20        19        17 
##      most      were       its     korea      kpop     world  yourself 
##        17        17        16        16        16        16        16 
##   episode     later      song    copies      from    albums  japanese 
##        15        15        15        14        14        13        13 
##       new 
##        13

단어 빈도 테이블에서 wordcloud를 만들어봅시다.

library(wordcloud)
## Loading required package: RColorBrewer
pal <- brewer.pal(8, "Dark2") # "Dark2"에서 8가지 색상 검색
set.seed(405)
wordcloud(words = names(bts_tidy_word_freq), # 고유 단어의 열
          freq = bts_tidy_word_freq, # 단어의 빈도
          min.freq = 5, # 표시된 단어의 최소 빈도
          max.words = 500, # 빈도 순서로 표시된 500개의 단어
          random.order = FALSE, # 중앙에 위치한 최다 빈도 단어들
          rot.per = 0.1, # 플롯에서 회전하는 단어의 비율
          scale = c(4, 0.3), # 단어의 크기 범위
          colors = pal) # 단어 색상

관심 있는 위키피디아 페이지에서 직접 텍스트 처리를 해봅시다.

첫번째 숙제

  1. Wikipedia에서 관심있는 주제를 선택하세요.
  2. 페이지의 PDF 파일을 다운로드하여 RStudio에 로드하세요.
  3. stringr 패키지를 사용하여 선택한 작업의 텍스트를 사전처리합니다.
  4. 빈도 상위 50개 단어를 표시하는 표를 만드세요.
  5. 빈도 테이블에서 wordcloud 만들기

질문있나요?