데이터 불러오기

library(readxl)
cv <- read_excel("bigkinds_corona_vaccine.xlsx", sheet = 1)

데이터 준비하기

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()
library(lubridate) 
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
cv_hls_date <- cv %>% 
  select(DATE, COMPANY, HEADLINE) %>% 
  filter(!duplicated(HEADLINE)) %>% 
  mutate(date = ymd(DATE))

텍스트 전처리

cv_hls_date
## # A tibble: 15,890 x 4
##    DATE    COMPANY  HEADLINE                                          date      
##    <chr>   <chr>    <chr>                                             <date>    
##  1 202103~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종"         2021-03-22
##  2 202103~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없어\""~ 2021-03-22
##  3 202103~ YTN      "[생생경제] SK바사. 공모주라고 무조건 성공은 아니다. 투자 시 유의할 점"~ 2021-03-22
##  4 202103~ YTN      "[생생경제] 코로나의무검사보다 근로환경개선이 더 시급(우다야 라이 민주노총 이주노조위~ 2021-03-22
##  5 202103~ 중앙일보 "\"AZ 접종 후 희귀 혈전 발생 20대 구급대원 증상 호전돼\""~ 2021-03-22
##  6 202103~ 전남일보 "이용빈, \"미얀마 군부 폭력의 희생자에 대한 인도적 지원 시급\""~ 2021-03-22
##  7 202103~ 동아일보 "“선택적 분노 김제동 선생” 신간 리뷰 삭제 논란"   2021-03-22
##  8 202103~ 중앙일보 "김제동 책 비판 리뷰 삭제 논란 \"욕설도 아닌데 검열하냐\""~ 2021-03-22
##  9 202103~ 중앙일보 "LH 두고 \"부동산 적폐\"→\"누적된 관행\"  일주일만에 말바뀐 文"~ 2021-03-22
## 10 202103~ YTN      "[더뉴스] [리얼미터] 문 대통령 지지율 '최저치'...LH 사태 여파"~ 2021-03-22
## # ... with 15,880 more rows
cv_hls_date <- cv_hls_date %>% 
  mutate(hl = str_remove_all(HEADLINE, "\\[[[:print:]]+\\]|\\<[[:print:]]+\\>"))
cv_hls_date
## # A tibble: 15,890 x 5
##    DATE   COMPANY  HEADLINE                   date       hl                     
##    <chr>  <chr>    <chr>                      <date>     <chr>                  
##  1 20210~ 중부일보 "[사설] 고령층에도 본격화된 AZ 백신 접종~ 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~
##  2 20210~ 중앙일보 "AZ \"코로나백신, 美 임상서 효과 79%~ 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 ~
##  3 20210~ YTN      "[생생경제] SK바사. 공모주라고 무조건 성~ 2021-03-22 " SK바사. 공모주라고 무조건 성공은 ~
##  4 20210~ YTN      "[생생경제] 코로나의무검사보다 근로환경개선이~ 2021-03-22 " 코로나의무검사보다 근로환경개선이 더 ~
##  5 20210~ 중앙일보 "\"AZ 접종 후 희귀 혈전 발생 20대 구~ 2021-03-22 "\"AZ 접종 후 희귀 혈전 발생 20~
##  6 20210~ 전남일보 "이용빈, \"미얀마 군부 폭력의 희생자에 대~ 2021-03-22 "이용빈, \"미얀마 군부 폭력의 희생자~
##  7 20210~ 동아일보 "“선택적 분노 김제동 선생” 신간 리뷰 삭제~ 2021-03-22 "“선택적 분노 김제동 선생” 신간 리뷰~
##  8 20210~ 중앙일보 "김제동 책 비판 리뷰 삭제 논란 \"욕설도 ~ 2021-03-22 "김제동 책 비판 리뷰 삭제 논란 \"욕~
##  9 20210~ 중앙일보 "LH 두고 \"부동산 적폐\"→\"누적된 관~ 2021-03-22 "LH 두고 \"부동산 적폐\"→\"누적~
## 10 20210~ YTN      "[더뉴스] [리얼미터] 문 대통령 지지율 '~ 2021-03-22 " 문 대통령 지지율 '최저치'...LH~
## # ... with 15,880 more rows

토큰화 작업

library(tidytext)
cv_hls_date %>% 
  unnest_tokens(word, HEADLINE, token="regex", pattern=" ")
## # A tibble: 123,854 x 5
##    DATE    COMPANY  date       hl                                    word       
##    <chr>   <chr>    <date>     <chr>                                 <chr>      
##  1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   "[사설]"   
##  2 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   "고령층에도"~
##  3 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   "본격화된" 
##  4 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   "az"       
##  5 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   "백신"     
##  6 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   "접종"     
##  7 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ "az"       
##  8 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ "\"코로나백신,"~
##  9 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ "美"       
## 10 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ "임상서"   
## # ... with 123,844 more rows

