본문 바로가기

R/R로 배우는 텍스트마이닝

CHAPTER 1. 정돈 텍스트(깔끔한 텍스트) 형식

반응형

정돈 데이터 원리(tidy data principles)를 따르면 이전보다 더 데이터를 아주 쉽고 효과적으로 처리할 수 있기는 하지만, 텍스트까지 쉽고 효과적으로 처리할 수 있다고 보기는 어렵다. 해들리 위컴(Wickham 2014)이 설명한 것처럼 정돈 데이터(tidy data)에는 특정한 구조가 있다.

  • 각 변량(variable)이 1개 열을 구성한다.
  • 각 관측(observation)이 1개 행을 구성한다.
  • 관측 단위의 각 유형(type)은 1개 테이블을 구성한다.

따라서 우리는 정돈 텍스트 형식(tidy text format)이란 1개 행마다 1개 토큰이 있게 구성한 테이블(a table with one token per row)이라고 정의한다. 토큰(token)이란 분석을 위해 사용하고자 하는, 단어와 같은 의미 있는 텍스트 단위이며, 토큰화(tokenization)란 텍스트를 토큰으로 분할하는 과정을 말한다. 이 1행당 1토큰(one-token-per-row) 구조는 현재 분석 업무 시 텍스트를 흔히 문자열이나 문서-용어 행렬 등에 저장할 때 쓰는 구조에 대비해 보면 사뭇 다르다는 점을 알 수 있다. 정돈 텍스트 마이닝(tidy text mining) 방식에서는 각 행에 저장하는 토큰을 1개 단어로만 구성하는 편이 흔하기는 하지만, 엔그램(n-gram)이나 문장 또는 단락으로 구성될 수도 있다. 우리는 이와 같이 흔히 사용되는 텍스트 단위에 맞춰 토큰화를 한 다음에 1행당 1용어(one-term-per-row) 형식으로 변환하는 기능을 tidytext 패키지에 넣어 두었다.

 

정돈 데이터 집합들(tidy data sets)을 dplyr(Wickham & Francois 2016), tydyr(Wickham 2016), ggplot2(Wickham 2009) 및 broom(Robinseon 2017)과 같은 주요 패키지를 포함하는 표준 '정돈' 도구 모음을 사용해 다룰 수 있다. 사용자는 정돈된 테이블에 입력과 출력을 유지함으로써 이러한 패키지 간에 (데이터를) 유동적으로 옮겨 실을 수 있다. 우리는 이러한 정돈 도구를 많은 텍스트 분석 업무 및 텍스트 탐색 업무에까지 자연스럽게 확장해서 쓸 수 있다는 점을 알았다. 

 

그뿐만 아니라 tidytext라는 패키지에서도 사용자가 자신의 분석 업무를 진행하는 과정에서 텍스트를늘 잘 정돈된 형식이 되게 지켜 나갈 것이라고는 가정하지 않는다. 이런 전제를 깔고있기 때문에 이 패키지에는 인기 있는 텍스트 마이닝용 R 패키지인 tm(Feinerer et al. 2008)과 quanteda(Benoit and Nulty 2016)로부터 나온 tidy 객체(앞에서 인용한 Robison의 broom 패키지를 볼 것)를 다루는 데 쓸 함수들이 들어 있다. 예를 들어 dplyr 및 기타 정돈 도구를 사용해 가져오고(importing) 선별하고(filtering) 처리한(processing) 후에 데이터를 머신러닝 응용 프로그램의 문서-용어 행렬로 변환하는 식의 작업 흐름이 가능하다. 그런 다음 모델을 ggplot2를 사용해 정돈된 형식으로 재구성해 해석하고 시각화할 수 있다.

 

정돈 텍스트와 다른 데이터 구조 비교하기

