감정분석

감정분석은 컴퓨터를 이용하여 사람들의 감정이 표현된 텍스트를 분석하는 연구방법을 말합니다. 여기에서 감정은 사실 보다는 주관적인 인상에 바탕을 둔 의견, 감정, 또는 태도를 포함하는 느낌의 상태를 의미합니다.

감정 분석은 사람들의 의견과 행동을 예측하거나 설명하는데 유용한 분석 방법으로서 여러 분야에서 점차 그 역할이 중요해지고 있습니다. 가령, 감정분석을 통해 우리는 다음과 같은 질문에 대한 답을 구할 수 있습니다.

영화: 사람들의 영화평이 긍정적인가 부정적인가? 상품: 신상품에 대한 대중의 반응은 어떠한가? 여론: 정부 정책에 대한 시민들의 지지 여부는? 정치: 선거 출마자에 대한 투표권자들의 호감도는? *경제: 경기 또는 시장 상황이 어떻게 변할 것인가?

이처럼 유용한 감정분석을 수행하기 위해, 전문가들은 NLP, 사전, 통계 또는 기계학습 등 다양한 텍스트 마이닝 방법을 이용해 감정 내용을 확인하고, 추출하거나 설명합니다. 다양한 텍스트 마이닝 기법을 사용해서 분석 대상의 감정을 정확하게 파악하는 것이 중요합니다.

이 수업에서는 감정분석의 기초 단계로서 사전기반 감정분석을 수행하는 방법을 알려드리도록 하겠습니다.

감정사전을 사용한 감정분석 <출처: 김영우, “Do it! 쉽게 배우는 R 텍스트 마이닝”, 2021, 이지스퍼블리싱>

이번 시간에는 사전기반 감정분석에 대해 알아보도록 하겠습니다. 사전기반 감정분석이란 텍스트의 감정 형태는 사용된 표현(어휘, 어구, 문형, 축약어, 이모티콘)들의 합이라는 전제하에 감정사전을 이용하는 방법이라 할 수 있습니다. 따라서, 하나의 텍스트가 어떠한 감정을 표현하는지 분석하기 위해서 각각의 표현을 감정어휘 사전에 따라 분류하고 출현 빈도수를 합하는 과정을 거치게 됩니다.

사전기반 감정분석

사전기반 감정분석

사전기반 감정분석은 감정사전에 등재되어있는 표현 분류에 따라 우리가 분석하고자 하는 텍스트에 출현하는 표현을 분류하는 것으로 시작합니다. 그렇다면, 감정 표현이 분류되어있는 사전을 어떻게 이용할 수 있을까요. 가령, 긍정의 의미 또는 부정의 의미를 뜻하는 표현들이 정리되어있는 사전들도 그 종류와 쓰임이 다양합니다. 어떠한 사전을 어떻게 이용하느냐가 사전기반 감정분석에서 중요한 지점입니다. R에서 이용할 수 있는 감정사전도 다양합니다. 하지만, 본 강의에서는 군산대학교 소프트웨어융합공학과에서 만든 “KNU 한국어 감성사전”을 이용해 감정분석을 하는 방법을 살펴보겠습니다. “KNU 한국어 감성사전”은 본 수업의 e-class 강의 콘텐츠에서 다운로드 가능합니다. “KNU 한국어 감성사전” 깃허브 출처: https://github.com/park1200656/KnuSentiLex

감성사전의 구조는 감정 단어 word와 감정의 강도를 표현한 polarity로 구성되어 있습니다.

사전기반 감정분석은 텍스트의 각 표현들의 감정 상태를 표시하는 것부터 시작합니다. 그리고 앞서 말했듯, 감정사전의 표현들은 감정상태에 따라 분류되어 있죠. 예를 들어, 힘찬 그리고 희망적은 긍정적 감정 표현으로 분류되어 있고, 힘들여희망이 없는은 부정적 감정 표현으로 분류되어 있죠. 즉, 감정사전의 표현은 감정 상태와 쌍을 이루고 있습니다. 이러한 표현과 감정의 쌍으로 이루어진 감정사전을 우리가 분석하고자 하는 텍스트를 포함한 데이터셋과 결합해주는 방식으로 감정분석을 진행할 겁니다.

library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.0 --
## √ ggplot2 3.3.3     √ purrr   0.3.4
## √ tibble  3.1.0     √ dplyr   1.0.4
## √ tidyr   1.1.2     √ stringr 1.4.0
## √ readr   1.3.1     √ forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
dic <- read_csv("knu_sentiment_lexicon.csv")
## Parsed with column specification:
## cols(
##   word = col_character(),
##   polarity = col_double()
## )
dic
## # A tibble: 14,854 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 ㅗ          -1
## # ... with 14,844 more rows