한국어 형태소 분석

https://docs.google.com/spreadsheets/d/1-9blXKjtjeKZqsf4NzHeYJCrr49-nXeRF6D80udfcwY/edit#gid=4

library(RcppMeCab)
cv_hls_tidy <- cv_hls_date %>% 
  rowid_to_column() %>% 
  unnest_tokens(word, HEADLINE, token=pos)
## Warning: Outer names are only allowed for unnamed scalar atomic inputs
cv_hls_tidy
## # A tibble: 229,232 x 6
##    rowid DATE     COMPANY  date       hl                               word     
##    <int> <chr>    <chr>    <date>     <chr>                            <chr>    
##  1     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ [/sso    
##  2     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 사설/nng 
##  3     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ ]/ssc    
##  4     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 고령/nng 
##  5     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 층/xsn   
##  6     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 에/jkb   
##  7     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 도/jx    
##  8     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 본격/xr  
##  9     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 화/xsn   
## 10     1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 된/xsv+et~
## # ... with 229,222 more rows
# 에러가 날 경우
cv_hls_date %>% 
  unnest_tokens(word, HEADLINE, token = RcppMeCab::pos)
## Warning: Outer names are only allowed for unnamed scalar atomic inputs
## # A tibble: 229,232 x 5
##    DATE     COMPANY  date       hl                                  word      
##    <chr>    <chr>    <date>     <chr>                               <chr>     
##  1 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" [/sso     
##  2 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 사설/nng  
##  3 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" ]/ssc     
##  4 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 고령/nng  
##  5 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 층/xsn    
##  6 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 에/jkb    
##  7 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 도/jx     
##  8 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 본격/xr   
##  9 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 화/xsn    
## 10 20210322 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 된/xsv+etm
## # ... with 229,222 more rows

명사만 선택

cv_hls_nouns <- cv_hls_tidy %>% 
  filter(str_detect(word, "[/]n|[/]sl"))
cv_hls_nouns
## # A tibble: 124,889 x 6
##    rowid DATE    COMPANY  date       hl                                  word   
##    <int> <chr>   <chr>    <date>     <chr>                               <chr>  
##  1     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 사설/nng~
##  2     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 고령/nng~
##  3     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" az/sl  
##  4     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 백신/nng~
##  5     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종" 접종/nng~
##  6     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ az/sl  
##  7     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 코로나/nn~
##  8     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 백신/nng~
##  9     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 美/nng 
## 10     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가~ 임상/nng~
## # ... with 124,879 more rows

품사 태그 제거

cv_hls_nouns <- cv_hls_nouns %>% 
  mutate(word = str_remove(word, "[/][a-z]+"))
cv_hls_nouns
## # A tibble: 124,889 x 6
##    rowid DATE    COMPANY  date       hl                                    word 
##    <int> <chr>   <chr>    <date>     <chr>                                 <chr>
##  1     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   사설 
##  2     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   고령 
##  3     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   az   
##  4     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   백신 
##  5     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   접종 
##  6     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ az   
##  7     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 코로나~
##  8     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 백신 
##  9     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 美   
## 10     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 임상 
## # ... with 124,879 more rows

데이터 저장하기

save(cv_hls_nouns, file="cv_hls_nouns.RData")

저장된 데이터 불러오기

load("cv_hls_nouns.RData")
cv_hls_nouns
## # A tibble: 124,889 x 6
##    rowid DATE    COMPANY  date       hl                                    word 
##    <int> <chr>   <chr>    <date>     <chr>                                 <chr>
##  1     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   사설 
##  2     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   고령 
##  3     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   az   
##  4     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   백신 
##  5     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   접종 
##  6     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ az   
##  7     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 코로나~
##  8     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 백신 
##  9     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 美   
## 10     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 임상 
## # ... with 124,879 more rows

집단 비교 분석

시기별 백신 보도 비교: 백신 접종 개시 전과 후의 주요 어휘(키워드) 비교

cv_hls_period <- cv_hls_nouns %>% 
  mutate(period = ifelse(date < as.Date("2021-02-26"),
                         "접종전","접종후"))
cv_hls_period
## # A tibble: 124,889 x 7
##    rowid DATE    COMPANY  date       hl                             word  period
##    <int> <chr>   <chr>    <date>     <chr>                          <chr> <chr> 
##  1     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 사설  접종후
##  2     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 고령  접종후
##  3     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ az    접종후
##  4     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 백신  접종후
##  5     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"~ 접종  접종후
##  6     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 ~ az    접종후
##  7     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 ~ 코로나~ 접종후
##  8     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 ~ 백신  접종후
##  9     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 ~ 美    접종후
## 10     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 ~ 임상  접종후
## # ... with 124,879 more rows
cv_hls_period %>% 
  count(period, word, sort=T)