위에서 언급했듯이 우리는 정돈 텍스트 형식(즉, 깔끔한 데이터 형식)이란 1행당 1토큰이 있는 테이블이라고 정의한다. 이런 방식으로 텍스트 데이터를 구성하면 정돈 데이터 원리를 따르는 게 되므로 여러 도구 모음을 사용해 텍스트를일관되게 조작할 수 있다. 이런 방식은 정돈 텍스트 마이닝 방식이 아닌 기존 텍스트 마이닝 접근법에서 흔히 텍스트를 저장하는 방식들인 아래 방식들과 비교해 본다면 확실한 장점을 지닌 셈이 된다.

 

-문자열(string)

물론 텍스트를 R 언어의 문자열(즉, 문자 벡터) 형식으로도 저장할 수 있으며, 종종 텍스트 데이터는 이 형식에 맞춰 메모리로 읽혀진다.

 

-말뭉치(corpus)

이러한 객체 유형에는 일반적으로 축 ㅏ메타데이터(metadata) 및 세부 사항(details)을 사용해 주석 처리한 원시 문자열(raw strings)도 들어있다.

 

문서-용어 행렬(document-term matrix)

이것은 각 문서가 1개 행을 이루고 각 용어가 1개 열을 채우는 구조로 된 문서들의 모음집(즉, 말뭉치)이라고 설명할 수 있는 희소 행렬(sparse matrix)이다. 이 행렬을 구헝하는 각 값은 일반적으로 단어 개수(word count) 또는 tf-idf이다.

 

5장에 이르기 전ㄴ까지는 말뭉치와 문서-용어 행렬 객체를 탐색하는 일을 잠시 접어 두고, 대신에 기본으로 돌아가서 텍스트를 정돈 형식으로 변환하는 일부터 살펴보자.

 

unnest_tokens 함수

에밀리 디킨슨(Emily Dickinson)이 오래전에 멋진 문장을 썼다.

 

text <- c("Because I could not stop for Death -",
          "He kindly stopped for me -",
          "The Carriage held but just Ourselves -",
          "and Immrtality")

text
[1] "Because I could not stop for Death -"  
[2] "He kindly stopped for me -"            
[3] "The Carriage held but just Ourselves -"
[4] "and Immrtality"   

 

이 코드에 나오는 text는 전형적인 문자 벡터이며, 우리는 이것을 분석해 볼 생각이다. 그러자면 먼저 text를 데이터 프레임에 넣어야 하는데, 이렇게 해야만 정돈 텍스트 데이터셋을 바꿀 수 있기 떄문이다.

 

library(dplyr)
text_df <- data_frame(line = 1:4, text = text)

text_df
# A tibble: 4 x 2
   line text                                  
  <int> <chr>                                 
1     1 Because I could not stop for Death -  
2     2 He kindly stopped for me -            
3     3 The Carriage held but just Ourselves -
4     4 and Immrtality 

 

이 데이터 프레임이 'tibble'이라는 이름으로 출력된 이유는 뭘까? 티블(tibble)은 R의 데이터 프레임을 나타내는 현대적인 클래스로서, dplyr 및 tibble 패키지에서 사용할 수 있는 편리한 출력 메서드가 들어 있으며, 문자열을 인수(factor)로 변환하지 않고, 행 이름을 사용하지 않는다. 티블은 정돈 도구들에서 사용하기에 아주 좋다.

 

텍스트를 담고 있는 이 데이터 프레임이 정돈 텍스트 분석(tidy text analysis) 기법과는 아직 호환되지 않는다는 점에 주의하자. 각 행이 여러 개의 결합된 단어로 구성되므로 가장 자주 출현하는 단어나 카운트(count)를 선별할 수 없다. 그러므로 우리는 먼저 이 데이터 프레임을 1행당 1문서, 1문서당 1토큰 형식으로 바꿔야 한다.

 

이 첫 번째 예제에는 1개 문서(즉, 시 한 편)만 있지만, 여러 문서가 있는 예제를 곧 탐구해 볼 생각이다.

 

우리의 정돈 텍스트 프레임워트 내에서 우리는 텍스트를 개별 토큰으로 분해하고(토큰화라고하는 과정) 이를 정돈 데이터 구조(tidy data structure)로 변환해야 한다. 이렇게 하려면 tidytext 패키지에 들어 있는 unnest_tokens() 함수를 사용한다.

 

