spacyr
포스태깅(Part of speech)는 단어의 품사 정보를 결정하는 절차이며 개체명 인식(Named entity recognition)은 인명, 지명 등 고유명사를 분류하는 방법론이다. 형태소란 의미를 가지는 최소 단위로 더 쪼개면 뜻을 잃어버리는 말의 단위를 뜻한다.
spaCy
→ spacyr
spacyr
은 파이썬 spaCy
팩키지 감싼(wrapper) R 팩키지로 spaCy
기능을 R에서 사용할 수 있도록 개발되었다. spaCy
는 다음과 같은 기능을 수행할 수 있는데 “industrial strength natural language processing”라고 표방하면서 자연어 처리에 자부심이 있음에 분명하다.
Github
, CRAN
에서 spacyr
팩키지를 설치한다. 윈도우 환경에서 설치할 경우 Install spaCy를 참조하여 설치한다.
RStudio 아이콘 위에 마우스를 올려 관리자 권한으로 실행시킨 후에 spacy_install()
함수를 호출하여 설치한다.
spacyr
헬로월드spacyr
소품문에 담긴 사례를 활용하여 tidytext
와 결합하여 개체명 인식을 시도해본다.
library(tidyverse)
library(tidytext)
library(spacyr)
spacy_initialize()
txt <- c(d1 = "spaCy is great at fast natural language processing.",
d2 = "Mr. Smith spent two years in North Carolina.")
parsed_txt <- spacy_parse(txt, tag = TRUE, entity = TRUE, lemma = TRUE)
parsed_txt %>%
unnest_tokens(word, token) %>%
anti_join(stop_words) %>%
filter(entity != "")
doc_id sentence_id token_id lemma pos tag entity word
1 d1 1 1 spacy ADJ JJ GPE_B spacy
2 d2 1 2 smith PROPN NNP PERSON_B smith
3 d2 1 7 north PROPN NNP GPE_B north
4 d2 1 8 carolina PROPN NNP GPE_I carolina
pdf_text
함수로 .pdf
파일을 R에서 처리 가능한 문자열을 뽑아내서 객체에 담아낸다.
library(pdftools)
library(tidyverse)
library(tidytext)
keynes_pdf <- pdf_text("data/generaltheory.pdf")
hayek_pdf <- pdf_text("data/The-Road-to-Serfdom-F.A.-Hayek.pdf")
keynes_df <- keynes_pdf %>%
tbl_df() %>%
mutate(book = "케인즈") %>%
unnest_tokens(word, value)
hayek_df <- hayek_pdf %>%
tbl_df() %>%
mutate(book = "하이에크") %>%
unnest_tokens(word, value)
data(stop_words)
book_df <- bind_rows(keynes_df, hayek_df) %>%
mutate(word = str_to_lower(word)) %>%
anti_join(stop_words)
케인즈 “일반이론” 책을 각 Chapter 장으로 쪼갠다. 이를 위해서 정규표현식을 동원해서 각 장의 첫번째 장을 추출하고 매칭되지 않는 것을 NA
로 처리한 후에 tidyr::fill()
함수로 채워넣고 기타 불필요한 서문과 부록(Appendix)을 제거시킨다.
kbook_df <- keynes_pdf %>%
tbl_df() %>%
mutate(text = str_split(value, "\r\n")) %>%
unnest(text) %>%
select(-value) %>%
mutate(book="케인즈")
kbook_df %>%
filter(str_detect(text, "^Chapter [0-9]+|^Appendix [0-3]")) %>% tail
# A tibble: 6 x 2
text book
<chr> <chr>
1 Chapter 22 케인즈
2 Chapter 23 케인즈
3 Chapter 24 케인즈
4 Appendix 1 케인즈
5 Appendix 2 케인즈
6 Appendix 3 케인즈
앞서 “일반이론”을 각 장별로 나눠서 총 24장으로 구성된 것을 확인하고 제대로 검증하기 위해서 단어 갯수를 불용어 처리해서 세어본다.
kbook_chap_df %>%
select(book, chapter, text) %>%
unnest_tokens(word, text) %>%
anti_join(stop_words) %>%
count(word, sort=TRUE)
# A tibble: 6,008 x 2
word n
<chr> <int>
1 rate 759
2 money 724
3 employment 623
4 investment 543
5 capital 510
6 income 377
7 marginal 367
8 wage 338
9 demand 337
10 cost 322
# ... with 5,998 more rows
일반이론 모든 장에 대해 개체명 인식작업을 수행하게 되면 시간이 많이 걸려 무작위로 5장만 추출하여 개체명인식 작업을 수행한다. spaCy
Annotation Specifications을 참조하여 가장 많이 언급된 사람을 찾아본다. 이를 위해서 각 장별로 spacy_parse()
함수로 개체명 인식작업을 수행하고 데이터프레임에서 개체를 추출한다. 가장 많이 추출된 개체명에 대한 빈도수를 뽑아본다.
spacy_initialize()
set.seed(777)
kbook_chap_spacyr_df <- kbook_chap_df %>%
select(chapter, text) %>%
group_by(chapter) %>%
mutate(text = paste0(text, collapse = " ")) %>%
ungroup() %>%
sample_n(5) %>%
mutate(ner = map(text, ~spacy_parse(., entity = TRUE)))
spacy_finalize()
entity_df <- kbook_chap_spacyr_df %>%
mutate(entity = map(ner, ~ count(., entity, sort = TRUE) )) %>%
unnest(entity)
entity_df %>%
group_by(entity) %>%
summarise(entity_sum = sum(n)) %>%
arrange(desc(entity_sum)) %>%
filter(entity != "")
# A tibble: 26 x 2
entity entity_sum
<chr> <int>
1 CARDINAL_B 148
2 ORG_I 97
3 PERSON_B 91
4 GPE_B 63
5 MONEY_I 50
6 DATE_I 47
7 ORG_B 44
8 MONEY_B 28
9 DATE_B 27
10 ORDINAL_B 26
# ... with 16 more rows
개체명에서 “PERSON”을 추출하여 가장 많이 언급된 사람을 살펴본다.
person_df <- kbook_chap_spacyr_df %>%
mutate(person_ent = map(ner, ~ filter(., str_detect(entity, "PERSON")) )) %>%
unnest(person_ent)
person_df %>%
group_by(chapter, lemma) %>%
summarise(freq = n()) %>%
filter(str_length(lemma) > 3,
str_detect(lemma, "[[::alpha::]]")) %>%
arrange(desc(freq)) %>%
spread(chapter, freq, fill=0) %>%
DT::datatable()