## # A tibble: 13,211 x 3
##    period word       n
##    <chr>  <chr>  <int>
##  1 접종전 코로나  5346
##  2 접종전 백신    5139
##  3 접종전 접종    2344
##  4 접종전 명      1252
##  5 접종후 백신    1209
##  6 접종전 대통령  1066
##  7 접종후 접종     994
##  8 접종후 코로나   943
##  9 접종전 만       862
## 10 접종전 문       638
## # ... with 13,201 more rows

상위 20개 단어 비교 그래프

cv_hls_period %>% 
  group_by(period) %>% 
  count(word, sort=T) %>% 
  slice_max(n, n=20) %>% # n 기준으로 큰 값을 가진 상위 20개 단어 추출 
  ungroup %>% 
  ggplot(aes(x=reorder_within(word, n, period), 
             y=n, fill=period)) + 
  geom_col() +
  coord_flip() +
  facet_wrap(~period, scales="free_y") +
  scale_x_reordered() + # 항목이름 제거
  scale_fill_discrete(name="시기") +
  labs(x=NULL,y="빈도수")

집단 간 차이 비교: 상대적으로 중요한 단어 비교

“아스트라제네카” 혹은 “화이자”가 등장하는 헤드라인 비교

pos(enc2utf8("아스트라제네카"))
## $아스트라제네카
## [1] "아스트라/NNP" "제/NP"        "네/XSN"       "카/NNG"
cv_hls_nouns %>% 
  group_by(rowid) %>% 
  summarise(hl = paste(word, collapse=" ")) %>% 
  ungroup
## # A tibble: 15,886 x 2
##    rowid hl                                                                     
##  * <int> <chr>                                                                  
##  1     1 사설 고령 az 백신 접종                                                 
##  2     2 az 코로나 백신 美 임상 효과 혈전 위험 증가                             
##  3     3 경제 sk 바사 공모주 성공 투자 시 유의 점                               
##  4     4 경제 코로나 검사 근로 환경 개선 시급 우다 라이 민주 노총 이 주 노조 위원장~
##  5     5 az 접종 후 혈전 발생 대 구급대 원 증상 호전                            
##  6     6 이용빈 미얀마 군부 폭력 희생자 인도적 지원 시급                        
##  7     7 선택 분노 김제동 선생 신간 리뷰 삭제 논란                              
##  8     8 김제동 책 비판 리뷰 삭제 논란 욕설 검열                                
##  9     9 lh 부동산 적폐 누적 관행 일 주일 만 말 文                              
## 10    10 뉴스 리얼미터 문 대통령 지지율 최저치 lh 사태 여파                     
## # ... with 15,876 more rows
cv_hls_type <- cv_hls_nouns %>% 
  group_by(rowid) %>% 
  summarise(hl = paste(word, collapse=" ")) %>% 
  ungroup %>% 
  filter(str_detect(hl, "아스트라|AZ|화이자")) %>% 
  mutate(type = ifelse(str_detect(hl, "AZ|아스트라"), 
                "AZ", "Pfizer"))
cv_hls_type
## # A tibble: 872 x 3
##    rowid hl                                                 type  
##    <int> <chr>                                              <chr> 
##  1    25 文 아스트라 접종 하루 전날 백신 가짜 뉴스 경계심   AZ    
##  2    28 속보 방접 종위 아스트라 혈전 연관 접종 계속        AZ    
##  3    62 존슨 英 총리 저 아스트라 백신                      AZ    
##  4    64 세 이상 요양 병원 환자 az 일반인 화이자            Pfizer
##  5    65 丁 총리 아스트라 안전 문제 결론 전문가 접종 이익   AZ    
##  6    89 아스트라 접종 혈압 당뇨 부모                       AZ    
##  7   130 화이자 차 접종 시작 의료 인력 백 명 완료           Pfizer
##  8   143 신규 확진 나흘 명 오늘 화이자 차 접종 시작         Pfizer
##  9   148 이번 주 리뷰 공시 가격 폭탄 아스트라 제 카 명숙 일 AZ    
## 10   150 유럽 약청 아스트라 게+jks 이익 뇌 혈전 추가 조사   AZ    
## # ... with 862 more rows

형태소 분석 오류 수정

cv_hls_type <- cv_hls_type %>% 
  mutate(hl = str_replace_all(hl, "아스트라 제 카|아스트라 제네 카", 
                              "아스트라제네카"))
