class: title0 Do it! 쉽게 배우는 R 텍스트 마이닝 --- class: no-page-num <br> .pull-left[ <img src="https://raw.githubusercontent.com/youngwoos/Doit_textmining/main/cover.png" width="70%" height="70%" /> ] .pull-right[ <br> <br> <br> <svg viewBox="0 0 496 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg> [github.com/youngwoos/Doit_textmining](https://github.com/youngwoos/Doit_textmining) <svg viewBox="0 0 448 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M400 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h137.25V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.27c-30.81 0-40.42 19.12-40.42 38.73V256h68.78l-11 71.69h-57.78V480H400a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48z"></path></svg> [facebook.com/groups/datacommunity](https://facebook.com/groups/datacommunity) - [네이버책](https://book.naver.com/bookdb/book_detail.nhn?bid=17891971) - [yes24](http://bit.ly/3oUuJOB) - [알라딘](http://bit.ly/3oXOSDn) - [교보문고](https://bit.ly/2LtNOcB) ] --- class: title0 04 감정 분석: <br> 어떤 마음으로 글을 썼을까? --- class: title0-2 We'll make <br-back-20> <img src="Image/04/04_2_2.png" width="70%" height="70%" /> --- class: title0-2 and <br-back-40> <img src="Image/04/04_4_1.png" width="70%" height="70%" /> --- <br> .large2[.font-jua[목차]] .large[.font-jua[04-1 감정 사전 활용하기]]([link](#04-1)) .large[.font-jua[04-2 댓글 감정 분석하기]]([link](#04-2)) .large[.font-jua[04-3 감정 범주별 주요 단어 살펴보기]]([link](#04-3)) .large[.font-jua[04-4 감정 사전 수정하기]]([link](#04-4)) --- name: 04-1 class: title1 04-1 감정 사전 활용하기 --- ##### 감정 분석(sentiment analysis) - 텍스트에 어떤 감정이 담겨있는지 분석하는 방법 - 글쓴이가 어떤 감정을 담아 글을 썼는가? - 사람들이 어떤 주제를 긍정적/부정적으로 느끼는가? <img src="Image/etc/04_1_dic.png" width="80%" /> --- ##### 감정 사전 - '감정 단어'와 '감정의 강도를 표현한 숫자'로 구성된 사전 - 사전을 이용해 문장의 단어에 감정 점수를 부여한 다음 합산 -- #### 감정 사전 살펴보기 - KNU 한국어 감성사전 - 군산대학교 소프트웨어융합공학과에서 개발 - `word`: 감정 단어 - `polarity`: 감정의 강도 <svg viewBox="0 0 352 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M176 80c-52.94 0-96 43.06-96 96 0 8.84 7.16 16 16 16s16-7.16 16-16c0-35.3 28.72-64 64-64 8.84 0 16-7.16 16-16s-7.16-16-16-16zM96.06 459.17c0 3.15.93 6.22 2.68 8.84l24.51 36.84c2.97 4.46 7.97 7.14 13.32 7.14h78.85c5.36 0 10.36-2.68 13.32-7.14l24.51-36.84c1.74-2.62 2.67-5.7 2.68-8.84l.05-43.18H96.02l.04 43.18zM176 0C73.72 0 0 82.97 0 176c0 44.37 16.45 84.85 43.56 115.78 16.64 18.99 42.74 58.8 52.42 92.16v.06h48v-.12c-.01-4.77-.72-9.51-2.15-14.07-5.59-17.81-22.82-64.77-62.17-109.67-20.54-23.43-31.52-53.15-31.61-84.14-.2-73.64 59.67-128 127.95-128 70.58 0 128 57.42 128 128 0 30.97-11.24 60.85-31.65 84.14-39.11 44.61-56.42 91.47-62.1 109.46a47.507 47.507 0 0 0-2.22 14.3v.1h48v-.05c9.68-33.37 35.78-73.18 52.42-92.16C335.55 260.85 352 220.37 352 176 352 78.8 273.2 0 176 0z"></path></svg> KNU 한국어 감성사전 출처: github.com/park1200656/KnuSentiLex ```r # 감정 사전 불러오기 library(dplyr) library(readr) dic <- read_csv("knu_sentiment_lexicon.csv") ``` --- .pull-left[ ```r # 긍정 단어 dic %>% filter(polarity == 2) %>% arrange(word) ``` ``` ## # A tibble: 2,602 x 2 ## word polarity ## <chr> <dbl> ## 1 가능성이 늘어나다 2 ## 2 가능성이 있다고 2 ## 3 가능하다 2 ## 4 가볍고 상쾌하다 2 ## 5 가볍고 상쾌한 2 ## 6 가볍고 시원하게 2 ## 7 가볍고 편안하게 2 ## 8 가볍고 환하게 2 ## 9 가운데에서 뛰어남 2 ## 10 가장 거룩한 2 ## # ... with 2,592 more rows ``` ] .pull-right[ ```r # 부정 단어 dic %>% filter(polarity == -2) %>% arrange(word) ``` ``` ## # A tibble: 4,799 x 2 ## word polarity ## <chr> <dbl> ## 1 가난 -2 ## 2 가난뱅이 -2 ## 3 가난살이 -2 ## 4 가난살이하다 -2 ## 5 가난설음 -2 ## 6 가난에 -2 ## 7 가난에 쪼들려서 -2 ## 8 가난하게 -2 ## 9 가난하고 -2 ## 10 가난하고 어렵다 -2 ## # ... with 4,789 more rows ``` ] --- ##### 감정 단어의 종류 살펴보기 <br10> - `word` - 한 단어로 된 단일어, 둘 이상 단어 결합된 복합어 - 이모티콘: `^^`, `ㅠㅠ` - `polarity` - 5가지 정수(+2, +1, 0, -1, -2) - `+`: 긍정 단어, `-`: 부정 단어, `0`: 중성 단어 -- .pull-left[ ```r dic %>% filter(word %in% c("좋은", "나쁜")) ``` ``` ## # A tibble: 2 x 2 ## word polarity ## <chr> <dbl> ## 1 좋은 2 ## 2 나쁜 -2 ``` ] .pull-right[ ```r dic %>% filter(word %in% c("기쁜", "슬픈")) ``` ``` ## # A tibble: 2 x 2 ## word polarity ## <chr> <dbl> ## 1 슬픈 -2 ## 2 기쁜 2 ``` ] --- ```r # 이모티콘 library(stringr) dic %>% filter(!str_detect(word, "[가-힣]")) %>% arrange(word) ``` ``` ## # A tibble: 77 x 2 ## word polarity ## <chr> <dbl> ## 1 -_-^ -1 ## 2 (-; 1 ## 3 (-_-) -1 ## 4 (;_;) -1 ## 5 (^-^) 1 ## 6 (^^) 1 ## 7 (^^* 1 ## 8 (^_^) 1 ## 9 (^_^; -1 ## 10 (^o^) 1 ## # ... with 67 more rows ``` --- - 총 14,854개 단어 ```r dic %>% mutate(sentiment = ifelse(polarity >= 1, "pos", ifelse(polarity <= -1, "neg", "neu"))) %>% count(sentiment) ``` ``` ## # A tibble: 3 x 2 ## sentiment n ## <chr> <int> ## 1 neg 9829 ## 2 neu 154 ## 3 pos 4871 ``` --- #### 문장의 감정 점수 구하기 ##### 1. 단어 기준으로 토큰화하기 ```r df <- tibble(sentence = c("디자인 예쁘고 마감도 좋아서 만족스럽다.", "디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다.")) df ``` ``` ## # A tibble: 2 x 1 ## sentence ## <chr> ## 1 디자인 예쁘고 마감도 좋아서 만족스럽다. ## 2 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. ``` --- - 텍스트를 단어 기준으로 토큰화: 감정 사전과 동일하게 - `unnest_tokens(drop = F)` - 원문 제거하지 않기 - 단어가 어느 문장에서 추출됐는지 알수 있도록 ```r library(tidytext) df <- df %>% unnest_tokens(input = sentence, output = word, token = "words", drop = F) df ``` --- ``` ## # A tibble: 12 x 2 ## sentence word ## <chr> <chr> ## 1 디자인 예쁘고 마감도 좋아서 만족스럽다. 디자인 ## 2 디자인 예쁘고 마감도 좋아서 만족스럽다. 예쁘고 ## 3 디자인 예쁘고 마감도 좋아서 만족스럽다. 마감도 ## 4 디자인 예쁘고 마감도 좋아서 만족스럽다. 좋아서 ## 5 디자인 예쁘고 마감도 좋아서 만족스럽다. 만족스럽다 ## 6 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 디자인은 ## 7 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 괜찮다 ## 8 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 그런데 ## 9 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 마감이 ## 10 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 나쁘고 ## 11 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 가격도 ## 12 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 비싸다 ``` --- #### 단어에 감정 점수 부여하기 - `dplyr::left_join()`: `word` 기준 감정 사전 결합 - 감정 사전에 없는 단어 `polarity` `NA` → `0` 부여 ```r df <- df %>% left_join(dic, by = "word") %>% mutate(polarity = ifelse(is.na(polarity), 0, polarity)) df ``` --- ``` ## # A tibble: 12 x 3 ## sentence word polarity ## <chr> <chr> <dbl> ## 1 디자인 예쁘고 마감도 좋아서 만족스럽다. 디자인 0 ## 2 디자인 예쁘고 마감도 좋아서 만족스럽다. 예쁘고 2 ## 3 디자인 예쁘고 마감도 좋아서 만족스럽다. 마감도 0 ## 4 디자인 예쁘고 마감도 좋아서 만족스럽다. 좋아서 2 ## 5 디자인 예쁘고 마감도 좋아서 만족스럽다. 만족스럽다 2 ## 6 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 디자인은 0 ## 7 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 괜찮다 1 ## 8 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 그런데 0 ## 9 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 마감이 0 ## 10 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 나쁘고 -2 ## 11 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 가격도 0 ## 12 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. 비싸다 -2 ``` --- ##### 3. 문장별로 감정 점수 합산하기 ```r score_df <- df %>% group_by(sentence) %>% summarise(score = sum(polarity)) score_df ``` ``` ## # A tibble: 2 x 2 ## sentence score ## <chr> <dbl> ## 1 디자인 예쁘고 마감도 좋아서 만족스럽다. 6 ## 2 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다. -3 ``` --- name: 04-2 class: title1 04-2 댓글 감정 분석하기 --- #### 영화 '기생충' 아카데미상 수상 관련 기사 댓글 - `"news_comment_parasite.csv"` - 2020년 2월 10일 영화 '기생충' 아카데미상 수상 소식 다룬 기사에 달린 댓글 ##### 기본적인 전처리 - 고유 번호 변수 만들기: 댓글 내용 같아도 구별할 수 있도록 - html 특수 문자 제거하기: 웹에서 만들어진 텍스트는 html 특수 문자 포함되어 내용 알아보기 불편 - `textclean::replace_html()` : html 태그를 공백으로 바꾸기 - `stringr::str_squish()`: 중복 공백 제거 - 특수 문자와 두 글자 미만 단어 포함하기: - 감정 사전의 특수 문자, 모음, 자음으로 된 두 글자 미만의 이모티콘 활용 --- ```r # 데이터 불러오기 raw_news_comment <- read_csv("news_comment_parasite.csv") # 기본적인 전처리 install.packages("textclean") library(textclean) news_comment <- raw_news_comment %>% mutate(id = row_number(), reply = str_squish(replace_html(reply))) # 데이터 구조 확인 glimpse(news_comment) ``` ``` ## Rows: 4,150 ## Columns: 6 ## $ reg_time <dttm> 2020-02-10 16:59:02, 2020-02-10 13:32:24, 2020-02-10 12:30:0~ ## $ reply <chr> "정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일~ ## $ press <chr> "MBC", "SBS", "한겨레", "한겨레", "한겨레", "한겨레", "한겨레~ ## $ title <chr> "'기생충' 아카데미 작품상까지 4관왕…영화사 새로 썼다", "[영~ ## $ url <chr> "https://news.naver.com/main/read.nhn?mode=LSD&mid=sec&sid1=1~ ## $ id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18~ ``` --- #### 단어 기준으로 토큰화하고 감정 점수 부여하기 ```r # 토큰화 word_comment <- news_comment %>% unnest_tokens(input = reply, output = word, token = "words", drop = F) word_comment %>% select(word, reply) ``` --- ``` ## # A tibble: 37,718 x 2 ## word reply ## <chr> <chr> ## 1 정말 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 2 우리 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 3 집에 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 4 좋은 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 5 일이 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 6 생겨 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 7 기쁘고 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 8 행복한 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 9 것처럼 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## 10 나의 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행~ ## # ... with 37,708 more rows ``` --- ```r # 감정 점수 부여 word_comment <- word_comment %>% left_join(dic, by = "word") %>% mutate(polarity = ifelse(is.na(polarity), 0, polarity)) word_comment %>% select(word, polarity) ``` ``` ## # A tibble: 37,718 x 2 ## word polarity ## <chr> <dbl> ## 1 정말 0 ## 2 우리 0 ## 3 집에 0 ## 4 좋은 2 ## 5 일이 0 ## 6 생겨 0 ## 7 기쁘고 2 ## 8 행복한 2 ## 9 것처럼 0 ## 10 나의 0 ## # ... with 37,708 more rows ``` --- #### 자주 사용된 감정 단어 살펴보기 ##### 1. 감정 분류하기 ```r word_comment <- word_comment %>% mutate(sentiment = ifelse(polarity == 2, "pos", ifelse(polarity == -2, "neg", "neu"))) word_comment %>% count(sentiment) ``` ``` ## # A tibble: 3 x 2 ## sentiment n ## <chr> <int> ## 1 neg 285 ## 2 neu 36671 ## 3 pos 762 ``` --- #### 2. 막대 그래프 만들기 ```r top10_sentiment <- word_comment %>% filter(sentiment != "neu") %>% count(sentiment, word) %>% group_by(sentiment) %>% slice_max(n, n = 10) top10_sentiment ``` --- ``` ## # A tibble: 22 x 3 ## # Groups: sentiment [2] ## sentiment word n ## <chr> <chr> <int> ## 1 neg 소름 56 ## 2 neg 소름이 16 ## 3 neg 아니다 15 ## 4 neg 우울한 9 ## 5 neg 해 8 ## 6 neg 미친 7 ## 7 neg 가난한 5 ## 8 neg 어려운 5 ## 9 neg 힘든 5 ## 10 neg 더러운 4 ## 11 neg 못하고 4 ## 12 neg 싫다 4 ## 13 pos 대단하다 89 ## 14 pos 자랑스럽다 81 ## 15 pos 축하 65 ## 16 pos 멋지다 56 ## 17 pos 대단한 51 ## 18 pos 좋은 45 ## 19 pos 최고 30 ## 20 pos 최고의 24 ## 21 pos 위대한 23 ## 22 pos 대단하고 21 ``` --- ```r # 막대 그래프 만들기 library(ggplot2) ggplot(top10_sentiment, aes(x = reorder(word, n), y = n, fill = sentiment)) + geom_col() + coord_flip() + geom_text(aes(label = n), hjust = -0.3) + facet_wrap(~ sentiment, scales = "free") + scale_y_continuous(expand = expansion(mult = c(0.05, 0.15))) + labs(x = NULL) + theme(text = element_text(family = "nanumgothic")) ``` <svg viewBox="0 0 352 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M176 80c-52.94 0-96 43.06-96 96 0 8.84 7.16 16 16 16s16-7.16 16-16c0-35.3 28.72-64 64-64 8.84 0 16-7.16 16-16s-7.16-16-16-16zM96.06 459.17c0 3.15.93 6.22 2.68 8.84l24.51 36.84c2.97 4.46 7.97 7.14 13.32 7.14h78.85c5.36 0 10.36-2.68 13.32-7.14l24.51-36.84c1.74-2.62 2.67-5.7 2.68-8.84l.05-43.18H96.02l.04 43.18zM176 0C73.72 0 0 82.97 0 176c0 44.37 16.45 84.85 43.56 115.78 16.64 18.99 42.74 58.8 52.42 92.16v.06h48v-.12c-.01-4.77-.72-9.51-2.15-14.07-5.59-17.81-22.82-64.77-62.17-109.67-20.54-23.43-31.52-53.15-31.61-84.14-.2-73.64 59.67-128 127.95-128 70.58 0 128 57.42 128 128 0 30.97-11.24 60.85-31.65 84.14-39.11 44.61-56.42 91.47-62.1 109.46a47.507 47.507 0 0 0-2.22 14.3v.1h48v-.05c9.68-33.37 35.78-73.18 52.42-92.16C335.55 260.85 352 220.37 352 176 352 78.8 273.2 0 176 0z"></path></svg> `scale_y_continuous()`: 막대-그래프 간격 넓히기. 막대 끝의 빈도 값이 그래프 경계 밖으로 벗어나지 <br> 않도록 설정. --- ![](04-sentimentAnalysis_files/figure-html/p-top10_sentiment-1.png)<!-- --> --- #### 댓글별 감정 점수 구하고 댓글 살펴보기 ##### 1. 댓글별 감정 점수 구하기 - `id`, `reply`별로 분리한 다음 `polarity` 합산 - `id`로 먼저 나누는 이유: - 내용이 같은 댓글이 여럿 있더라도 서로 다른 댓글로 취급 - `id`별로 먼저 나누지 않으면 내용 같은 댓글 점수 모두 하나로 합산 ```r score_comment <- word_comment %>% group_by(id, reply) %>% summarise(score = sum(polarity)) %>% ungroup() score_comment %>% select(score, reply) ``` --- ``` ## # A tibble: 4,140 x 2 ## score reply ## <dbl> <chr> ## 1 6 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행복~ ## 2 6 와 너무 기쁘다! 이 시국에 정말 내 일같이 기쁘고 감사하다!!! 축하드려요~ ## 3 4 우리나라의 영화감독분들 그리고 앞으로 그 꿈을 그리는 분들에게 큰 영감~ ## 4 3 봉준호 감독과 우리나라 대한민국 모두 자랑스럽다. 세계 어디를 가고 우리~ ## 5 0 노벨상 탄느낌이네요 축하축하 합니다 ## 6 0 기생충 상 받을때 박수 쳤어요.감독상도 기대해요.봉준호 감독 화이팅^^ ## 7 0 대한민국 영화사를 새로 쓰고 계시네요 ㅊㅊㅊ ## 8 0 저런게 아카데미상 받으면 '태극기 휘날리며'' '광해' '명량''은 전부문 휩~ ## 9 0 다시한번 보여주세요 영화관에서 보고싶은디 ## 10 2 대한민국 BTS와함께 봉준호감독님까지 대단하고 한국의 문화에 자긍심을 가~ ## # ... with 4,130 more rows ``` --- ##### 2. 감정 점수 높은 댓글 살펴보기 ```r # 긍정 댓글 score_comment %>% select(score, reply) %>% arrange(-score) ``` ``` ## # A tibble: 4,140 x 2 ## score reply ## <dbl> <chr> ## 1 11 아니 다른상을 받은것도 충분히 대단하고 굉장하지만 최고의 영예인 작품상~ ## 2 9 봉준호의 위대한 업적은 진보 영화계의 위대한 업적이고 대한민국의 업적입~ ## 3 7 이 수상소식을 듣고 억수로 기뻐하는 가족이 있을것 같다. SNS를 통해 자기~ ## 4 7 감사 감사 감사 수상 소감도 3관왕 답네요 ## 5 6 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행복~ ## 6 6 와 너무 기쁘다! 이 시국에 정말 내 일같이 기쁘고 감사하다!!! 축하드려요~ ## 7 6 축하 축하 축하 모두들 수고 하셨어요 기생충 화이팅 ## 8 6 축하!!!! 축하!!!!! 오스카의 정복은 무엇보다 시나리오의 힘이다. 작가의 ~ ## 9 6 조여정 ㆍ예쁜얼굴때문에 연기력을 제대로 평가받지 못해 안타깝던 내가 좋~ ## 10 6 좋은 걸 좋다고 말하지 못하는 인간들이 참 불쌍해지네....댓글 보니 인생~ ## # ... with 4,130 more rows ``` --- ##### 2. 감정 점수 높은 댓글 살펴보기 ```r # 부정 댓글 score_comment %>% select(score, reply) %>% arrange(score) ``` ``` ## # A tibble: 4,140 x 2 ## score reply ## <dbl> <chr> ## 1 -7 기생충 영화 한국인 으로써 싫다 대단히 싫다!! 가난한 서민들의 마지막 자~ ## 2 -6 이 페미민국이 잘 되는 게 아주 싫다. 최악의 나쁜일들과 불운, 불행, 어둡~ ## 3 -5 특정 인물의 성공을 국가의 부흥으로 연관짓는 것은 미개한 발상이다. 봉준~ ## 4 -4 좌파들이 나라 망신 다 시킨다..ㅠ 설레발 오지게 치더니..꼴랑 각본상 하~ ## 5 -4 부패한 386 민주화 세대 정권의 무분별한 포퓰리즘으로 탄생한 좀비들의 살~ ## 6 -4 기생충 내용은 좋은데 제목이 그래요. 극 중 송강호가족이 부잣집에 대해서~ ## 7 -4 이런 감독과 이런 배우보고 좌좀 이라고 지1랄하던 그분들 다 어디계시냐? ~ ## 8 -4 축하합니다. 근데 현실 세계인 한국에선 그보다 훨씬 나쁜 넘인 조로남불 ~ ## 9 -4 큰일이다....국제적 망신이다...전 세계사람들이 우리나라를 기생충으로 보~ ## 10 -4 더럽고 추잡한 그들만의 리그 ## # ... with 4,130 more rows ``` --- #### 감정 경향 살펴보기 ##### 1. 감정 점수 빈도 구하기 .pull-left[ ```r score_comment %>% count(score) ``` ] .pull-right[ ``` ## # A tibble: 17 x 2 ## score n ## <dbl> <int> ## 1 -7 1 ## 2 -6 1 ## 3 -5 1 ## 4 -4 17 ## 5 -3 35 ## 6 -2 175 ## 7 -1 206 ## 8 0 2897 ## 9 1 222 ## 10 2 432 ## 11 3 57 ## 12 4 71 ## 13 5 7 ## 14 6 14 ## 15 7 2 ## 16 9 1 ## 17 11 1 ``` ] --- ##### 2. 감정 분류하고 막대 그래프 만들기 ```r # 감정 분류하기 score_comment <- score_comment %>% mutate(sentiment = ifelse(score >= 1, "pos", ifelse(score <= -1, "neg", "neu"))) ``` ```r # 감정 빈도와 비율 구하기 frequency_score <- score_comment %>% count(sentiment) %>% mutate(ratio = n/sum(n)*100) frequency_score ``` ``` ## # A tibble: 3 x 3 ## sentiment n ratio ## <chr> <int> <dbl> ## 1 neg 436 10.5 ## 2 neu 2897 70.0 ## 3 pos 807 19.5 ``` --- ```r # 막대 그래프 만들기 ggplot(frequency_score, aes(x = sentiment, y = n, fill = sentiment)) + geom_col() + geom_text(aes(label = n), vjust = -0.3) + scale_x_discrete(limits = c("pos", "neu", "neg")) ``` <svg viewBox="0 0 352 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M176 80c-52.94 0-96 43.06-96 96 0 8.84 7.16 16 16 16s16-7.16 16-16c0-35.3 28.72-64 64-64 8.84 0 16-7.16 16-16s-7.16-16-16-16zM96.06 459.17c0 3.15.93 6.22 2.68 8.84l24.51 36.84c2.97 4.46 7.97 7.14 13.32 7.14h78.85c5.36 0 10.36-2.68 13.32-7.14l24.51-36.84c1.74-2.62 2.67-5.7 2.68-8.84l.05-43.18H96.02l.04 43.18zM176 0C73.72 0 0 82.97 0 176c0 44.37 16.45 84.85 43.56 115.78 16.64 18.99 42.74 58.8 52.42 92.16v.06h48v-.12c-.01-4.77-.72-9.51-2.15-14.07-5.59-17.81-22.82-64.77-62.17-109.67-20.54-23.43-31.52-53.15-31.61-84.14-.2-73.64 59.67-128 127.95-128 70.58 0 128 57.42 128 128 0 30.97-11.24 60.85-31.65 84.14-39.11 44.61-56.42 91.47-62.1 109.46a47.507 47.507 0 0 0-2.22 14.3v.1h48v-.05c9.68-33.37 35.78-73.18 52.42-92.16C335.55 260.85 352 220.37 352 176 352 78.8 273.2 0 176 0z"></path></svg> `scale_x_discrete()`: x축 순서 정하기 <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-30-1.png" width="70%" /> --- ##### 3. 비율 누적 막대 그래프 만들기 <br10> - 샘플 데이터로 비율 누적 막대 그래프 만들기 - 데이터에 x축, y축, 누적 막대를 표현할 변수 필요 ```r df <- tibble(contry = c("Korea", "Korea", "Japen", "Japen"), # 축 sex = c("M", "F", "M", "F"), # 누적 막대 ratio = c(60, 40, 30, 70)) # 값 df ``` ``` ## # A tibble: 4 x 3 ## contry sex ratio ## <chr> <chr> <dbl> ## 1 Korea M 60 ## 2 Korea F 40 ## 3 Japen M 30 ## 4 Japen F 70 ``` --- ```r ggplot(df, aes(x = contry, y = ratio, fill = sex)) + geom_col() ``` <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-32-1.png" width="70%" /> --- - `geom_text()`: 막대에 비율 표기 - `position_stack(vjust = 0.5)`: 비율을 막대의 가운데에 표시 ```r ggplot(df, aes(x = contry, y = ratio, fill = sex)) + geom_col() + geom_text(aes(label = paste0(ratio, "%")), # % 표시 position = position_stack(vjust = 0.5)) # 가운데 표시 ``` <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-33-1.png" width="70%" /> --- **댓글의 감정 비율로 누적 막대 그래프 만들기** - x축을 구성할 더미 변수(dummy variable) 추가 ```r # 더미 변수 생성 frequency_score$dummy <- 0 frequency_score ``` ``` ## # A tibble: 3 x 4 ## sentiment n ratio dummy ## <chr> <int> <dbl> <dbl> ## 1 neg 436 10.5 0 ## 2 neu 2897 70.0 0 ## 3 pos 807 19.5 0 ``` --- ```r ggplot(frequency_score, aes(x = dummy, y = ratio, fill = sentiment)) + geom_col() + geom_text(aes(label = paste0(round(ratio, 1), "%")), position = position_stack(vjust = 0.5)) + theme(axis.title.x = element_blank(), # x축 이름 삭제 axis.text.x = element_blank(), # x축 값 삭제 axis.ticks.x = element_blank()) # x축 눈금 삭제 ``` <img src="04-sentimentAnalysis_files/figure-html/p-frequency_score-ratio-1.png" width="40%" /> --- <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-35-1.png" width="60%" /> --- name: 04-3 class: title1 04-3 감정 범주별 주요 단어 살펴보기 --- #### 감정 범주별 단어 빈도 구하기 ##### 1. 토큰화하고 두 글자 이상 한글 단어만 남기기 ```r comment <- score_comment %>% unnest_tokens(input = reply, # 단어 기준 토큰화 output = word, token = "words", drop = F) %>% filter(str_detect(word, "[가-힣]") & # 한글 추출 str_count(word) >= 2) # 두 글자 이상 추출 ``` --- ##### 2. 감정 범주별 빈도 구하기 ```r # 감정 및 단어별 빈도 구하기 frequency_word <- comment %>% filter(str_count(word) >= 2) %>% count(sentiment, word, sort = T) frequency_word ``` ``` ## # A tibble: 19,223 x 3 ## sentiment word n ## <chr> <chr> <int> ## 1 neu 축하합니다 214 ## 2 neu 봉준호 203 ## 3 neu 기생충 164 ## 4 neu 축하드립니다 155 ## 5 neu 정말 146 ## 6 neu 대박 134 ## 7 neu 진짜 121 ## 8 pos 봉준호 106 ## 9 pos 정말 97 ## 10 neu 자랑스럽습니다 96 ## # ... with 19,213 more rows ``` --- .pull-left[ ```r # 긍정 댓글 고빈도 단어 frequency_word %>% filter(sentiment == "pos") ``` ``` ## # A tibble: 5,234 x 3 ## sentiment word n ## <chr> <chr> <int> ## 1 pos 봉준호 106 ## 2 pos 정말 97 ## 3 pos 대단하다 83 ## 4 pos 진짜 79 ## 5 pos 자랑스럽다 78 ## 6 pos 축하 63 ## 7 pos 대한민국 61 ## 8 pos 영화 58 ## 9 pos 멋지다 55 ## 10 pos 기생충 53 ## # ... with 5,224 more rows ``` ] .pull-right[ ```r # 부정 댓글 고빈도 단어 frequency_word %>% filter(sentiment == "neg") ``` ``` ## # A tibble: 4,080 x 3 ## sentiment word n ## <chr> <chr> <int> ## 1 neg 소름 49 ## 2 neg 봉준호 47 ## 3 neg 기생충 33 ## 4 neg 이런 33 ## 5 neg 정말 32 ## 6 neg 진짜 26 ## 7 neg 좌빨 21 ## 8 neg 너무 20 ## 9 neg 블랙리스트에 19 ## 10 neg 영화 18 ## # ... with 4,070 more rows ``` ] --- #### 상대적으로 자주 사용된 단어 비교하기 ##### 1. 로그 오즈비 구하기 .pull-left[ ```r # wide form으로 변환 library(tidyr) comment_wide <- frequency_word %>% filter(sentiment != "neu") %>% pivot_wider(names_from = sentiment, values_from = n, values_fill = list(n = 0)) comment_wide ``` ] .pull-right[ ``` ## # A tibble: 8,380 x 3 ## word pos neg ## <chr> <int> <int> ## 1 봉준호 106 47 ## 2 정말 97 32 ## 3 대단하다 83 1 ## 4 진짜 79 26 ## 5 자랑스럽다 78 1 ## 6 축하 63 0 ## 7 대한민국 61 4 ## 8 영화 58 18 ## 9 멋지다 55 0 ## 10 기생충 53 33 ## # ... with 8,370 more rows ``` ] --- ```r # 로그 오즈비 구하기 comment_wide <- comment_wide %>% mutate(log_odds_ratio = log(((pos + 1) / (sum(pos + 1))) / ((neg + 1) / (sum(neg + 1))))) comment_wide ``` ``` ## # A tibble: 8,380 x 4 ## word pos neg log_odds_ratio ## <chr> <int> <int> <dbl> ## 1 봉준호 106 47 0.589 ## 2 정말 97 32 0.876 ## 3 대단하다 83 1 3.52 ## 4 진짜 79 26 0.873 ## 5 자랑스럽다 78 1 3.46 ## 6 축하 63 0 3.95 ## 7 대한민국 61 4 2.30 ## 8 영화 58 18 0.920 ## 9 멋지다 55 0 3.81 ## 10 기생충 53 33 0.250 ## # ... with 8,370 more rows ``` --- ##### 2. 로그 오즈비가 가장 큰 단어 10개씩 추출하기 ```r top10 <- comment_wide %>% group_by(sentiment = ifelse(log_odds_ratio > 0, "pos", "neg")) %>% slice_max(abs(log_odds_ratio), n = 10, with_ties = F) top10 ``` <svg viewBox="0 0 352 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M176 80c-52.94 0-96 43.06-96 96 0 8.84 7.16 16 16 16s16-7.16 16-16c0-35.3 28.72-64 64-64 8.84 0 16-7.16 16-16s-7.16-16-16-16zM96.06 459.17c0 3.15.93 6.22 2.68 8.84l24.51 36.84c2.97 4.46 7.97 7.14 13.32 7.14h78.85c5.36 0 10.36-2.68 13.32-7.14l24.51-36.84c1.74-2.62 2.67-5.7 2.68-8.84l.05-43.18H96.02l.04 43.18zM176 0C73.72 0 0 82.97 0 176c0 44.37 16.45 84.85 43.56 115.78 16.64 18.99 42.74 58.8 52.42 92.16v.06h48v-.12c-.01-4.77-.72-9.51-2.15-14.07-5.59-17.81-22.82-64.77-62.17-109.67-20.54-23.43-31.52-53.15-31.61-84.14-.2-73.64 59.67-128 127.95-128 70.58 0 128 57.42 128 128 0 30.97-11.24 60.85-31.65 84.14-39.11 44.61-56.42 91.47-62.1 109.46a47.507 47.507 0 0 0-2.22 14.3v.1h48v-.05c9.68-33.37 35.78-73.18 52.42-92.16C335.55 260.85 352 220.37 352 176 352 78.8 273.2 0 176 0z"></path></svg> 로그 오즈비 동점 단어 제외 --- ``` ## # A tibble: 20 x 5 ## # Groups: sentiment [2] ## word pos neg log_odds_ratio sentiment ## <chr> <int> <int> <dbl> <chr> ## 1 소름 2 49 -3.03 neg ## 2 좌빨 1 21 -2.61 neg ## 3 못한 0 7 -2.29 neg ## 4 미친 0 7 -2.29 neg ## 5 좌좀 0 6 -2.16 neg ## 6 소름이 1 12 -2.08 neg ## 7 가난한 0 5 -2.00 neg ## 8 모르는 0 5 -2.00 neg ## 9 아쉽다 0 5 -2.00 neg ## 10 닭그네 0 4 -1.82 neg ## 11 축하 63 0 3.95 pos ## 12 멋지다 55 0 3.81 pos ## 13 대단한 47 0 3.66 pos ## 14 좋은 42 0 3.55 pos ## 15 대단하다 83 1 3.52 pos ## 16 자랑스럽다 78 1 3.46 pos ## 17 최고 27 0 3.12 pos ## 18 세계적인 24 0 3.01 pos ## 19 최고의 23 0 2.97 pos ## 20 위대한 22 0 2.92 pos ``` --- ##### 3. 막대 그래프 만들기 ```r # 막대 그래프 만들기 ggplot(top10, aes(x = reorder(word, log_odds_ratio), y = log_odds_ratio, fill = sentiment)) + geom_col() + coord_flip() + labs(x = NULL) + theme(text = element_text(family = "nanumgothic")) ``` <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-42-1.png" width="50%" /> --- <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-43-1.png" width="70%" /> --- name: 04-4 class: title1 04-4 감정 사전 수정하기 --- #### 감정 단어가 사용된 원문 살펴보기 - `"소름"`, `"미친"`: 긍정적인 감정을 극적으로 표현한 단어 ```r # "소름"이 사용된 댓글 score_comment %>% filter(str_detect(reply, "소름")) %>% select(reply) ``` ``` ## # A tibble: 131 x 1 ## reply ## <chr> ## 1 소름돋네요 ## 2 와..진짜소름 저 소리처음질렀어요 눈물나요.. ㅠㅠ ## 3 생중계 보며 봉준호 할 때 소름이~~~!! ㅠㅠ 수상소감들으며 함께 가슴이 벅차네~ ## 4 와 보다가 소름 짝 수고들하셨어요 ## 5 한국어 소감 듣는데 소름돋네 축하드립니다 ## 6 대단하다!! 봉준호 이름 나오자마자 소름 ## 7 와우 브라보~ 키아누리브스의 봉준호, 순간 소름이.. 멋지십니다. ## 8 소름 돋네요. 축하합니다 ## 9 소름.... 기생충 각본집 산거 다시한번 잘했다는 생각이ㅠㅠㅠ 축하해요!!!!!! ## 10 소름끼쳤어요 너무 멋집니다 ^^!!!! ## # ... with 121 more rows ``` --- #### 감정 단어가 사용된 원문 살펴보기 - `"소름"`, `"미친"`: 긍정적인 감정을 극적으로 표현한 단어 ```r # "미친"이 사용된 댓글 score_comment %>% filter(str_detect(reply, "미친")) %>% select(reply) ``` ``` ## # A tibble: 15 x 1 ## reply ## <chr> ## 1 와 3관왕 미친 ## 2 미친거야 이건~~ ## 3 Korea 대단합니다 김연아 방탄 봉준호 스포츠 음악 영화 못하는게 없어요 좌빨 감~ ## 4 청룡영화제에서 다른나라가 상을 휩쓴거죠? 와..미쳤다 미국영화제에서 한국이 빅~ ## 5 설마했는데 감독상, 작품상, 각본상을 죄다 휩쓸어버릴 줄이야. 이건 미친 꿈이야~ ## 6 완전 완전...미친촌재감~이런게 바로 애국이지~ 존경합니다~ ## 7 이세상엔 참 미 친 인간들이 많다는걸 댓글에서 다시한번 느낀다..모두가 축하해~ ## 8 올해 아카데미 최다 수상작이기도 하다 이건 진짜 미친사건이다 ## 9 CJ회장이 저기서 왜 언급되는지... 미친 부회장.. 공과사 구분 못하는 정권의 홍~ ## 10 미친봉 ## # ... with 5 more rows ``` --- - `"소름"`, `"미친"`이 감정 사전에 부정적인 단어로 분류되어 있어서 생긴 문제 - 텍스트의 맥락이 감정 사전의 맥락과 다르면 반대되는 감정 점수 부여 - 감정 사전 수정 필요 ```r dic %>% filter(word %in% c("소름", "소름이", "미친")) ``` ``` ## # A tibble: 3 x 2 ## word polarity ## <chr> <dbl> ## 1 소름이 -2 ## 2 소름 -2 ## 3 미친 -2 ``` --- #### 감정 사전 수정하기 - `"소름이"`, `"소름"`, `"미친"`의 `polarity`를 양수 2로 수정 ```r new_dic <- dic %>% mutate(polarity = ifelse(word %in% c("소름", "소름이", "미친"), 2, polarity)) new_dic %>% filter(word %in% c("소름", "소름이", "미친")) ``` ``` ## # A tibble: 3 x 2 ## word polarity ## <chr> <dbl> ## 1 소름이 2 ## 2 소름 2 ## 3 미친 2 ``` --- #### 수정한 사전으로 감정 점수 부여하기 ```r new_word_comment <- word_comment %>% select(-polarity) %>% left_join(new_dic, by = "word") %>% mutate(polarity = ifelse(is.na(polarity), 0, polarity)) ``` --- #### 댓글별 감정 점수 구하기 <br-back-10> ```r new_score_comment <- new_word_comment %>% group_by(id, reply) %>% summarise(score = sum(polarity)) %>% ungroup() new_score_comment %>% select(score, reply) %>% arrange(-score) ``` ``` ## # A tibble: 4,140 x 2 ## score reply ## <dbl> <chr> ## 1 11 아니 다른상을 받은것도 충분히 대단하고 굉장하지만 최고의 영예인 작품상~ ## 2 9 봉준호의 위대한 업적은 진보 영화계의 위대한 업적이고 대한민국의 업적입~ ## 3 8 소름 소름 진짜 멋지다 대단하다 ## 4 7 이 수상소식을 듣고 억수로 기뻐하는 가족이 있을것 같다. SNS를 통해 자기~ ## 5 7 감사 감사 감사 수상 소감도 3관왕 답네요 ## 6 6 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행복~ ## 7 6 와 너무 기쁘다! 이 시국에 정말 내 일같이 기쁘고 감사하다!!! 축하드려요~ ## 8 6 축하 축하 축하 모두들 수고 하셨어요 기생충 화이팅 ## 9 6 생중계 보며 봉준호 할 때 소름이~~~!! ㅠㅠ 수상소감들으며 함께 가슴이 ~ ## 10 6 축하!!!! 축하!!!!! 오스카의 정복은 무엇보다 시나리오의 힘이다. 작가의 ~ ## # ... with 4,130 more rows ``` <svg viewBox="0 0 352 512" style="height:1em;position:relative;display:inline-block;top:.1em;" xmlns="http://www.w3.org/2000/svg"> <path d="M176 80c-52.94 0-96 43.06-96 96 0 8.84 7.16 16 16 16s16-7.16 16-16c0-35.3 28.72-64 64-64 8.84 0 16-7.16 16-16s-7.16-16-16-16zM96.06 459.17c0 3.15.93 6.22 2.68 8.84l24.51 36.84c2.97 4.46 7.97 7.14 13.32 7.14h78.85c5.36 0 10.36-2.68 13.32-7.14l24.51-36.84c1.74-2.62 2.67-5.7 2.68-8.84l.05-43.18H96.02l.04 43.18zM176 0C73.72 0 0 82.97 0 176c0 44.37 16.45 84.85 43.56 115.78 16.64 18.99 42.74 58.8 52.42 92.16v.06h48v-.12c-.01-4.77-.72-9.51-2.15-14.07-5.59-17.81-22.82-64.77-62.17-109.67-20.54-23.43-31.52-53.15-31.61-84.14-.2-73.64 59.67-128 127.95-128 70.58 0 128 57.42 128 128 0 30.97-11.24 60.85-31.65 84.14-39.11 44.61-56.42 91.47-62.1 109.46a47.507 47.507 0 0 0-2.22 14.3v.1h48v-.05c9.68-33.37 35.78-73.18 52.42-92.16C335.55 260.85 352 220.37 352 176 352 78.8 273.2 0 176 0z"></path></svg> `ungroup()`: 이후 분석 작업은 그룹별로 처리하지 않도록 그룹 해제 --- #### 전반적인 감정 경향 살펴보기 ##### 1. 감정 분류하기 ```r # 1점 기준으로 긍정 중립 부정 분류 new_score_comment <- new_score_comment %>% mutate(sentiment = ifelse(score >= 1, "pos", ifelse(score <= -1, "neg", "neu"))) ``` --- ##### 2. 감정 범주별 빈도와 비율 구하기 .pull-left[ ```r # 원본 감정 사전 활용 *score_comment %>% count(sentiment) %>% mutate(ratio = n/sum(n)*100) ``` ``` ## # A tibble: 3 x 3 ## sentiment n ratio ## <chr> <int> <dbl> ## 1 neg 436 10.5 ## 2 neu 2897 70.0 ## 3 pos 807 19.5 ``` ] .pull-right[ ```r # 수정한 감정 사전 활용 *new_score_comment %>% count(sentiment) %>% mutate(ratio = n/sum(n)*100) ``` ``` ## # A tibble: 3 x 3 ## sentiment n ratio ## <chr> <int> <dbl> ## 1 neg 368 8.89 ## 2 neu 2890 69.8 ## 3 pos 882 21.3 ``` ] --- ##### 3. 분석 결과 비교하기 ```r word <- "소름|소름이|미친" ``` .pull-left[ ```r # 원본 감정 사전 활용 *score_comment %>% filter(str_detect(reply, word)) %>% count(sentiment) ``` ``` ## # A tibble: 3 x 2 ## sentiment n ## <chr> <int> ## 1 neg 73 ## 2 neu 63 ## 3 pos 9 ``` ] .pull-right[ ```r # 수정한 감정 사전 활용 *new_score_comment %>% filter(str_detect(reply, word)) %>% count(sentiment) ``` ``` ## # A tibble: 3 x 2 ## sentiment n ## <chr> <int> ## 1 neg 5 ## 2 neu 56 ## 3 pos 84 ``` ] <svg viewBox="0 0 576 512" style="height:1em;position:relative;display:inline-block;top:.1em;fill:#FF7333;" xmlns="http://www.w3.org/2000/svg"> <path d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path></svg> `str_detect()`에 여러 문자를 입력할 때는 `|`로 문자를 구분 --- #### 감정 범주별 주요 단어 살펴보기 ##### 1. 두 글자 이상 한글 단어만 남기고 단어 빈도 구하기 ```r # 토큰화 및 전처리 new_comment <- new_score_comment %>% unnest_tokens(input = reply, output = word, token = "words", drop = F) %>% filter(str_detect(word, "[가-힣]") & str_count(word) >= 2) # 감정 및 단어별 빈도 구하기 new_frequency_word <- new_comment %>% count(sentiment, word, sort = T) ``` --- ##### 2. 로그 오즈비 구하기 ```r # Wide form으로 변환 new_comment_wide <- new_frequency_word %>% filter(sentiment != "neu") %>% pivot_wider(names_from = sentiment, values_from = n, values_fill = list(n = 0)) # 로그 오즈비 구하기 new_comment_wide <- new_comment_wide %>% mutate(log_odds_ratio = log(((pos + 1) / (sum(pos + 1))) / ((neg + 1) / (sum(neg + 1))))) ``` --- ##### 3. 로그 오즈비가 큰 단어로 막대 그래프 만들기 ```r new_top10 <- new_comment_wide %>% group_by(sentiment = ifelse(log_odds_ratio > 0, "pos", "neg")) %>% slice_max(abs(log_odds_ratio), n = 10, with_ties = F) ggplot(new_top10, aes(x = reorder(word, log_odds_ratio), y = log_odds_ratio, fill = sentiment)) + geom_col() + coord_flip() + labs(x = NULL) + theme(text = element_text(family = "nanumgothic")) ``` <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-58-1.png" width="38%" /> --- <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-59-1.png" width="80%" /> --- ##### 4. 주요 단어가 사용된 댓글 살펴보기 ##### 긍정 댓글 원문 ```r new_score_comment %>% filter(sentiment == "pos" & str_detect(reply, "축하")) %>% select(reply) ``` ``` ## # A tibble: 189 x 1 ## reply ## <chr> ## 1 정말 우리 집에 좋은 일이 생겨 기쁘고 행복한 것처럼!! 나의 일인 양 행복합니다~ ## 2 와 너무 기쁘다! 이 시국에 정말 내 일같이 기쁘고 감사하다!!! 축하드려요 진심~ ## 3 우리나라의 영화감독분들 그리고 앞으로 그 꿈을 그리는 분들에게 큰 영감을 주시~ ## 4 아카데미나 다른 상이나 지들만의 잔치지~ 난 대한민국에서 받는 상이 제일 가치 ~ ## 5 정부에 빨대 꼽은 정치시민단체 기생충들이 득실거리는 떼한민국애서 훌륭한 영화~ ## 6 대단해요 나는 안봤는데 그렇게 잘 만들어 한국인의 기백을 세계에 알리는 큰 일~ ## 7 나한테 돌아오는게 하나도 없는데 왜이렇게 자랑스럽지?ㅎㅎㅎ 축하 합니다~작품~ ## 8 한국영화 100년사에 한횟을 긋네요. 정말 축하 합니다 ## 9 와 대단하다 진짜 축하드려요!!! 대박 진짜 ## 10 각본상, 국제 영화상 수상 축하. 편집상은 꽝남. ## # ... with 179 more rows ``` --- ##### 4. 주요 단어가 사용된 댓글 살펴보기 ##### 긍정 댓글 원문 ```r new_score_comment %>% filter(sentiment == "pos" & str_detect(reply, "소름")) %>% select(reply) ``` ``` ## # A tibble: 77 x 1 ## reply ## <chr> ## 1 생중계 보며 봉준호 할 때 소름이~~~!! ㅠㅠ 수상소감들으며 함께 가슴이 벅차네~ ## 2 와 보다가 소름 짝 수고들하셨어요 ## 3 대단하다!! 봉준호 이름 나오자마자 소름 ## 4 와우 브라보~ 키아누리브스의 봉준호, 순간 소름이.. 멋지십니다. ## 5 소름 돋네요. 축하합니다 ## 6 소름.... 기생충 각본집 산거 다시한번 잘했다는 생각이ㅠㅠㅠ 축하해요!!!!!! ## 7 봉준호 아저씨 우리나라 자랑입니다 헐리웃 배우들과 화면에 같이 비춰지는게 아~ ## 8 추카해요. 봉준호하는데 막 완전 소름 돋았어요. ## 9 소름돋아서 닭살돋고.. 그냥 막 감동이라 눈물이 나려했어요.. 대단하고 자랑스럽~ ## 10 한국 영화 최초 아카데미상 수상, 92년 역사의 국제 장편 영화상과 최우수작품상 ~ ## # ... with 67 more rows ``` --- ##### 4. 주요 단어가 사용된 댓글 살펴보기 ##### 부정 댓글 원문 ```r new_score_comment %>% filter(sentiment == "neg" & str_detect(reply, "좌빨")) %>% select(reply) ``` ``` ## # A tibble: 34 x 1 ## reply ## <chr> ## 1 자칭 보수들은 분노의 타이핑중 ㅋㅋㅋㅋㅋㅋ전세계를 좌빨로 몰수는 없고 자존심~ ## 2 자칭보수 왈 : 미국에 로비했다 ㅋㅋ좌빨영화가 상받을리 없다 ㅋㅋㅋㅋㅋㅋㅋ 본~ ## 3 좌빨 봉준호 영화는 쳐다도 안본다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ ## 4 봉준호 그렇게 미국 싫어하는데 상은 쳐 받으러 가는 좌빨 수준ㅋㅋㅋ ## 5 좌빨 기생충들은 댓글도 달지마라 미국 영화제 수상이 니들하고 뭔상관인데. ## 6 얘들은 왜 인정을 안하냐? ㅋㅋ 니들 이미 변호인 찍을대 부터 송강호 욕해대고 ~ ## 7 넷상 보수들 만큼 이중적인 새1끼들 없음. 봉준호 송강호 보고 종북좌빨 홍어드립~ ## 8 우선 축하합니다.그리고 다음에는 조씨가족을 모델로한 뻔뻔하고 거짓말을 밥 먹~ ## 9 Korea 대단합니다 김연아 방탄 봉준호 스포츠 음악 영화 못하는게 없어요 좌빨 감~ ## 10 좌빨 감독이라고 블랙리스트에 올랐던 사람을 세계인이 인정해주네. 방구석에 앉~ ## # ... with 24 more rows ``` --- ##### 4. 주요 단어가 사용된 댓글 살펴보기 ##### 부정 댓글 원문 ```r new_score_comment %>% filter(sentiment == "neg" & str_detect(reply, "못한")) %>% select(reply) ``` ``` ## # A tibble: 7 x 1 ## reply ## <chr> ## 1 한번도경험하지. 못한 조국가족사기단기생충. 개봉박두 ## 2 여기서 정치얘기하는 건 학창시절 공부 못한 거 인증하는 꼴... 주제좀 벗어나지 ~ ## 3 이 기사를 반문으로 먹고 사는 자유왜국당과, mb아바타 간철수 댓글알바들이 매우 ~ ## 4 한국미국일본 vs 주적북한,중국러시아 이 구도인 현 시대 상황 속에서, 미국 일본~ ## 5 친일수꼴 들과 자한당넘들이 나라에 경사만 있으면 엄청 싫어합니다, 맨날 사고만 ~ ## 6 각본상,국제상,감독상 ...어디서 듣도보도 못한 아차상 같은 쩌리처리용 상 아닌가~ ## 7 난 밥을 먹고 기생충은 오스카를 먹다, 기생충은 대한민국의 국격을 높였는데 난 ~ ``` --- ##### 5. 분석 결과 비교하기 .pull-left[ ```r # 수정한 감정 사전 활용 *new_top10 %>% select(-pos, -neg) %>% arrange(-log_odds_ratio) ``` ] .pull-right[ ```r # 원본 감정 사전 활용 *top10 %>% select(-pos, -neg) %>% arrange(-log_odds_ratio) ``` ] --- .pull-left[ ``` ## # A tibble: 20 x 3 ## # Groups: sentiment [2] ## word log_odds_ratio sentiment ## <chr> <dbl> <chr> ## 1 축하 3.88 pos ## 2 멋지다 3.76 pos *## 3 소름 3.76 pos ## 4 대단한 3.59 pos ## 5 대단하다 3.49 pos ## 6 좋은 3.48 pos ## 7 자랑스럽다 3.40 pos ## 8 최고 3.09 pos ## 9 세계적인 2.94 pos ## 10 최고의 2.90 pos ## 11 닭그네 -1.89 neg ## 12 못하고 -1.89 neg ## 13 사회적 -1.89 neg ## 14 싫다 -1.89 neg ## 15 가난한 -2.07 neg ## 16 모르는 -2.07 neg ## 17 아쉽다 -2.07 neg ## 18 좌좀 -2.22 neg ## 19 못한 -2.36 neg ## 20 좌빨 -2.68 neg ``` ] .pull-right[ ``` ## # A tibble: 20 x 3 ## # Groups: sentiment [2] ## word log_odds_ratio sentiment ## <chr> <dbl> <chr> ## 1 축하 3.95 pos ## 2 멋지다 3.81 pos ## 3 대단한 3.66 pos ## 4 좋은 3.55 pos ## 5 대단하다 3.52 pos ## 6 자랑스럽다 3.46 pos ## 7 최고 3.12 pos ## 8 세계적인 3.01 pos ## 9 최고의 2.97 pos ## 10 위대한 2.92 pos ## 11 닭그네 -1.82 neg ## 12 가난한 -2.00 neg ## 13 모르는 -2.00 neg ## 14 아쉽다 -2.00 neg ## 15 소름이 -2.08 neg ## 16 좌좀 -2.16 neg ## 17 못한 -2.29 neg *## 18 미친 -2.29 neg ## 19 좌빨 -2.61 neg *## 20 소름 -3.03 neg ``` ] --- - `"미친"`이 목록에서 사라짐: 로그 오즈비가 10위 안에 들지 못할 정도로 낮기 때문 - 긍정 단어 10위 `"최고의"` 로그 오즈비 2.90 ```r new_comment_wide %>% filter(word == "미친") ``` ``` ## # A tibble: 1 x 4 ## word pos neg log_odds_ratio ## <chr> <int> <int> <dbl> ## 1 미친 7 0 1.80 ``` --- class: title1 정리하기 --- ### 정리하기 ##### 1. 자주 사용된 감정 단어 살펴보기 ```r # 단어에 감정 점수 부여 word_comment <- word_comment %>% left_join(dic, by = "word") %>% mutate(polarity = ifelse(is.na(polarity), 0, polarity)) # 감정 분류 word_comment <- word_comment %>% mutate(sentiment = ifelse(polarity == 2, "pos", ifelse(polarity == -2, "neg", "neu"))) # 자주 사용된 감정 단어 추출 top10_sentiment <- word_comment %>% filter(sentiment != "neu") %>% count(sentiment, word) %>% group_by(sentiment) %>% slice_max(n, n = 10) ``` --- ### 정리하기 ##### 2. 텍스트의 감정 점수 구하기 ```r # 텍스트별로 단어의 감정 점수 합산 score_comment <- word_comment %>% group_by(id, reply) %>% summarise(score = sum(polarity)) %>% ungroup() ``` --- ### 정리하기 ##### 3. 감정 범주별 주요 단어 살펴보기 ```r # 감정 범주 변수 생성 score_comment <- score_comment %>% mutate(sentiment = ifelse(score >= 1, "pos", ifelse(score <= -1, "neg", "neu"))) # 토큰화 및 전처리 comment <- score_comment %>% unnest_tokens(input = reply, output = word, token = "words", drop = F) %>% filter(str_detect(word, "[가-힣]") & str_count(word) >= 2) # 감정 범주별 단어 빈도 구하기 frequency_word <- comment %>% count(sentiment, word, sort = T) ``` --- ### 정리하기 ##### 3. 감정 범주별 주요 단어 살펴보기 ```r # 로그 오즈비 구하기 comment_wide <- frequency_word %>% filter(sentiment != "neu") %>% pivot_wider(names_from = sentiment, values_from = n, values_fill = list(n = 0)) comment_wide <- comment_wide %>% mutate(log_odds_ratio = log(((pos + 1) / (sum(pos + 1))) / ((neg + 1) / (sum(neg + 1))))) # 긍정, 부정 텍스트에 상대적으로 자주 사용된 단어 추출 top10 <- comment_wide %>% group_by(sentiment = ifelse(log_odds_ratio > 0, "pos", "neg")) %>% slice_max(abs(log_odds_ratio), n = 10) ``` --- ### 분석 도전 **`"news_comment_BTS.csv"`에는 2020년 9월 21일 방탄소년단이 '빌보드 핫 100 차트' 1위에 오른 소식을 <br>다룬 기사에 달린 댓글이 들어있습니다. `"news_comment_BTS.csv"`를 이용해 문제를 해결해 보세요.** Q1. `"news_comment_BTS.csv"`를 불러온 다음 행 번호를 나타낸 변수를 추가하고 분석에 적합하게 <br> 전처리하세요. Q2. 댓글을 띄어쓰기 기준으로 토큰화하고 감정 사전을 이용해 댓글의 감정 점수를 구하세요. Q3. 감정 범주별 댓글 빈도를 나타낸 막대 그래프를 만드세요. Q4. 댓글을 띄어쓰기 기준으로 토큰화한 다음 감정 범주별 단어 빈도를 구하세요. Q5. 로그 오즈비를 이용해 긍정 댓글과 부정 댓글에 상대적으로 자주 사용된 단어를 10개씩 추출하세요. Q6. 긍정 댓글과 부정 댓글에 상대적으로 자주 사용된 단어를 나타낸 막대 그래프를 만드세요. Q7. 'Q3'에서 만든 데이터를 이용해 '긍정 댓글에 가장 자주 사용된 단어'를 언급한 댓글을 <br> 감정 점수가 높은 순으로 출력하세요. Q8. 'Q3'에서 만든 데이터를 이용해 '부정 댓글에 가장 자주 사용된 단어'를 언급한 댓글을 <br> 감정 점수가 낮은 순으로 출력하세요. --- Q1. `"news_comment_BTS.csv"`를 불러온 다음 행 번호를 나타낸 변수를 추가하고 분석에 적합하게 <br> 전처리하세요. ```r # 기사 댓글 불러오기 library(readr) library(dplyr) raw_news_comment <- read_csv("news_comment_BTS.csv") glimpse(raw_news_comment) ``` ``` ## Rows: 1,200 ## Columns: 5 ## $ reg_time <dttm> 2020-09-01 22:58:09, 2020-09-01 09:56:46~ ## $ reply <chr> "국보소년단<U+0001F49C>", "아줌마가 들어~ ## $ press <chr> "한국경제", "한국경제", "한국경제", "한국~ ## $ title <chr> "[속보]BTS '다이너마이트', 한국 가수 최초~ ## $ url <chr> "https://news.naver.com/main/read.nhn?mod~ ``` --- Q1. `"news_comment_BTS.csv"`를 불러온 다음 행 번호를 나타낸 변수를 추가하고 분석에 적합하게 <br> 전처리하세요. ```r # 전처리 library(stringr) library(textclean) news_comment <- raw_news_comment %>% mutate(id = row_number(), reply = str_squish(replace_html(reply))) news_comment %>% select(id, reply) ``` --- ``` ## # A tibble: 1,200 x 2 ## id reply ## <int> <chr> ## 1 1 국보소년단 ## 2 2 아줌마가 들어도 좋더라 ## 3 3 팩트체크 현재 빌보드 HOT 100 1위 방탄소년단[BTS] 2위 Cardi B ft. Megan~ ## 4 4 방탄소년단이 한국사람이라 너무 자랑스러워요 ㅠㅠ 우리오래오래 함께하자! ## 5 5 대단한 BTS, 월드 클래스는 다르네^^ 좋은 소식!! 응원해요 ## 6 6 정국오빠 생일과 더불어 빌보드 1위기사라니ㅠㅠ축제구나 ## 7 7 정말 축하하고 응원하지만 집에서 여러 계정으로 스트리밍 돌리고 사재기하~ ## 8 8 기자는 자고 일어났지만, 팬들은 못자고 발표 기다림 ## 9 9 자랑스럽다!!!!!! 축하합니다!!!! ## 10 10 SuperM 늘 응원하고 사랑합니다~ ## # ... with 1,190 more rows ``` --- Q2. 댓글을 띄어쓰기 기준으로 토큰화하고 감정 사전을 이용해 댓글의 감정 점수를 구하세요. ```r # 토큰화 library(tidytext) library(KoNLP) word_comment <- news_comment %>% unnest_tokens(input = reply, output = word, token = "words", # 띄어쓰기 기준 drop = F) # 원문 유지 word_comment %>% select(word) ``` --- ``` ## # A tibble: 11,673 x 1 ## word ## <chr> ## 1 국보소년단 ## 2 아줌마가 ## 3 들어도 ## 4 좋더라 ## 5 팩트체크 ## 6 현재 ## 7 빌보드 ## 8 hot ## 9 100 ## 10 1 ## # ... with 11,663 more rows ``` --- Q2. 댓글을 띄어쓰기 기준으로 토큰화하고 감정 사전을 이용해 댓글의 감정 점수를 구하세요. ```r # 감정 사전 불러오기 dic <- read_csv("knu_sentiment_lexicon.csv") # 단어에 감정 점수 부여 word_comment <- word_comment %>% left_join(dic, by = "word") %>% mutate(polarity = ifelse(is.na(polarity), 0, polarity)) word_comment %>% select(word, polarity) %>% arrange(-polarity) ``` --- ``` ## # A tibble: 11,673 x 2 ## word polarity ## <chr> <dbl> ## 1 대단한 2 ## 2 좋은 2 ## 3 자랑스럽다 2 ## 4 자랑스럽다 2 ## 5 자랑스럽다 2 ## 6 장하다 2 ## 7 꾸준히 2 ## 8 행복한 2 ## 9 대단한 2 ## 10 대단한 2 ## # ... with 11,663 more rows ``` --- Q2. 댓글을 띄어쓰기 기준으로 토큰화하고 감정 사전을 이용해 댓글의 감정 점수를 구하세요. ```r # 댓글별로 단어의 감정 점수 합산 score_comment <- word_comment %>% group_by(id, reply) %>% summarise(score = sum(polarity)) %>% ungroup() score_comment %>% select(score, reply) %>% arrange(-score) ``` --- ``` ## # A tibble: 1,194 x 2 ## score reply ## <dbl> <chr> ## 1 8 멋지다, 자랑스럽다, 대단하다 방탄소년단!!! 다이너마이트 빌보드 핫100 1~ ## 2 7 팬은 아니야. 그래서 저 노력과 업적이 더 대단해보여. 정말 멋지다. 잘생~ ## 3 6 축하 합니다 우리에 보물이네 대한미국에 애국자 들이다 나라 홍보도하고 ~ ## 4 6 우리딸이 보는 눈이 있네 호르몬전쟁 노래부터 애네들 좋아했는데 그때는 ~ ## 5 6 ㅜㅜ . 진짜 이 코로나에 너희들이 빛이여. 핫백 1위라니. 모든 기록을 다 ~ ## 6 6 축하 축하 아미분들도 축하^^ ## 7 6 정말 대단하고 자랑스럽습니다.. 국격이 업그레이드 된거 같습니다..축하 ~ ## 8 6 빌보드 핫100 1위 축하해요 여기까지 오느라 힘들었을텐데 수고했어요 앞으~ ## 9 6 진짜 대단하다. K팝 아시아 최고 넘어서 빌보드 1위 등극 이제 BTS가 그냥 ~ ## 10 6 정국이 생일에 빌보드 핫100 1위라니... 정말 뜻깊은 하루네요ㅠㅠ 좋은 음~ ## # ... with 1,184 more rows ``` --- Q3. 감정 범주별 댓글 빈도를 나타낸 막대 그래프를 만드세요. ```r # 감정 범주 변수 생성 score_comment <- score_comment %>% mutate(sentiment = ifelse(score >= 1, "pos", ifelse(score <= -1, "neg", "neu"))) score_comment %>% select(sentiment, reply) ``` ``` ## # A tibble: 1,194 x 2 ## sentiment reply ## <chr> <chr> ## 1 neu 국보소년단 ## 2 neu 아줌마가 들어도 좋더라 ## 3 pos 팩트체크 현재 빌보드 HOT 100 1위 방탄소년단[BTS] 2위 Cardi B ft. M~ ## 4 neg 방탄소년단이 한국사람이라 너무 자랑스러워요 ㅠㅠ 우리오래오래 함께~ ## 5 pos 대단한 BTS, 월드 클래스는 다르네^^ 좋은 소식!! 응원해요 ## 6 neg 정국오빠 생일과 더불어 빌보드 1위기사라니ㅠㅠ축제구나 ## 7 neu 정말 축하하고 응원하지만 집에서 여러 계정으로 스트리밍 돌리고 사재~ ## 8 neu 기자는 자고 일어났지만, 팬들은 못자고 발표 기다림 ## 9 pos 자랑스럽다!!!!!! 축하합니다!!!! ## 10 neu SuperM 늘 응원하고 사랑합니다~ ## # ... with 1,184 more rows ``` --- Q3. 감정 범주별 댓글 빈도를 나타낸 막대 그래프를 만드세요. ```r # 감정 범주 빈도 구하기 frequency_score <- score_comment %>% count(sentiment) frequency_score ``` ``` ## # A tibble: 3 x 2 ## sentiment n ## <chr> <int> ## 1 neg 113 ## 2 neu 743 ## 3 pos 338 ``` --- Q3. 감정 범주별 댓글 빈도를 나타낸 막대 그래프를 만드세요. ```r # 막대 그래프 만들기 library(ggplot2) ggplot(frequency_score, aes(x = sentiment, y = n, fill = sentiment)) + geom_col() + geom_text(aes(label = n), vjust = -0.3) ``` <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-81-1.png" width="50%" /> --- <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-82-1.png" width="80%" /> --- Q4. 댓글을 띄어쓰기 기준으로 토큰화한 다음 감정 범주별 단어 빈도를 구하세요. .pull-left[ ```r # 토큰화 comment <- score_comment %>% unnest_tokens(input = reply, output = word, token = "words", drop = F) # 감정 범주별 단어 빈도 구하기 frequency_word <- comment %>% count(sentiment, word, sort = T) frequency_word ``` ] .pull-right[ ``` ## # A tibble: 6,900 x 3 ## sentiment word n ## <chr> <chr> <int> ## 1 neu 1 126 ## 2 pos 진짜 90 ## 3 pos 1 82 ## 4 neu 진짜 79 ## 5 pos 자랑스럽다 77 ## 6 neu bts 72 ## 7 pos 너무 70 ## 8 neu 빌보드 66 ## 9 pos 정말 57 ## 10 neu 군면제 48 ## # ... with 6,890 more rows ``` ] --- Q5. 로그 오즈비를 이용해 긍정 댓글과 부정 댓글에 상대적으로 자주 사용된 단어를 10개씩 추출하세요. .pull-left[ ```r # long form을 wide form으로 변환 library(tidyr) comment_wide <- frequency_word %>% filter(sentiment != "neu") %>% pivot_wider(names_from = sentiment, values_from = n, values_fill = list(n = 0)) comment_wide ``` ] .pull-right[ ``` ## # A tibble: 3,247 x 3 ## word pos neg ## <chr> <int> <int> ## 1 진짜 90 20 ## 2 1 82 29 ## 3 자랑스럽다 77 0 ## 4 너무 70 14 ## 5 정말 57 5 ## 6 위 46 11 ## 7 빌보드 40 15 ## 8 방탄 39 8 ## 9 방탄소년단 39 13 ## 10 bts 37 21 ## # ... with 3,237 more rows ``` ] --- Q5. 로그 오즈비를 이용해 긍정 댓글과 부정 댓글에 상대적으로 자주 사용된 단어를 10개씩 추출하세요. ```r # 로그 오즈비 구하기 comment_wide <- comment_wide %>% mutate(log_odds_ratio = log(((pos + 1) / (sum(pos + 1))) / ((neg + 1) / (sum(neg + 1))))) comment_wide ``` ``` ## # A tibble: 3,247 x 4 ## word pos neg log_odds_ratio ## <chr> <int> <int> <dbl> ## 1 진짜 90 20 1.03 ## 2 1 82 29 0.586 ## 3 자랑스럽다 77 0 3.93 ## 4 너무 70 14 1.12 ## 5 정말 57 5 1.84 ## 6 위 46 11 0.934 ## 7 빌보드 40 15 0.510 ## 8 방탄 39 8 1.06 ## 9 방탄소년단 39 13 0.618 ## 10 bts 37 21 0.115 ## # ... with 3,237 more rows ``` --- Q5. 로그 오즈비를 이용해 긍정 댓글과 부정 댓글에 상대적으로 자주 사용된 단어를 10개씩 추출하세요. ```r # 긍정, 부정 댓글에 상대적으로 자주 사용된 단어 추출 top10 <- comment_wide %>% group_by(sentiment = ifelse(log_odds_ratio > 0, "pos", "neg")) %>% slice_max(abs(log_odds_ratio), n = 10) top10 ``` ``` ## # A tibble: 25 x 5 ## # Groups: sentiment [2] ## word pos neg log_odds_ratio sentiment ## <chr> <int> <int> <dbl> <chr> ## 1 국내 0 5 -2.22 neg ## 2 모르는 0 4 -2.04 neg ## 3 없어서 0 4 -2.04 neg ## 4 있다 0 4 -2.04 neg ## 5 널리 0 3 -1.82 neg ## 6 독도 0 3 -1.82 neg ## 7 보다 0 3 -1.82 neg ## 8 아니다 0 3 -1.82 neg ## 9 없다 0 3 -1.82 neg ## 10 케이팝 0 3 -1.82 neg ## # ... with 15 more rows ``` --- Q6. 긍정 댓글과 부정 댓글에 상대적으로 자주 사용된 단어를 나타낸 막대 그래프를 만드세요. ```r ggplot(top10, aes(x = reorder(word, log_odds_ratio), y = log_odds_ratio, fill = sentiment)) + geom_col() + coord_flip() + labs(x = NULL) ``` <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-88-1.png" width="50%" /> --- <img src="04-sentimentAnalysis_files/figure-html/unnamed-chunk-89-1.png" width="80%" /> --- Q7. 'Q3'에서 만든 데이터를 이용해 '긍정 댓글에 가장 자주 사용된 단어'를 언급한 댓글을 <br> 감정 점수가 높은 순으로 출력하세요. ```r score_comment %>% filter(str_detect(reply, "자랑스럽다")) %>% arrange(-score) %>% select(reply) ``` ``` ## # A tibble: 82 x 1 ## reply ## <chr> ## 1 멋지다, 자랑스럽다, 대단하다 방탄소년단!!! 다이너마이트 빌보드 핫100 1위 진~ ## 2 축하 합니다 우리에 보물이네 대한미국에 애국자 들이다 나라 홍보도하고 달라도~ ## 3 자랑스러운 방탄소년단.... 아~~이거 방탄소년단 아입니까!!!! 사랑하는 막둥이정~ ## 4 자랑스럽다 역사적인 길들을 함께 걸어갈 수 있어 감사하다 ## 5 아시아에서 60년만에 빌보드 핫백 차트 1위 아시아에서 최초로 200차트와 핫백 차~ ## 6 와 진짜 너무 대단하고 자랑스럽다 ! 진짜 꿈인지 생시인지 모르겠네요! 이건 정~ ## 7 정말 대단하고 자랑스럽다~ ## 8 이건 진짜 대서특필 감이다.. 혼란스러운 이 시국에 이렇게 기쁜 소식이라니 진짜~ ## 9 진짜 대박이다 방탄 역사를 썼다 대단하고 자랑스럽다!!!! ## 10 정말 너무 자랑스럽다. 역사의 순간에 있다는 게 고맙고 뿌듯하다 ## # ... with 72 more rows ``` --- Q8. 'Q3'에서 만든 데이터를 이용해 '부정 댓글에 가장 자주 사용된 단어'를 언급한 댓글을 <br> 감정 점수가 낮은 순으로 출력하세요. ```r score_comment %>% filter(str_detect(reply, "국내")) %>% arrange(score) %>% select(reply) ``` ``` ## # A tibble: 10 x 1 ## reply ## <chr> ## 1 "국위선양 한건 대단 하지만 40대 이후로는 BTS 이름만 알지 히트곡은 전혀모름 ~ ## 2 "국내 국악대회 거기 입상한 애들도 군대 안간다던데 세계 1위 한거 인정해서 면~ ## 3 "아니 진짜 군면제하자 뭘더 얼마나 일등을 해야해 국내 클래식 콩쿨 1등해도 면~ ## 4 "진짜 세계를 kpop이 이렇게 휩쓸고 있는데, 정작 국내는 뽕짝만 주구장창 나오는~ ## 5 "아시아 아티스트로서는 57년만이랍니다... 방탄소년단 국내 시상식 대상도 못받~ ## 6 "bts 너무 자랑스러워요 국내에는 웃을일이 없는데 bts 때문에 기분이 좋네요" ## 7 "이렇게 국위선양하고 BTS때문에 파생되는 경제효과는 어마어마한데 애들은 군대 ~ ## 8 "그냥 국내 1등 아이돌 가수같은데 감흥이없네 여튼 축하해요" ## 9 "아니 진짜 방탄 팬도아니고 그냥 노래 나오면 즐겨듣는정돈데 방탄 군대 왜보냄 ~ ## 10 "국내 콩쿨에서 입상해도 군면제 해주는데... 신지어 빌보드 1위인데.. 군대 보내~ ``` --- class: title0 끝