“KNU 감정사전”은 14,854 행과 2개의 열로 이루어진 데이터 프레임의 형식을 가지고 있습니다. 첫번째 열은 “word”인데, 즉 사전에 수록된 14,854 표현들이 열거되어 있습니다. 두번째 열은 “polarity”로서 각 표현의 감정 점수가 규정되어 있죠. 가령 “힘찬 기운이” 라는 표현은 “2” 즉 긍정적 감정으로 “힘이 없음을”이라는 표현은 “-1” 즉, 부정적 감정으로 분류되어 있는 것을 볼 수 있습니다.

# 매우 긍정 단어 (+2)
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
# 긍정 단어 (+1)
dic %>% 
  filter(polarity == 1) %>% 
  arrange(word)
## # A tibble: 2,269 x 2
##    word     polarity
##    <chr>       <dbl>
##  1 "(-;"           1
##  2 "(^-^)"         1
##  3 "(^^)"          1
##  4 "(^^*"          1
##  5 "(^_^)"         1
##  6 "(^o^)"         1
##  7 "*^^*"          1
##  8 "/^o^\\"        1
##  9 ":'-("          1
## 10 ":-("           1
## # ... with 2,259 more rows
# 중립 단어 (0)
dic %>% 
  filter(polarity == 0) %>% 
  arrange(word)
## # A tibble: 154 x 2
##    word          polarity
##    <chr>            <dbl>
##  1 :p                   0
##  2 8-)                  0
##  3 B-)                  0
##  4 가까스로             0
##  5 가라앉다             0
##  6 가라앉지 않은        0
##  7 가르침을 받아        0
##  8 가리지 않고          0
##  9 감싸고 달래다        0
## 10 강구하다             0
## # ... with 144 more rows
# 부정 단어 (-1)
dic %>% 
  filter(polarity == -1) %>% 
  arrange(word)
## # A tibble: 5,030 x 2
##    word   polarity
##    <chr>     <dbl>
##  1 -_-^         -1
##  2 (-_-)        -1
##  3 (;_;)        -1
##  4 (^_^;        -1
##  5 (T_T)        -1
##  6 (ㅡㅡ)       -1
##  7 )-:          -1
##  8 :-D          -1
##  9 :-P          -1
## 10 :)           -1
## # ... with 5,020 more rows
# 매우 부정 단어 (-2)
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

한가지 주목할 점은, 이러한 감정사전 역시 tidy 데이터 프레임을 갖추고 있다는 점인데요. 각 행마다 하나의 표현 씩 위치하고, 그 표현을 설명하는 추가적인 열 “polarity”가 variable(변수)로서 존재합니다.

감정 표현의 종류 살펴보기

감정 표현을 나타낸 word는 한 단어로 구성된 단일어, 두 개 이상의 단어가 결합된 복합어, ^^,ㅠㅠ와 같은 이모티콘으로 구성됩니다. polarity+2에서 -2까지 5가지 정수로 되어 있습니다. 긍정 단어는 +, 부정 단어는 -로 표현됩니다. 긍정과 부정 중 어느 한쪽으로 판단하기 어려운 중성 단어는 0으로 표현됩니다.

그리고, 중요한 부분이 있습니다. “KNU 감정사전”에는 “힘이 있게”나 “힘이 없어”와 같은 수식어구로서의 감정 표현도 수록되어 있다는 점인데요. 따라서 이러한 사전을 이용한 감정분석은 보다 면밀하고 섬세한 분석 및 결과 해석이 필요합니다.

dic %>% 
  filter(word %in% c("좋은", "나쁜"))
## # A tibble: 2 x 2
##   word  polarity
##   <chr>    <dbl>
## 1 좋은         2
## 2 나쁜        -2
dic %>% 
  filter(word %in% c("기쁜", "슬픈"))
## # A tibble: 2 x 2
##   word  polarity
##   <chr>    <dbl>
## 1 슬픈        -2
## 2 기쁜         2
dic %>%
  filter(word %in% c("행복하다", "좌절하다"))
## # A tibble: 2 x 2
##   word     polarity
##   <chr>       <dbl>
## 1 행복하다        2
## 2 좌절하다       -2
# 이모티콘
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

“KNU 감정사전”의 표현은 총 14,854개입니다. 긍정 표현 4,871개, 부정 표현 9,829개, 중성 표현154개로 구성됩니다.

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. 단어 기준으로 토큰화하기 감정사전을 이용해 문장의 감정 점수를 구하는 방법을 알아보겠습니다. 우선, 분석할 텍스트의 표현을 감정사전의 표현과 대조할 수 있도록 토큰화해야 합니다. 감정사전은 단일어와 복합어로 구성되어 있으므로 분석할 텍스트를 형태소 단위가 아닌 띄어쓰기 기준으로 토큰화해야 합니다.

unnest_tokens() 함수를 이용해 샘플 텍스트