cv_hls_type
## # A tibble: 872 x 3
##    rowid hl                                                 type  
##    <int> <chr>                                              <chr> 
##  1    25 文 아스트라 접종 하루 전날 백신 가짜 뉴스 경계심   AZ    
##  2    28 속보 방접 종위 아스트라 혈전 연관 접종 계속        AZ    
##  3    62 존슨 英 총리 저 아스트라 백신                      AZ    
##  4    64 세 이상 요양 병원 환자 az 일반인 화이자            Pfizer
##  5    65 丁 총리 아스트라 안전 문제 결론 전문가 접종 이익   AZ    
##  6    89 아스트라 접종 혈압 당뇨 부모                       AZ    
##  7   130 화이자 차 접종 시작 의료 인력 백 명 완료           Pfizer
##  8   143 신규 확진 나흘 명 오늘 화이자 차 접종 시작         Pfizer
##  9   148 이번 주 리뷰 공시 가격 폭탄 아스트라제네카 명숙 일 AZ    
## 10   150 유럽 약청 아스트라 게+jks 이익 뇌 혈전 추가 조사   AZ    
## # ... with 862 more rows

비교를 위한 데이터 형태 변환

# long form 데이터
cv_hls_type %>% 
  unnest_tokens(word, hl) %>% 
  count(type, word, sort=T)
## # A tibble: 1,452 x 3
##    type   word               n
##    <chr>  <chr>          <int>
##  1 Pfizer 화이자           536
##  2 Pfizer 백신             458
##  3 AZ     백신             237
##  4 AZ     아스트라제네카   198
##  5 Pfizer 코로나           195
##  6 Pfizer 접종             187
##  7 AZ     접종             141
##  8 AZ     아스트라         137
##  9 Pfizer 승인              82
## 10 Pfizer 명                80
## # ... with 1,442 more rows
# wide form 데이터
cv_hls_type %>% 
  unnest_tokens(word, hl) %>%
  count(type, word, sort=T) %>%
  pivot_wider(names_from = type, 
              values_from = n)
## # A tibble: 1,159 x 3
##    word           Pfizer    AZ
##    <chr>           <int> <int>
##  1 화이자            536    33
##  2 백신              458   237
##  3 아스트라제네카     NA   198
##  4 코로나            195    63
##  5 접종              187   141
##  6 아스트라           NA   137
##  7 승인               82    41
##  8 명                 80    27
##  9 효과               68    35
## 10 만                 59    18
## # ... with 1,149 more rows

NA 값을 0으로 치환

cv_hls_type %>% 
  unnest_tokens(word, hl) %>%
  count(type, word, sort=T) %>%
  pivot_wider(names_from = type, 
              values_from = n,
              values_fill = list(n = 0)) # NA값을 0으로 치환
## # A tibble: 1,159 x 3
##    word           Pfizer    AZ
##    <chr>           <int> <int>
##  1 화이자            536    33
##  2 백신              458   237
##  3 아스트라제네카      0   198
##  4 코로나            195    63
##  5 접종              187   141
##  6 아스트라            0   137
##  7 승인               82    41
##  8 명                 80    27
##  9 효과               68    35
## 10 만                 59    18
## # ... with 1,149 more rows
cv_frequency_wide <- cv_hls_type %>% 
  unnest_tokens(word, hl) %>%
  count(type, word, sort=T) %>%
  pivot_wider(names_from = type, 
              values_from = n,
              values_fill = list(n = 0))
cv_frequency_wide
## # A tibble: 1,159 x 3
##    word           Pfizer    AZ
##    <chr>           <int> <int>
##  1 화이자            536    33
##  2 백신              458   237
##  3 아스트라제네카      0   198
##  4 코로나            195    63
##  5 접종              187   141
##  6 아스트라            0   137
##  7 승인               82    41
##  8 명                 80    27
##  9 효과               68    35
## 10 만                 59    18
## # ... with 1,149 more rows

오즈비 구하기

오즈비(odds ratio)는 어떤 사건이 A 조건에서 발생할 확률이 B 조건에서 발생할 확률에 비해 얼마나 더 큰지를 나타낸 값.

  1. 각 단어 비중 나타내는 변수 추가: 우선, 각 집단별로 ’각 단어의 빈도’를 ’모든 단어 빈도의 합’으로 나눈다.
cv_frequency_wide <- cv_frequency_wide %>% 
  mutate(ratio_pf = ((Pfizer+1)/sum(Pfizer+1)),
         ratio_az = ((AZ+1)/(sum(AZ+1))))
