R을 이용한 텍스트 데이터 분석
마이리얼트립에는 45만개가 넘는 여행 후기 데이터가 쌓여 있습니다. 세세한 맥락 정보가 생략될 수 밖에 없는 정량 데이터와는 달리, 텍스트로 남는 여행 후기 데이터는 다녀온 여행에서 구체적으로 어떤 점이 좋았는지(혹은 불편했는지)를 상세히 알려주는 굉장히 소중한 자료입니다. 실제로 여행상품을 예약할 때 사용자들이 가장 많이 참고하는 요소 중 하나이기도 하구요. 마이리얼트립 직원들 역시, 사용자분들이 어떤 경험을 하는지 확인하기 위해서 여행 후 남겨지는 후기들을 꼼꼼하게 살펴보고 있습니다. (특히 1점 후기 ㅠㅜ)
5점을 받은 ‘매우 만족’ 후기와 1점을 받은 ‘매우 불만족’ 후기가 어떻게 다른지, 후기를 남길 때 어떤 단어들을 쓰는지를 살펴보면 사용자들이 좋아할만한 여행 상품을 만드는 데 힌트를 얻을 수 있지 않을까요? ‘만족스러운 여행 경험’에 영향을 미치는 변수들은 무엇일까? 에 대한 아이디어를 얻기 위해서, 마이리얼트립 여행 후기 데이터를 분석해 보았습니다.
누적 데이터가 너무 많아서, 일단 2018년 1년 간 쌓인 후기 데이터만 가져와서 분석을 진행하기로 했습니다. (raw_data는 아래와 같은 형태입니다.) 평점을 기준으로 그룹핑하고, 특수문자를 제거하는 것으로 간단한 전처리를 완료했습니다.
# 필요한 패키지 로드
library(tidyverse)
library(KoNLP)
library(reshape2)
library(wordcloud2)
library(tidygraph)
library(ggraph)
# 데이터 준비
review_2018 #raw data
# 평점 기준으로 그룹핑
score1 <- review_2018 %>% filter(score==1)
score5 <- review_2018 %>% filter(score==5)
# 특수문자를 제거한 텍스트 데이터만 남기기
review_score1 <- str_replace_all(score1$message, "\\W"," ")
review_score5 <- str_replace_all(score5$message, "\\W"," ")
R에서 한글 텍스트분석을 하는 데 가장 널리 사용되는 패키지는 KoNLP 입니다. KoNLP를 로드하면 다양한 함수를 사용해서 문장을 형태소단위로 쪼개고, 적절한 품사 태그를 붙일 수 있습니다. (자세한 내용은 여기를 참조)
본 분석에서는 KoNLP의 SimplePos09() 함수를 사용하여, 리뷰 내용중에서 체언[N: 명사, 대명사, 수사]과 용언[P: 동사, 형용사, 보조용언]에 해당하는 단어들을 추출해서 테이블과 워드클라우드를 만들었습니다.
#### SimplePos09 를 이용한 품사 태깅 ####
# N 체언: 명사, 대명사, 수사
# P 용언: 동사, 형용사, 보조용언
# 사전을 정의합니다
useSejongDic()
# 품사 태그 붙이기
raw_score5 <- SimplePos09(review_score5) %>% melt() %>% select(3,1)
# 체언(N), 용언(P)에 해당하는 단어 추출
score5_NP <- raw_score5 %>%
mutate(extract = str_match(value, '([가-힣]+)/[NP]')[,2]) %>%
filter(!is.na(extract))
# 용언(P)의 경우, 어미에 '다'를 붙여서 형용사/동사 형태로 변경
score5_NP$keyword <- ifelse(str_detect(score5_NP$value, "/P"), paste0(score5_NP$extract, '다'), paste(score5_NP$extract))
score5_NP$type <- ifelse(str_detect(score5_NP$value, "/P"), 'P', 'N')
# 상위 100개 추출하여 테이블 만들기 (+추출된 형태소가 2글자 이상인 것만 남김)
score5_NP_table <- score5_NP %>%
filter(str_length(extract) >= 2) %>%
group_by(keyword) %>%
summarise(cnt = n()) %>%
arrange(desc(cnt)) %>%
head(100)
# 워드클라우드 만들기
score5_NP_table %>%
wordcloud2(fontFamily='NanumGothic', size = 0.8, color = 'random-dark', minRotation=0,
maxRotation=0)
평점 5점 후기와, 평점 1점 후기에서 각각 뽑아낸 명사, 대명사, 동사, 형용사 로 만든 워드클라우드는 아래와 같습니다. (상위 100개 추출)
몇 가지 재미있는 결과가 보이긴 하는데요. (5점 리뷰에서는 ‘가이드님’이 많고, 1점 리뷰에서는 ‘가이드’가 많다던지??;;) 이 경우 ‘여행’, ‘투어’, ‘가이드’ 등 양쪽 결과에 모두 나타나는 중립적인 단어들로 인해 그룹 간 차이를 명확하게 구분하기가 어렵습니다. 그래서 양쪽 그룹에서 모두 100위 안에 든 텍스트를 제외하고, 각 그룹에서 Unique하게 확인되는 텍스트 기준으로 워드클라우드를 다시 만들어 보았습니다.
# 1점 리뷰와 5점 리뷰 각각 상위 100 키워드
score1_NP_keyword <- score1_NP_table %>% select(keyword) %>% unlist()
score5_NP_keyword <- score5_NP_table %>% select(keyword) %>% unlist()
# 1점 리뷰 상위 100위안에 드는 키워드를 제외하고 워드클라우드 다시 그리기
score5_NP_only <- score5_NP %>%
filter(str_length(extract) >= 2) %>%
filter(!(keyword %in% score1_NP_keyword)) %>%
group_by(keyword) %>%
summarise(cnt = n()) %>%
arrange(desc(cnt)) %>%
head(100)
score5_NP_only %>%
wordcloud2(fontFamily='NanumGothic', size = 0.8, color = 'random-dark', minRotation=0, maxRotation=0)
이렇게 보니 5점과 1점을 대표하는 단어가 명확하게 구분됩니다. ‘편하다’와 ‘안되다’가 대표 키워드라고 할 수 있겠네요. 5점 후기의 경우 감정을 표현하는 형용사들이 상대적으로 많은 것을 확인할 수 있습니다. 상대적으로 1점 후기에서는 ‘환불’이나 ‘연락’ 등 불만족의 원인이 되는 명사형 키워드들이 많이 포함된 게 눈에 띕니다.
텍스트에 포함된 단어의 노출빈도(frequency)를 기반으로 한 워드클라우드를 만드는 것은 전체 데이터를 요약해서 한 눈으로 확인하는 데 매우 효율적인 방법이지만 뚜렷한 단점이 있습니다. 단어와 단어 간 연관성을 알기가 어렵다는 점입니다. 가령, 위 케이스에서는 ‘편하다’와 ‘안되다’를 대표단어로 뽑아낼 수는 있지만 무엇이 편해서 좋았는지, 무엇이 안되서 실망했는지…에 대한 구체적인 내용을 알기가 어렵습니다.
단순히 등록된 단어의 빈도를 세는 것 이외에, 단어 간의 관계를 좀 더 자세히 확인할수 있는 방법이 있을까요? 가장 심플한 n-gram 분석방법인 바이그램(Bigram)을 이용하여 추가적인 시각화를 해 보았습니다. (바이그램은 문장에서 사용된 전/후 단어를 2개씩 조합해서 분석하는 것을 의미합니다. 자세한 설명은 여기를 참조)
# 바이그램 테이블 만들기
bigram_score5 <- score5_NP %>%
filter(str_length(extract) >= 2) %>%
select(keyword) %>%
mutate(lead=lead(keyword)) %>%
filter(keyword != lead) %>%
unite(bigram, c(keyword, lead), sep=' ') %>%
count(bigram, sort=TRUE) %>%
head(30) %>%
separate(bigram, c('word1', 'word2'))
# 바이그램 그래프 그리기
bigram_score5 %>%
as_tbl_graph %>%
ggraph() +
geom_edge_link(aes(start_cap = label_rect(node1.name), end_cap = label_rect(node2.name)), edge_color = 'blue', edge_width = 1) +
geom_node_text(aes(label=name), size = 5, color = 'darkblue', fontface = 'bold') +
theme_classic()
문장의 전/후 단어를 함께 고려함으로써, 위와 같이 맥락이 조금 더 포함된 구체적인 정보를 확인할 수 있게 되었습니다. 5점 후기와 1점 후기에 모두 ‘시간’ 이라는 단어가 사용되었지만, 5점 후기에서는 ‘시간’ 이라는 단어가 ‘즐겁다’ / ‘보내다’ 등과 함께 사용된 반면 1점 후기에서는 ‘기다리다’ / ‘걸리다’ 등과 같은 부정적인 단어와 함께 사용되었네요. 1점 후기에서 가장 두드러지게 나온 ‘안되다’ 와 함께 사용된 단어들은 ‘연락’, ‘충전’, ‘환불’ 과 같은 명사인데요. ‘무엇이 안되어서 불만족했는지’를 훨씬 더 명확하게 알 수 있습니다.
위 케이스에는 전체 데이터를 사용한 예시를 보여드렸지만, 분석에 사용하는 데이터셋을 어떤 식으로 준비하고 전처리하느냐에 따라 엄청나게 다양한 결과를 볼 수 있습니다. 가령, 오사카 상품 리뷰와 로마 상품 리뷰를 비교한다던지, 혼자 다녀온 배낭여행자들의 리뷰와 가족여행으로 다녀온 분들의 리뷰를 비교한다던지 하는 식으로 말이죠. 혹은, 위 케이스에서처럼 다양한 품사를 한꺼번에 뽑아내지 말고 하나씩 집중해서 볼 수도 있겠네요. (예를 들면, ‘명사’만 태깅하기)
이상으로 간단한(?) 한글 텍스트분석에 대해서 알아봤습니다. 사실 한글 텍스트분석의 경우 데이터 전처리가 꽤 번거롭고 시간이 많이 걸리는 작업인데요. (오타가 있는 단어, 띄어쓰기가 되지 않은 문장…의 경우, 형태소로 쪼개거나 품사 태깅하는 과정이 매끄럽게 진행되지 않습니다. 또 표준어가 아닌 구어체를 사용하는 경우에도 문제가 됩니다. 예를 들면 “가이드님 깨알지식 재밌었어요. TMI스럽긴 했지만 ㅋㅋ 갠적으로 완전강추!” 이런 거? -_-) 이 글에서는 전처리를 위한 ‘노가다’에 해당하는 부분은 가능한 간략하게 기술했습니다.
모든 데이터 분석 과정이 그렇겠지만, 특히나 정성데이터는 분석과정의 자유도가 높아서 분석가의 질문과 상상력이 중요한 것 같습니다. (역시 중요한 건 좋은 질문!) 더불어, 이 글에는 모두 공개되지 않았지만, 텍스트분석 결과를 정리하고 인사이트를 찾아내는 과정에서는 매일같이 1점 후기를 정독(!)하고 계신 마이리얼트립 운영팀 분들의 도움을 많이 받았고 + 앞으로 더 많은 도움을 받을 예정입니다.
마지막으로,
이렇게 재미있는 데이터가 한가득 쌓여있고, 좋은 사람들이 함께 일하는 곳. 마이리얼트립에서 함께 할 동료들을 찾고 있습니다.