library(readxl)
library(lubridate) 
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
cv <- read_excel("bigkinds_corona_vaccine.xlsx", sheet = 1)
cv_hls_type <- cv %>% 
  select(DATE, COMPANY, HEADLINE) %>% 
  filter(!duplicated(HEADLINE)) %>% 
  mutate(date = ymd(DATE)) %>% 
  mutate(hl = str_remove_all(HEADLINE, "\\[[[:print:]]+\\]|\\<[[:print:]]+\\>")) %>% 
  filter(str_detect(hl, "아스트라|AZ|화이자")) %>% 
  mutate(type = ifelse(str_detect(hl, "AZ|아스트라"), 
                "AZ", "Pfizer"))

library(tidytext)
cv_hls_tidy <- cv_hls_type %>% 
  unnest_tokens(input = hl, 
                output = word,
                token = "words")
cv_hls_tidy
## # A tibble: 9,123 x 6
##    DATE    COMPANY  HEADLINE                            date       type  word   
##    <chr>   <chr>    <chr>                               <date>     <chr> <chr>  
##  1 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    고령층에도~
##  2 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    본격화된~
##  3 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    az     
##  4 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    백신   
##  5 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    접종   
##  6 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    az     
##  7 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    코로나백신~
##  8 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    美     
##  9 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    임상서 
## 10 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    효과   
## # ... with 9,113 more rows
  1. left_join() 함수를 이용한 표현에 감정 점수 부여하기 사전기반 감정분석은 tidy 데이터 프레임을 이용하는 것에서부터 시작합니다. 구체적으로, 한 행당 하나의 단어 혹은 어구씩으로 tidy 데이터 프레임 화 되어 있는 트윗 데이터를 감정사전과 결합하는 방식으로 감정분석이 진행됩니다. 그럼 우선 예제를 통해 데이터 셋 결합 방법을 살펴봅시다.
df <- tibble(sentence = c("디자인 예쁘고 마감도 좋아서 만족스럽다.",
                          "디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다."))
df
## # A tibble: 2 x 1
##   sentence                                            
##   <chr>                                               
## 1 디자인 예쁘고 마감도 좋아서 만족스럽다.             
## 2 디자인은 괜찮다. 그런데 마감이 나쁘고 가격도 비싸다.
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을 부여합니다.

left_join() 함수를 이용한 데이터 셋 결합

left_join

left_join

left_join

left_join

위에서 볼 수 있듯, left_join() 함수는 두 tidy 데이터 간에 공통 요소 (여기에서는 각 어휘, word)들을 골라 각 요소와 짝지어져 있는 변수들을 추가하는 방식으로 두 데이터를 결합하는 기능을 합니다. df 객체의 word 변수에 있는 각 어휘들, 그리고 dic 객체의 word 변인의 요소들 중 공통되는 것들에 그 어휘들과 짝지어져 있는 polarity 변수의 점수를 부여하고 나머지에는 NA를 부여하지만 이를 0으로 변환하는 방식인 것이죠.

이러한 left_join() 함수를 이용하면 tidy 방식의 헤드라인 데이터를 KNU 감정사전과 결합하여 감정 점수를 부여할 수 있습니다.

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

헤드라인 데이터 셋에 감정 사전 결합하기

cv_hls_tidy  # "코로나" 및 "백신" 키워드를 포함한 뉴스기사 헤드라인 데이터의 tidy data frame
## # A tibble: 9,123 x 6
##    DATE    COMPANY  HEADLINE                            date       type  word   
##    <chr>   <chr>    <chr>                               <date>     <chr> <chr>  
##  1 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    고령층에도~
##  2 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    본격화된~
##  3 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    az     
##  4 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    백신   
##  5 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    접종   
##  6 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    az     
##  7 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    코로나백신~
##  8 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    美     
##  9 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    임상서 
## 10 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 2021-03-22 AZ    효과   
## # ... with 9,113 more rows
cv_hls_senti <- cv_hls_tidy %>% 
  left_join(dic, by = "word") %>% 
  mutate(polarity = ifelse(is.na(polarity), 0, polarity))
cv_hls_senti
## # A tibble: 9,123 x 7
##    DATE    COMPANY  HEADLINE                    date       type  word   polarity
##    <chr>   <chr>    <chr>                       <date>     <chr> <chr>     <dbl>
##  1 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    고령층에도~        0
##  2 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    본격화된~        0
##  3 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    az            0
##  4 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    백신          0
##  5 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"~ 2021-03-22 AZ    접종          0
##  6 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% ~ 2021-03-22 AZ    az            0
##  7 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% ~ 2021-03-22 AZ    코로나백신~        0
##  8 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% ~ 2021-03-22 AZ    美            0
##  9 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% ~ 2021-03-22 AZ    임상서        0
## 10 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% ~ 2021-03-22 AZ    효과          0
## # ... with 9,113 more rows
  1. 헤드라인 별로 감정 점수 합산하기 위 코딩 결과에서 볼 수 있듯이, left_join() 함수를 이용해서 cv_hls_tidy 객체를 dic 객체와 결합하는 방식으로 감정 어휘를 정리할 수 있습니다. 자, 그러면 각 헤드라인의 감정 점수는 어떻게 될까요? HEADLINE 별로 감정 점수를 합산하는 방식으로 각 헤드라인의 감정 점수를 계산할 수 있습니다.