cv_frequency_wide
## # A tibble: 1,159 x 5
##    word           Pfizer    AZ ratio_pf ratio_az
##    <chr>           <int> <int>    <dbl>    <dbl>
##  1 화이자            536    33 0.0946    0.00914
##  2 백신              458   237 0.0808    0.0640 
##  3 아스트라제네카      0   198 0.000176  0.0535 
##  4 코로나            195    63 0.0345    0.0172 
##  5 접종              187   141 0.0331    0.0382 
##  6 아스트라            0   137 0.000176  0.0371 
##  7 승인               82    41 0.0146    0.0113 
##  8 명                 80    27 0.0143    0.00753
##  9 효과               68    35 0.0122    0.00968
## 10 만                 59    18 0.0106    0.00511
## # ... with 1,149 more rows
  1. 오즈비 변수 추가: 각 단어가 집단에서 차지하는 비중을 그 단어가 다른 집단에서 차지하는 비중으로 나누면 됨.
cv_frequency_wide <- cv_frequency_wide %>% 
  mutate(odds_ratio = ratio_az/ratio_pf)
cv_frequency_wide
## # A tibble: 1,159 x 6
##    word           Pfizer    AZ ratio_pf ratio_az odds_ratio
##    <chr>           <int> <int>    <dbl>    <dbl>      <dbl>
##  1 화이자            536    33 0.0946    0.00914     0.0967
##  2 백신              458   237 0.0808    0.0640      0.792 
##  3 아스트라제네카      0   198 0.000176  0.0535    304.    
##  4 코로나            195    63 0.0345    0.0172      0.499 
##  5 접종              187   141 0.0331    0.0382      1.15  
##  6 아스트라            0   137 0.000176  0.0371    211.    
##  7 승인               82    41 0.0146    0.0113      0.773 
##  8 명                 80    27 0.0143    0.00753     0.528 
##  9 효과               68    35 0.0122    0.00968     0.797 
## 10 만                 59    18 0.0106    0.00511     0.483 
## # ... with 1,149 more rows
  1. 정렬해서 상대적 중요 단어 살펴보기
cv_frequency_wide %>% arrange(odds_ratio) # Pfizer 그룹에서 빈번한 단어
## # A tibble: 1,159 x 6
##    word     Pfizer    AZ ratio_pf ratio_az odds_ratio
##    <chr>     <int> <int>    <dbl>    <dbl>      <dbl>
##  1 기술         15     0  0.00282 0.000269     0.0954
##  2 화이자      536    33  0.0946  0.00914      0.0967
##  3 차           14     0  0.00264 0.000269     0.102 
##  4 탈취         13     0  0.00247 0.000269     0.109 
##  5 국정원       10     0  0.00194 0.000269     0.139 
##  6 이스라엘     10     0  0.00194 0.000269     0.139 
##  7 日           10     0  0.00194 0.000269     0.139 
##  8 모더         59     5  0.0106  0.00161      0.153 
##  9 병동          9     0  0.00176 0.000269     0.153 
## 10 증시          9     0  0.00176 0.000269     0.153 
## # ... with 1,149 more rows
cv_frequency_wide %>% arrange(-odds_ratio) # AZ 그룹에서 빈번한 단어
## # A tibble: 1,159 x 6
##    word           Pfizer    AZ ratio_pf ratio_az odds_ratio
##    <chr>           <int> <int>    <dbl>    <dbl>      <dbl>
##  1 아스트라제네카      0   198 0.000176  0.0535      304.  
##  2 아스트라            0   137 0.000176  0.0371      211.  
##  3 고령                0    19 0.000176  0.00538      30.5 
##  4 논란                0    11 0.000176  0.00323      18.3 
##  5 혈전                0    10 0.000176  0.00296      16.8 
##  6 독일                0     8 0.000176  0.00242      13.7 
##  7 미만                0     8 0.000176  0.00242      13.7 
##  8 노인                0     7 0.000176  0.00215      12.2 
##  9 대통령              0     7 0.000176  0.00215      12.2 
## 10 보류                0     5 0.000176  0.00161       9.16
## # ... with 1,149 more rows
  1. 막대그래프 만들기
# 오즈비가 가장 높거나 낮은 단어 추출하기
?rank
## starting httpd help server ... done
cv_frequency_wide %>% filter(rank(odds_ratio)==1)
## # A tibble: 1 x 6
##   word  Pfizer    AZ ratio_pf ratio_az odds_ratio
##   <chr>  <int> <int>    <dbl>    <dbl>      <dbl>
## 1 기술      15     0  0.00282 0.000269     0.0954
cv_frequency_wide %>% filter(rank(-odds_ratio)==1)
## # A tibble: 1 x 6
##   word           Pfizer    AZ ratio_pf ratio_az odds_ratio
##   <chr>           <int> <int>    <dbl>    <dbl>      <dbl>
## 1 아스트라제네카      0   198 0.000176   0.0535       304.
cv_frequency_wide %>% 
  filter(rank(odds_ratio) <= 20 | rank(-odds_ratio) <= 20)