install.package("tidytext")
library(tidytext)

text_df %>% 
  unnest_tokens(word, text)
  
# A tibble: 20 x 2
    line word      
   <int> <chr>     
 1     1 because   
 2     1 i         
 3     1 could     
 4     1 not       
 5     1 stop      
 6     1 for       
 7     1 death     
 8     2 he        
 9     2 kindly    
10     2 stopped  
...

 

여기에 사용된 unnest_tokens()의 두 가지 기본 인수인 word와 text는 각기 열 이름을 나타낸다. 첫째 인수로는 텍스트의 중첩이 해제될 때 생성될 출력 열 이름을 지정하고(이 경우에는 word), 둘쨰 인수로는 텍스트가 들어오는 입력 열을 지정한다(이 경우에는 text). 앞서 나온 text_df에는 우리의 관심 댁상인 데이터를 담은 text라는 열이 있었다는 점을 기억하라.

 

unnest_tokens()를 사용하면 새로운 데이터 프레임에서는 각 행마다 토큰(단어)이 한 개만 있도록 각 행이 분할된다. unnest_tokens()에서는 기본적으로 단일한 단어들을 대상으로 토큰화하는데, 이 점을 앞에 표시되어 있는 출력 내용을 보고 확인할 수 있다. 또한 주의할 점은 이렇다.

  • (각 단어들이 들어 있는 열을 제외한) 나머지 열들(columns)은 그대로 유지된다.
  • 구두점이 제거되었다.
  • 기본적으로 unnest_tokens()는 토큰을 소문자로 변환하므로 다른 데이터셋과 쉽게 비교하거나 결합할 수 있다. 이 동작을 해제하려면 to_lower = FALSE 인수를 사용하자.

이와 같은 형식으로 변환한 텍스트 데이터를 사용하면 그림 1-1과 같이 표준 정돈 도구인 dplyr, tidyr 및 ggplot2를 사용해 텍스트를 다루고 처리하고 시각화할 수 있다.

 

그림 1-1 정돈 데이터 원리를 사용하는 전형적인 텍스트 분석 흐름도. 이번 장에서는 이러한 도구들을 사용해 텍스트를 요약하고 시각화 하는 방법을 보여준다.

 

제인 오스틴의 작품 정돈하기