cv_hls_score <- cv_hls_senti %>% 
  group_by(type, HEADLINE) %>% 
  summarise(score  = sum(polarity))
## `summarise()` has grouped output by 'type'. You can override using the `.groups` argument.
cv_hls_score
## # A tibble: 1,006 x 3
## # Groups:   type [2]
##    type  HEADLINE                                                          score
##    <chr> <chr>                                                             <dbl>
##  1 AZ    "'文 대통령 정청래 유승민 안철수 ' AZ 백신 접종 1호 정치인은 누구?"~     0
##  2 AZ    "'아스트라' 국내 생산 중 백신 후보 3종, 어느 단계?"                   1
##  3 AZ    "'코로나 백신' 아스트라제네카 CEO \"추가 글로벌 임상시험 필요\""      0
##  4 AZ    "'코로나 백신' 아스트라제네카 가장 먼저 도입 확정 화이자는 내년 가을 예상"~     0
##  5 AZ    "'한국 선구매' 아스트라제네카 백신, 인도가 세계 최초 승인할 듯"       0
##  6 AZ    "\"'아스트라제네카' 코로나 백신, 고령자 투여 향후 다시 논의해야\""~     0
##  7 AZ    "\"20대男, AZ 맞고 걷지도 못해 기막힌 우연이냐\" 청원 등장"           0
##  8 AZ    "\"AZ 맞으면 감염되더라도 가볍게 앓아\""                             -1
##  9 AZ    "\"AZ 접종 후 희귀 혈전 발생 20대 구급대원 증상 호전돼\""             0
## 10 AZ    "\"AZ 코로나 백신 허가 5일 쯤 결정\""                                 0
## # ... with 996 more rows
  1. 백신 타입 별로 감정 점수 평균 비교하기
cv_hls_stat <- cv_hls_score %>% 
  group_by(type) %>% 
  summarise(mean = mean(score),
            sd = sd(score),
            n = length(score)) %>% 
  mutate(se = sd/sqrt(n))
cv_hls_stat
## # A tibble: 2 x 5
##   type      mean    sd     n     se
## * <chr>    <dbl> <dbl> <int>  <dbl>
## 1 AZ     -0.0103 0.848   486 0.0385
## 2 Pfizer  0.0423 0.644   520 0.0282
cv_hls_stat %>% 
  ggplot(aes(x=type, y=mean, fill=type)) + 
  geom_bar(stat="identity",  color="black") +
  geom_errorbar(aes(ymin=mean-se, ymax=mean+se), width=.2,
                 position=position_dodge(.9)) 

  1. 감정 표현 살펴보기

중립 표현은 제외하고, 긍정 및 부정 표현 중 각 백신에 대해 가장 자주 사용된 표현을 10개씩 추출해 비교하는 막대 그래프를 만들어봅시다.

top10_sentiment <- cv_hls_senti %>%
  mutate(sentiment = ifelse(polarity == 0, "neu",
                ifelse(polarity < 0, "neg", "pos"))) %>% 
  filter(sentiment != "neu") %>%
  count(type, sentiment, word) %>%
  group_by(sentiment) %>%
  slice_max(n, n = 10)

top10_sentiment
## # A tibble: 28 x 4
## # Groups:   sentiment [2]
##    type   sentiment word       n
##    <chr>  <chr>     <chr>  <int>
##  1 AZ     neg       부작용     8
##  2 AZ     neg       없다       7
##  3 AZ     neg       환자       5
##  4 Pfizer neg       부작용     5
##  5 AZ     neg       불안감     4
##  6 Pfizer neg       환자       4
##  7 AZ     neg       발열       3
##  8 AZ     neg       불신       3
##  9 AZ     neg       고열       2
## 10 AZ     neg       공포       2
## # ... with 18 more rows

막대 그래프 만들기

ggplot(top10_sentiment, aes(x = reorder_within(word, n, type), 
                            y = n, 
                            fill = sentiment)) +
  geom_col() +
  coord_flip() +
  geom_text(aes(label = n), hjust = -0.3) +
  facet_wrap(~ type+sentiment, scales = "free") +
  scale_x_reordered() +
  scale_y_continuous(expand = expansion(mult = c(0.05, 0.15))) +  
  labs(x = NULL)