## # A tibble: 41 x 6
##    word           Pfizer    AZ ratio_pf ratio_az odds_ratio
##    <chr>           <int> <int>    <dbl>    <dbl>      <dbl>
##  1 화이자            536    33 0.0946   0.00914      0.0967
##  2 아스트라제네카      0   198 0.000176 0.0535     304.    
##  3 아스트라            0   137 0.000176 0.0371     211.    
##  4 모더               59     5 0.0106   0.00161      0.153 
##  5 고령                0    19 0.000176 0.00538     30.5   
##  6 회분               17     1 0.00317  0.000538     0.170 
##  7 최종               16     1 0.00299  0.000538     0.180 
##  8 기술               15     0 0.00282  0.000269     0.0954
##  9 차                 14     0 0.00264  0.000269     0.102 
## 10 탈취               13     0 0.00247  0.000269     0.109 
## # ... with 31 more rows
# 그룹 표시
cv_frequency_type <- cv_frequency_wide %>% 
  filter(rank(odds_ratio) <= 20 | rank(-odds_ratio) <= 20) %>% 
  mutate(type = ifelse(odds_ratio > 1, "AZ", "Pfizer"),
         n = ifelse(odds_ratio > 1, AZ, Pfizer))
cv_frequency_type
## # A tibble: 41 x 8
##    word           Pfizer    AZ ratio_pf ratio_az odds_ratio type       n
##    <chr>           <int> <int>    <dbl>    <dbl>      <dbl> <chr>  <int>
##  1 화이자            536    33 0.0946   0.00914      0.0967 Pfizer   536
##  2 아스트라제네카      0   198 0.000176 0.0535     304.     AZ       198
##  3 아스트라            0   137 0.000176 0.0371     211.     AZ       137
##  4 모더               59     5 0.0106   0.00161      0.153  Pfizer    59
##  5 고령                0    19 0.000176 0.00538     30.5    AZ        19
##  6 회분               17     1 0.00317  0.000538     0.170  Pfizer    17
##  7 최종               16     1 0.00299  0.000538     0.180  Pfizer    16
##  8 기술               15     0 0.00282  0.000269     0.0954 Pfizer    15
##  9 차                 14     0 0.00264  0.000269     0.102  Pfizer    14
## 10 탈취               13     0 0.00247  0.000269     0.109  Pfizer    13
## # ... with 31 more rows
# 시각화
cv_frequency_type %>% 
  ggplot(aes(x=reorder_within(word, odds_ratio, type), 
             y=odds_ratio, fill=type)) + 
  geom_col() +
  coord_flip() +
  facet_wrap(~type, scales="free") + # "free" 그래프별로 x & y축
  scale_x_reordered() + # 항목이름 제거
  labs(x=NULL,y="빈도수")

  1. 로그 오즈비 구하기: 수치의 정규성을 높이기 위해
cv_frequency_type <- cv_frequency_type %>% 
  mutate(log_odds_ratio = log(odds_ratio))
cv_frequency_type
## # A tibble: 41 x 9
##    word     Pfizer    AZ ratio_pf ratio_az odds_ratio type      n log_odds_ratio
##    <chr>     <int> <int>    <dbl>    <dbl>      <dbl> <chr> <int>          <dbl>
##  1 화이자      536    33 0.0946   0.00914      0.0967 Pfiz~   536          -2.34
##  2 아스트라제네카~      0   198 0.000176 0.0535     304.     AZ      198           5.72
##  3 아스트라      0   137 0.000176 0.0371     211.     AZ      137           5.35
##  4 모더         59     5 0.0106   0.00161      0.153  Pfiz~    59          -1.88
##  5 고령          0    19 0.000176 0.00538     30.5    AZ       19           3.42
##  6 회분         17     1 0.00317  0.000538     0.170  Pfiz~    17          -1.77
##  7 최종         16     1 0.00299  0.000538     0.180  Pfiz~    16          -1.72
##  8 기술         15     0 0.00282  0.000269     0.0954 Pfiz~    15          -2.35
##  9 차           14     0 0.00264  0.000269     0.102  Pfiz~    14          -2.28
## 10 탈취         13     0 0.00247  0.000269     0.109  Pfiz~    13          -2.22
## # ... with 31 more rows
cv_frequency_type %>% 
  group_by(type) %>% 
  filter(rank(abs(log_odds_ratio)) <= 10) %>% 
  ungroup %>% 
  ggplot(aes(x=reorder(word, log_odds_ratio), 
             y=log_odds_ratio, fill=type)) + 
  geom_col() +
  coord_flip() +
  labs(x=NULL)