제인 오스틴(Jane Austen)이 탈고해 출판한 소설 여섯 개를 janeaustenr 패키지(Silge 2016)(https://cran.r-project.org/web/packages/janeaustenr/index.html  단축 url = http://bit.ly/wRLbWsj )에서 서 가져온 다음 정돈 형식으로 변형해 보자. janeaustenr 패키지는 텍스트를 1줄당 1행(one-row-per-line) 형식으로 제공하는데, 이 맥락에서 의미하는 줄(line)이란 실제 제 도서의 원문에 맞춰 인쇄된 한 줄을 말한다. 이것을 사용해 정돈 작업에 착수하되 mutate()를 사용해 Linenumber 수에 해당하는 만큼을 주석으로 처리함으로써 원래 줄 형식을 추적하는 데 사용하고, chapter(regex 사용)를 사용해 모든 장이 어디부터 나오는지를 알아낸다.

 

library(janeaustenr)
library(dplyr)
library(stringr)

original_books <- austen_books() %>% 
  group_by(book) %>% 
  mutate(linenumber = row_number(),
         chapter = cumsum(str_detect(text, regex("^chapter [\\divxlc]",
                                                 ignore_case = TRUE)))) %>% 
  ungroup()
  
original_books
# A tibble: 73,422 x 4
   text                    book                linenumber chapter
   <chr>                   <fct>                    <int>   <int>
 1 "SENSE AND SENSIBILITY" Sense & Sensibility          1       0
 2 ""                      Sense & Sensibility          2       0
 3 "by Jane Austen"        Sense & Sensibility          3       0
 4 ""                      Sense & Sensibility          4       0
 5 "(1811)"                Sense & Sensibility          5       0
 6 ""                      Sense & Sensibility          6       0
 7 ""                      Sense & Sensibility          7       0
 8 ""                      Sense & Sensibility          8       0
 9 ""                      Sense & Sensibility          9       0
10 "CHAPTER 1"             Sense & Sensibility         10       1
# ... with 73,412 more rows

 

이것을 정돈 데이터셋으로 사용하려면 이전에 설명한 대로 unnest_tokens() 함수를 사용해 1행당 1토큰(one-token-per-row) 형식으로 구조를 다시 구성해야 한다.

 

library(tidytext)
tidy_books <- original_books %>% 
  unnest_tokens(word, text)

tidy_books
# A tibble: 725,055 x 4
   book                linenumber chapter word       
   <fct>                    <int>   <int> <chr>      
 1 Sense & Sensibility          1       0 sense      
 2 Sense & Sensibility          1       0 and        
 3 Sense & Sensibility          1       0 sensibility
 4 Sense & Sensibility          3       0 by         
 5 Sense & Sensibility          3       0 jane       
 6 Sense & Sensibility          3       0 austen     
 7 Sense & Sensibility          5       0 1811       
 8 Sense & Sensibility         10       1 chapter    
 9 Sense & Sensibility         10       1 1          
10 Sense & Sensibility         13       1 the        
# ... with 725,045 more rows

 

이 함수는 tokenizers 패키지(https:\\github.com\ropensci\tokenizers 단축 url = http://bit.ly/2CqPjz0 )를 사용해 원래 데이터 프레임에 있는 텍스트의 각 행을 토큰으로 분리한다. 기본 토큰화는 단어에 대한 것이지만 다른 옵션을 사용하면 문자, 엔그램, 문장, 줄, 단락 단위로 토큰화를 할 수 있고, 또는 정규 표현식 패턴을 사용해서 분리할 수도 있다.

이제 데이터가 1행당 1단어 형식으로 저장되므로 dplyr와 같은 정돈 도구로 데이터를 조작할 수 있다. 텍스트를 분석할 때 종종 불용오(stop words)를 제거해야 할 때가 있는데, 불용어란 분석에 유용하지 않은 단어를 말하며, 일반적으로 영어의 'the', 'of', 'to' 등 과 같은 매우 전형적인 단어를 말한다. anti_join()을 사용해 불용어(정돈 텍스트 데이터셋인 stop_words에 기록해 둔 것)를 제거할 수 있다.

 

data(stop_words)

tidy_books <- tidy_books %>% 
  anti_join(stop_words)

 

tidytext 패키지의 stop_words 데이터셋에는 세 개의 불용어 용어집(lexicon)이 들어 있다. 여기에 있는 것처럼 모두 함께 사용할 수도 있고, 특정 분석에 더 적합한 경우 filter()를 사용해 1개 불용어 집합만 사용할 수도 있다.

 

또한, dplyr의 count()를 사용해 모든 도서에서 가장 흔하게 나오는 단어를 찾을 수 있다.

 

tidy_books %>% 
  count(word, sort = TRUE)
# A tibble: 13,914 x 2
   word       n
   <chr>  <int>
 1 miss    1855
 2 time    1337
 3 fanny    862
 4 dear     822
 5 lady     817
 6 sir      806
 7 day      797
 8 emma     787
 9 sister   727
10 house    699
# ... with 13,904 more rows

 

우리가 정돈 도구들을 사용했기 떄문에 단어 카운트(word count)는 정돈 데이터 프레임에 저장된다. 이처럼 텍스트가 저돈 데이터 프레임에 저장되었으므로 이제 ggplot2 패키지로 직접 연결(pipe)할 수 있다. 예를 들면 다음과 같이 가장 흔한(common) 단어를 시각화할 수 있다.(그림 1-2)

 

library(ggplot2)

tidy_books %>% 
  count(word, sort = TRUE) %>% 
  filter(n > 600) %>% 
  mutate(word = reorder(word, n)) %>% 
  ggplot(aes(word, n)) +
  geom_col() +
  xlab(NULL) +
  coord_flip()

그림 1-2 제인 오스틴의 소설에 가장 흔하게 나오는 단어

austen_books() 함수를 사용하면 우리가 분석하려고 하는 텍스트를 가지고 곧바로 작업을 시작할 수 있지만, 그렇지 않은 경우에는 저작권 헤더를 제거하거나 서식을 지정하는 등 텍스트 데이터를 정돈해야 할 수도 있다. 사례 연구를 다룬 여러 장에서 이런 종류의 전처리를 다룬 예제를 볼 수 있는데, 특히 179쪽부터 시작하는 '전처리' 단원에서 볼 수 있다.

 

gutenbergr 패키지

이제 janeaustenr 패키지를 사용해 정돈 텍스트를 탐색해 보았으므로, 이번에는 gutenbergr 패키지(Robinson 2016)를 소개하겠다. gutenbergr 패키지(https://github.com/ropenscilabs/gutenbergr/ 단축 url = http://bit.ly/2stjD8q )는 구텐베르크 프로젝트(Project Gutenberg, https://www.gutenberg.org/ ) 모음집 중 공공 저작물(poblic domain works)에 해당하는 텍스트에 접근할 수 있게 한다. 이 패키지에는 도서를 내려받기 위한 도구(도움이 되지 않는 머리글/바닥글 정보는 제거)와 관심 있는 작품을 찾는 데 사용할 수 있는 구텐베르크 프로젝트 메타데이터의 전체 데이터셋이 포함되어 있다. 이 책에서는 구텐베르크 프로젝트에서 ID별로 하나 이상의 작품을 다운로드하는 gutenberg_download() 함수를 주로 사용하지만, 다른 함수를 사용해 메타데이터를 탐색하고 제목, 작성자, 언어 등과 짝을 이루는 구텐베르크 ID를 탐색할 수 있을 뿐만 아니라 저자에 대한 정보도 수집할 수 있다.

 

단어 빈도

제인 오스틴의 소설에 대해 수행한 것처럼 텍스트 마이닝 시에는 단어 빈도를 살핀 다음에 다른 텍스트와 빈도를 비교해 보는 일을 자주 하게 된다. 정돈 데이터 원리를 사용하면 이런 작업 과정을 직관적이면서도 원활하게 할 수 있다. 우리에게는 이미 제인 오스틴의 작품이 있다. 비교해 볼 원문 두 개를 더 보자. 먼저 19세기 말부터 20세기 초반에 걸쳐 살았던 웰스(H.G. Wells)의 공상 과학 소설과 판타지 소설을 살펴보자. 「The Time Machine」(타임머신, https://www.gutenberg.org/ebooks/35 ), 「The Invisible Man」(인비지블 맨, https://www.gutenberg.org/ebooks/5230 ), 「The Island of Doctor Moreau」  (닥터 모로의 섬, https://www.gutenberg.org/ebooks/159 )를 입수하자. 우리는 gutenberg_download()와 각 소설에 대한 구텐베르크 프로젝트 식별 번호를 사용해 이러한 작품에 액세스할 수 있다.

 

#install.packages("gutenbergr")
library(gutenbergr)

hgwells <- gutenberg_download(c(35, 36, 5230, 159))

tidy_hgwells <- hgwells %>% 
  unnest_tokens(word, text) %>% 
  anti_join(stop_words)

 

재미 삼아서 웰스의 소설에 가장 공통적으로 나오는 단어는 무엇인지 알아보자.

 

tidy_hgwells %>% 
  count(word, sort = TRUE)

# A tibble: 11,769 x 2
   word       n
   <chr>  <int>
 1 time     454
 2 people   302
 3 door     260
 4 heard    249
 5 black    232
 6 stood    229
 7 white    222
 8 hand     218
 9 kemp     213
10 eyes     210
# ... with 11,759 more rows

 

이번에는 브론테(Bronte) 자매의 유명한 작품을 입수해 볼 텐데, 브론테 자매는 제인 오스틴과 비슷한 시대를 살았지만 오히려 다른 문체로 글을 썼다. 「Jane Eyre」 (제인 에어, https://www.gutenberg.org/ebooks/1260 ), 「Wuthering Heights」 (폭풍의언덕, https://www.gutenberg.org/ebooks/768 ), 「The Tenant of Wildfell Hall」  (와일드펠 홀의 소작인, https://www.gutenberg.org/ebooks/969 ) 「Villette」 (https://www.gutenberg.org/ebooks/767 )를 입수하자. 각 소설에 구텐베르크 프로젝트 식별 번호를 다시 사용하고 gutenberg_download()를 사용하면 해당 텍스트에 액세스할 수 있다.

 

bronte <- gutenberg_download(c(1260, 768, 969, 9182, 767))

tidy_bronte <- bronte %>% 
  unnest_tokens(word, text) %>% 
  anti_join(stop_words)

 

브론테 자매의 소설에서 가장 자주 출현하는 단어는 무엇인가?

 

tidy_bronte %>% 
  count(word, sort = TRUE)

# A tibble: 23,050 x 2
   word       n
   <chr>  <int>
 1 time    1065
 2 miss     855
 3 day      827
 4 hand     768
 5 eyes     713
 6 night    647
 7 heart    638
 8 looked   601
 9 door     592
10 half     586
# ... with 23,040 more rows

 

웰스와 브론테 자매의 작품에서 모두 'time', 'eyes', 'hand'라는 단어가 사우이 10위 안에 속한다는 점이 흥미롭다.

 

이제 제인 오스틴, 브론테 자매 및 웰스의 작품에서 각 단어의 빈도를 계산해 보고 데이터 프레임을 함께 묶자. 우리는 tidyr의 spread()와 gather()를 사용해 데이터 프레임을 재구성 함으로써 세 개의 소설을 그려서 비교하기에 알맞게 한다.

 

library(tidyr)

frequency <- bind_rows(mutate(tidy_bronte, author = "Brontë Sisters"),
                       mutate(tidy_hgwells, author = "H.G. Wells"),
                       mutate(tidy_books, author = "Jane Austen")) %>% 
  mutate(word = str_extract(word, "[a-z']+")) %>% 
  count(author, word) %>% 
  group_by(author) %>% 
  mutate(proportion = n / sum(n)) %>% 
  select(-n) %>% 
  spread(author, proportion) %>% 
  gather(author, proportion, 'Brontë Sisters' : 'H.G. Wells')

 

구텐베르크 프로젝트 내 UTF-8로 인코딩된 텍스트에 밑줄을 쳐서 강조한(기울임 꼴철머) 몇 가지 사례가 들어 있으므로 여기서는 str_extract()를 사용할 것이다. tokenizer는 이렇게 강조 처리한 단어를 강조 처리를 하지 않은 단어와 서로 다른 단어로 여기지만, str_extract()를 사용해 보기 전에 우리가 초기 데이터 탐색 작업을 할 때에 보았듯이 우리는 'any'와 'any'를 따로 세기를 원하지 않는다.

 

이제 그래프를 그려보자(그림 1-3)

library(scales)

#결측값(missing values)이 제거된 행(row)에 대한 경고가 표시될 수 있다.
ggplot(frequency, aes(x = proportion, y = `Jane Austen`,
                      color = abs(`Jane Austen` - proportion))) +
  geom_abline(color = "gray40", lty = 2) +
  geom_jitter(alpha = 0.1, size = 2.5, width = 0.3, height = 0.3) +
  geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
  scale_x_log10(labels = percent_format()) +
  scale_y_log10(labels = percent_format()) +
  scale_color_gradient(limits = c(0, 0.001),
                       low = "darkslategray4", high = "gray75") +
  facet_wrap(~author, ncol = 2) +
  theme(legend.position = "none") +
  labs(y = "Jane Austen", x = NULL)

그림 1-3 제인 오스틴, 브론테 자매 및 웰스의 단어 빈도를 비교

이 그림들 중에서 점선에 가까운 단어는 두 텍스트 집합에서 모두 비슷한 빈도를 보이는데, 예를 들면 오스틴과 브론테의 텍스트를 비교한 경우(빈도가 높은 상단에 'miss', 'time', 'day'가 있음)와 오스틴과 웰스의 텍스트를 비교한 경우(빈도가 높은 상단에 'time', 'day', 'brother'가 있음)를 들 수 있다. 점선에서 멀리 떨어져 있는 단어는 어느 한쪽 텍스트 집합에서만 더 많이 발견되는 단어다. 예를 들어 오스틴-브론테 비교 그림에서 'elizabeth', 'anne', 'fanny'와 같은 단어(모두 고유 명사)는 오스틴의 텍스트에는 많지만 브론테 텍스트에는 많지 않고, 'arthur'와 'dog'는 브론테 텍스트에는 있지만 오스틴 텍스트에는 ㅇ벗다. 웰스를 제인 오스틴과 비교해 보면 웰스가 쓰는 'beast', 'feet', 'black'과 같은 단어를 오스틴은 쓰지 않는 반면에, 오스틴이 사용하는 'family', 'frend', 'letter', 'dear'와 같은 단어를 웰스는 쓰지 않는다.

 

전반적으로 그림 1-3에서 오스틴-브론테 비교 그래프에 나오는 단어들이 오스틴-웰스 비교 그래프에 나오는 것보다 깅루게 그어진 점선에 더 가까이 있다는 점에 주목하자. 또한 단어가 오스틴-브론테 그래프에서 빈도가 더 낮은 곳까지 퍼져 있다는 점에 주목하자. 오스틴-웰스 비교 그래프에서는 빈도가 낮은 곳이 비어 있다. 이러한 특성은 오스틴이 웰스보다는 브론테와 더 비슷한 단어를 사용함을 나타낸다. 또한 우리는 모든 단어가 세 가지 집합으로 이뤄진 텍스트 모두에 나오지는 않는다는 점과, 오스틴과 웰스의 빈도를 나타낸 그래프에 데이터 점이 적다는 것을 알 수 있다.

 

이러한 단어 집합의 유사성과 차이점을 상관 검정을 통해 정량화(quantify, 수량화)해 보자. 오스틴과 브론테 자매 사이, 오스틴과 웰스 사이의 단어 빈도는 어떤 상관(correlation)이 있는가?

 

cor.test(data = frequency[frequency$author == "Brontë Sisters",],
         ~ proportion + `Jane Austen`)

	Pearson's product-moment correlation

data:  proportion and Jane Austen
t = 119.65, df = 10404, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7527869 0.7689642
sample estimates:
      cor 
0.7609938 

cor.test(data = frequency[frequency$author == "H.G. Wells",],
         ~ proportion + `Jane Austen`)  

	Pearson's product-moment correlation

data:  proportion and Jane Austen
t = 36.441, df = 6053, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.4032800 0.4445987
sample estimates:
      cor 
0.4241601 

 

우리가 그래프에서 볼 수 있었듯이 오스틴과 웰스 사이보다는 오스틴과 브론테 소설 사이에서 단어의 빈도가 더 많이 연관되어 있다.

 

요약

이번 장에서는 텍스트와 관련되었을 떄 정돈 데이터(tidy data, 깔끔한 데이터)가 무엇을 의미하느지, 그리고 정돈 데이터 원리(tidy data principles)가 자연어 처리에 어떻게 적용될 수 있는지를 살펴보았다. 텍스트가 1행당 1토큰 형식으로 구성되어 있으면 정돈 도구 생태계를 이루는 도구들을 동원해 불용어 제거 작업이나 단어 빈도 계산 작업과 같은 연산을 자연스럽게 처리할 수 있다. 1행당 1토큰이라는 구조를 단일 단어뿐만 아니라 엔그램을 거쳐 기타 의미 있는 텍스트 단위까지 확장해 적용할 수 있을 뿐 아니라, 이 책에서 앞으로 다루게 될 우선 분석 대상으로도 확장할 수 있다.

 

반응형