집단 간 비교2: TF-IDF (Term Frequency-Inverse Document Frequency)

“오즈비”는 두 집단 간의 상대적 중요 어휘 비교에 유용. 그런데 셋 이상의 집단들에서 중요 어휘를 비교하는 방법은?

TF-IDF

모든 집단에서 자주 등장하는 단어는 집단의 특성 비교를 위해서는 유용하지 않은 자료이다. 특정 집단에서는 자주 등장하지만 다른 집단에서는 등장하지 않는 어휘(키워드)가 집단 특성 파악에는 유용하다.

TF: 전체 문서에서 단어가 등장하는 횟수 (단어 빈도) IDF: 단어가 문서(집단)에서 등장하는 횟수 (역 문서 빈도)

TF-IDF = 단어 빈도 * 역 문서 빈도

TF-IDF는 각 단어가 전체 문서에서 얼마나 자주 등장하는지와 함께 특정 문서(집단)에서만 자주 등장하는지를 함께 고려함. 결국, 특정 문서(집단)에서만 자주 등장하는 단어들이 높은 TF-IDF 값을 가지게 되고, 다른 문서(집단)에서도 자주 등장하는 단어들은 상대적으로 낮은 TF-IDF 값을 가지게 됨. 따라서, TF-IDF를 이용하면 각 문서(집단)의 특성을 보여주는 단어들을 발견할 수 있다.

비교 집단 별로 헤드라인 텍스트 합치기

cv_hls_nouns
## # A tibble: 124,889 x 6
##    rowid DATE    COMPANY  date       hl                                    word 
##    <int> <chr>   <chr>    <date>     <chr>                                 <chr>
##  1     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   사설 
##  2     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   고령 
##  3     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   az   
##  4     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   백신 
##  5     1 202103~ 중부일보 2021-03-22 " 고령층에도 본격화된 AZ 백신 접종"   접종 
##  6     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ az   
##  7     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 코로나~
##  8     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 백신 
##  9     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 美   
## 10     2 202103~ 중앙일보 2021-03-22 "AZ \"코로나백신, 美 임상서 효과 79% 혈전 위험 증가 없~ 임상 
## # ... with 124,879 more rows
cv_hls_company <- cv_hls_nouns %>% 
  group_by(COMPANY) %>% 
  summarise(hl = paste(word, collapse=" ")) %>% 
  ungroup %>% 
  mutate(hl = str_replace_all(hl, "아스트라 제 카|아스트라 제네 카", 
                              "아스트라제네카"))

cv_hls_company
## # A tibble: 44 x 2
##    COMPANY      hl                                                              
##  * <chr>        <chr>                                                           
##  1 KBS          미 마이애미 해변 등 북적 밤 시 이후 통행 금지 코로나 국제 뉴스 고개 자국 중심 하나 유럽 약화 코로나 국제 ~
##  2 MBC          하루 천 명 사망 브라질 변이 급속 확산 이슈 톡 미국 백신 접종 후 포옹 열풍 스트레이트 예고 백신 괴담 가짜 ~
##  3 OBS          월드 프레임 코로나 확산 최대호 안양시장 코로나 완치 뒤 업무 복귀 월드 레이어 뉴스 월드 브리핑 해외 중국인 유~
##  4 SBS          달째 명 안팎 동량 주 연속 증가 긴장감 방역 위반 달 동안 건 사흘 명 예상 연쇄 감염 속초 시민 전수 검사 a~
##  5 YTN          경제 sk 바사 공모주 성공 투자 시 유의 점 경제 코로나 검사 근로 환경 개선 시급 우다 라이 민주 노총 이 주~
##  6 강원도민일보 바디 텍 메드 춘천 억 원 투입 연구 개발 센터 신축 추진 내달 일 춘천 지역 세 이상 만 명 코로나 백신 접종 ~
##  7 강원일보     춘천 춘천 만 세 이상 백신 접종 내달 시작 사설 내+jkg 동시 다발 코 감염 확산 사태 the 초점 위기 이제~
##  8 경기일보     경기 인터뷰 석범 경기 복지 재단 대표 이사 기본소득 인간 삶 보장 복지 패러다임 방역 조치 연장 코로나 확산 세~
##  9 경남도민일보 김해시 가야 문화축제 월 개최 취소 도시 사이 불리 속 방역 선방 우리 일상 봄 일평균 확진 자릿수 유지 월 일 ~
## 10 경남신문     도내 코로나 신규 확진 명 세 이상 이달 말 백신 접종 성산 칼럼 김해 가야 문화축제 올해 계속 집단 감염 일 연~
## # ... with 34 more rows

언론사별 단어 빈도 구하기 (두번 이상 등장 단어만)

cv_hls_company_freq <- cv_hls_company %>% 
  unnest_tokens(word, hl) %>% 
  count(COMPANY, word) %>% 
  filter(n > 1)
cv_hls_company_freq
## # A tibble: 16,181 x 3
##    COMPANY word      n
##    <chr>   <chr> <int>
##  1 KBS     a         7
##  2 KBS     apec      3
##  3 KBS     az       11
##  4 KBS     cdc       5
##  5 KBS     d         9
##  6 KBS     day       2
##  7 KBS     et       35
##  8 KBS     eu        7
##  9 KBS     fda       7
## 10 KBS     imf       2
## # ... with 16,171 more rows

TF-IDF 구하기

tidytext 패키지의 bind_tf_idf() 함수를 이용하면 손쉽게 TF-IDF 구할 수 있다.

cv_hls_company_freq <- cv_hls_company_freq %>% 
  bind_tf_idf(term = word, # 단어
              document = COMPANY, # 문서(집단) 구분 변수
              n = n) %>% # 단어 빈도
  arrange(-tf_idf) # 높은 tf-idf 값부터 정렬
cv_hls_company_freq
## # A tibble: 16,181 x 6
##    COMPANY      word      n     tf   idf tf_idf
##    <chr>        <chr> <int>  <dbl> <dbl>  <dbl>
##  1 제민일보     제주      4 0.235   1.59 0.373 
##  2 경남도민일보 경남     17 0.0762  1.99 0.152 
##  3 전북일보     전북     19 0.0514  2.69 0.138 
##  4 경상일보     울산     61 0.0706  1.70 0.120 
##  5 광주일보     전남     22 0.0570  1.99 0.114 
##  6 전북도민일보 전북     26 0.0393  2.69 0.105 
##  7 경남신문     경남     10 0.0505  1.99 0.101 
##  8 광주매일신문 광주     17 0.0547  1.70 0.0932
##  9 한라일보     제주      4 0.0563  1.59 0.0894
## 10 무등일보     광주     35 0.0483  1.70 0.0823
## # ... with 16,171 more rows

언론사 비교를 위해 문서(집단) 선정 후 TF-IDF

cv_hls_company_freq <- cv_hls_company %>% 
  filter(COMPANY %in% c("KBS","MBC","SBS","YTN",
                        "경향신문","동아일보",
                        "조선일보","한겨레")) %>% 
  unnest_tokens(word, hl) %>% 
  count(COMPANY, word) %>% 
  filter(n > 1)

cv_hls_company_freq <- cv_hls_company_freq %>% 
  bind_tf_idf(term = word, 
              document = COMPANY, 
              n = n) %>%
  arrange(-tf_idf)
cv_hls_company_freq
## # A tibble: 5,820 x 6
##    COMPANY  word         n      tf   idf  tf_idf
##    <chr>    <chr>    <int>   <dbl> <dbl>   <dbl>
##  1 KBS      et          35 0.00692 2.08  0.0144 
##  2 KBS      빅뉴스      27 0.00533 2.08  0.0111 
##  3 YTN      뉴          60 0.00522 2.08  0.0109 
##  4 동아일보 횡설수설    15 0.00441 2.08  0.00918
##  5 조선일보 mint        45 0.00401 2.08  0.00834
##  6 한겨레   사설        25 0.0112  0.693 0.00776
##  7 YTN      큐          41 0.00357 2.08  0.00742
##  8 KBS      해설        18 0.00356 2.08  0.00740
##  9 YTN      라이브      58 0.00505 1.39  0.00700
## 10 YTN      정면        38 0.00331 2.08  0.00688
## # ... with 5,810 more rows

각 언론사의 헤드라인에서 TF-IDF 높은 단어들을 비교 그래프 만들기

cv_hls_company_freq %>% 
  group_by(COMPANY) %>% 
  slice_max(tf_idf, n=10) %>% # 각 언론사에서 TF-IDF 기준 상위 10개 단어 추출
  ungroup %>% 
  ggplot(aes(x=reorder_within(word, tf_idf, COMPANY), 
             y=tf_idf, fill=COMPANY)) + 
  geom_col() +
  coord_flip() +
  facet_wrap(~COMPANY, scales="free", ncol=4) + # "free" 그래프별로 x & y축
  scale_x_reordered() + # 항목이름 제거
  labs(x=NULL,y="TF-IDF")