Comparison Analysis

Text Mining

Description for Comparison Analysis

Yeongeun Jeon , Jung In Seo
2023-10-20

패키지 설치

pacman::p_load("readr",
               "dplyr", "tidyr",
               "stringr",
               "tidytext",
               "KoNLP",
               "ggplot2")

1. 단어 빈도 비교


# 데이터 불러오기

# 1. 문재인 전 대통령 연설문 불러오기
raw_moon <- readLines(".../speech_moon.txt",
                      encoding = "UTF-8")
moon <- raw_moon %>%
  as_tibble() %>%                            # Tibble 형태로 변환
  mutate(president = "moon")                 # 변수 president 추가

moon
# A tibble: 117 × 2
   value                                                     president
   <chr>                                                     <chr>    
 1 "정권교체 하겠습니다!"                                    moon     
 2 "  정치교체 하겠습니다!"                                  moon     
 3 "  시대교체 하겠습니다!"                                  moon     
 4 "  "                                                      moon     
 5 "  ‘불비불명(不飛不鳴)’이라는 고사가 있습니다. 남쪽 언…   moon     
 6 ""                                                        moon     
 7 "그 동안 정치와 거리를 둬 왔습니다. 그러나 암울한 시대가… moon     
 8 ""                                                        moon     
 9 ""                                                        moon     
10 "‘우리나라 대통령’이 되겠습니다."                         moon     
# ℹ 107 more rows
# 2. 박근혜 전 대통령 연설문 불러오기
raw_park <- readLines(".../speech_park.txt", 
                      encoding = "UTF-8")

park <- raw_park %>%
  as_tibble() %>%                            # Tibble 형태로 변환
  mutate(president = "park")                 # 변수 president 추가

park
# A tibble: 96 × 2
   value                                                     president
   <chr>                                                     <chr>    
 1 "존경하는 국민 여러분! 저는 오늘, 국민 한 분 한 분의 꿈…  park     
 2 ""                                                        park     
 3 "국민 여러분! 저의 삶은 대한민국과 함께 해온 시간이었습…  park     
 4 ""                                                        park     
 5 "어머니가 흉탄에 돌아가신 후, 견딜 수 없는 고통과 어려움… park     
 6 ""                                                        park     
 7 "그때부터 제 삶은 완전히 다른 길을 가야했습니다. 개인의 … park     
 8 ""                                                        park     
 9 "아버지를 잃는 또 다른 고통과 아픔을 겪고, 저는 평범한 …  park     
10 ""                                                        park     
# ℹ 86 more rows
# 두 전 대통령의 연설문을 하나의 데이터셋으로 결합하기
bind_speeches <- bind_rows(moon, park) %>%   # 두 전 대통령의 연설문을 행으로 결합
  select(president, value)                   # 변수 president와 value 선택

head(bind_speeches)                          # 데이터셋의 앞부분 출력
# A tibble: 6 × 2
  president value                                                  
  <chr>     <chr>                                                  
1 moon      "정권교체 하겠습니다!"                                 
2 moon      "  정치교체 하겠습니다!"                               
3 moon      "  시대교체 하겠습니다!"                               
4 moon      "  "                                                   
5 moon      "  ‘불비불명(不飛不鳴)’이라는 고사가 있습니다. 남쪽 언…
6 moon      ""                                                     
tail(bind_speeches)                          # 데이터셋의 뒷부분 출력
# A tibble: 6 × 2
  president value                                                     
  <chr>     <chr>                                                     
1 park      "국민들이 꿈으로만 가졌던 행복한 삶을 실제로 이룰 수 있도…
2 park      ""                                                        
3 park      "감사합니다."                                             
4 park      ""                                                        
5 park      "2012년 7월 10일"                                         
6 park      "새누리당 예비후보 박근혜"                                
# 전처리
speeches <- bind_speeches %>%
  mutate(value = str_replace_all(value,      
                                 "[^가-힣]", # [^가-힣] : 한글을 제외한 모든 문자를 의미하는 정규 표현식
                                 " "),       # 공백으로 변경
         value = str_squish(value))          # 연속된 공백 제거

speeches
# A tibble: 213 × 2
   president value                                                    
   <chr>     <chr>                                                    
 1 moon      "정권교체 하겠습니다"                                    
 2 moon      "정치교체 하겠습니다"                                    
 3 moon      "시대교체 하겠습니다"                                    
 4 moon      ""                                                       
 5 moon      "불비불명 이라는 고사가 있습니다 남쪽 언덕 나뭇가지에 앉…
 6 moon      ""                                                       
 7 moon      "그 동안 정치와 거리를 둬 왔습니다 그러나 암울한 시대가 …
 8 moon      ""                                                       
 9 moon      ""                                                       
10 moon      "우리나라 대통령 이 되겠습니다"                          
# ℹ 203 more rows
# 토큰화
speeches <- speeches %>%
  unnest_tokens(input = value,               # 토큰화를 수행할 텍스트가 포함된 변수명
                output = word,               # 출력 변수명
                token = extractNoun)         # 명사 기준으로 토큰화

speeches
# A tibble: 2,997 × 2
   president word      
   <chr>     <chr>     
 1 moon      "정권교체"
 2 moon      "하겠습니"
 3 moon      "정치"    
 4 moon      "교체"    
 5 moon      "하겠습니"
 6 moon      "시대"    
 7 moon      "교체"    
 8 moon      "하겠습니"
 9 moon      ""        
10 moon      "불비불명"
# ℹ 2,987 more rows

1-1. 단어 빈도 계산

frequency <- speeches %>%                    # 전처리 & 토큰화를 수행한 결과가 저장되어 있는 객체 "speeches"
  count(president, word) %>%                 # 연설문 각각의 단어 빈도 계산
  filter(str_count(word) > 1)                # 두 글자 이상의 단어만 추출 -> 한 글자로 된 단어 제거

frequency                            
# A tibble: 1,131 × 3
   president word         n
   <chr>     <chr>    <int>
 1 moon      가동         1
 2 moon      가사         1
 3 moon      가슴         2
 4 moon      가족         1
 5 moon      가족구조     1
 6 moon      가지         4
 7 moon      가치         3
 8 moon      각종         1
 9 moon      감당         1
10 moon      강력         3
# ℹ 1,121 more rows

1-2. 자주 사용한 단어 추출

top10 <- frequency %>%                       # 단어 빈도가 저장되어 있는 객체 in 1-1
  group_by(president) %>%                    # 변수 president에 대해 그룹화 -> 각각의 연설문에서 빈도가 높은 단어를 추출하기 위해 수행
  slice_max(n,                               # 단어의 빈도가 입력된 변수명
            n = 10)                          # 빈도가 가장 높은 10개의 단어 추출 

top10
# A tibble: 22 × 3
# Groups:   president [2]
   president word       n
   <chr>     <chr>  <int>
 1 moon      국민      21
 2 moon      일자리    21
 3 moon      나라      19
 4 moon      우리      17
 5 moon      경제      15
 6 moon      사회      14
 7 moon      성장      13
 8 moon      대통령    12
 9 moon      정치      12
10 moon      하게      12
# ℹ 12 more rows
# 문재인 전 대통령 연설문에 대한 결과만 출력
top10 %>%
  filter(president == "moon")                
# A tibble: 10 × 3
# Groups:   president [1]
   president word       n
   <chr>     <chr>  <int>
 1 moon      국민      21
 2 moon      일자리    21
 3 moon      나라      19
 4 moon      우리      17
 5 moon      경제      15
 6 moon      사회      14
 7 moon      성장      13
 8 moon      대통령    12
 9 moon      정치      12
10 moon      하게      12
# 박근혜 전 대통령 연설문에 대한 결과만 출력
top10 %>%
  filter(president == "park")               
# A tibble: 12 × 3
# Groups:   president [1]
   president word       n
   <chr>     <chr>  <int>
 1 park      국민      72
 2 park      행복      23
 3 park      여러분    20
 4 park      정부      17
 5 park      경제      15
 6 park      신뢰      11
 7 park      국가      10
 8 park      우리      10
 9 park      교육       9
10 park      사람       9
11 park      사회       9
12 park      일자리     9

Result! 박근혜 전 대통령 연설문에서 빈도가 높은 단어 12개가 출력되었다. 10개가 아닌 12개인 이유는 “교육”, “사람”, “사회”, “일자리”가 똑같이 9번씩 사용되어 빈도가 동일한 단어를 모두 추출하였기 때문이다. 빈도가 동일하더라도 원하는 개수만큼만 단어를 추출하기 위해서는 옵션 with_ties = F를 지정하면 된다.

# 빈도가 동일하더라도 원하는 개수만큼 단어 추출
top10 <- frequency %>%                       # 단어 빈도가 저장되어 있는 객체 in 1-1
  group_by(president) %>%                    # 변수 president에 대해 그룹화 -> 각각의 연설문에서 빈도가 높은 단어를 추출하기 위해 수행
  slice_max(n,                               # 단어의 빈도가 입력된 변수명
            n = 10,                          # 빈도가 가장 높은 10개의 단어 추출 
            with_ties = F)                   # 빈도가 동일하더라도 옵션 n에 지정한 개수만큼만 단어 추출

top10
# A tibble: 20 × 3
# Groups:   president [2]
   president word       n
   <chr>     <chr>  <int>
 1 moon      국민      21
 2 moon      일자리    21
 3 moon      나라      19
 4 moon      우리      17
 5 moon      경제      15
 6 moon      사회      14
 7 moon      성장      13
 8 moon      대통령    12
 9 moon      정치      12
10 moon      하게      12
11 park      국민      72
12 park      행복      23
13 park      여러분    20
14 park      정부      17
15 park      경제      15
16 park      신뢰      11
17 park      국가      10
18 park      우리      10
19 park      교육       9
20 park      사람       9
# 문재인 전 대통령 연설문에 대한 결과만 출력
top10 %>%
  filter(president == "moon")                
# A tibble: 10 × 3
# Groups:   president [1]
   president word       n
   <chr>     <chr>  <int>
 1 moon      국민      21
 2 moon      일자리    21
 3 moon      나라      19
 4 moon      우리      17
 5 moon      경제      15
 6 moon      사회      14
 7 moon      성장      13
 8 moon      대통령    12
 9 moon      정치      12
10 moon      하게      12
# 박근혜 전 대통령 연설문에 대한 결과만 출력
top10 %>%
  filter(president == "park")               
# A tibble: 10 × 3
# Groups:   president [1]
   president word       n
   <chr>     <chr>  <int>
 1 park      국민      72
 2 park      행복      23
 3 park      여러분    20
 4 park      정부      17
 5 park      경제      15
 6 park      신뢰      11
 7 park      국가      10
 8 park      우리      10
 9 park      교육       9
10 park      사람       9

Result! 옵션 with_ties = F를 지정하여 박근혜 전 대통령 연설문에서 빈도가 높은 단어 10개가 출력되었다. 똑같이 9번씩 사용한 단어 “교육”, “사람”, “사회”, “일자리” 중 “교육”과 “사람”이 포함된 이유는 빈도가 동일한 단어의 경우 원본 데이터의 정렬 순서에 따라 출력하기 때문이다.


1-3. 시각화

# 기본 막대 그래프
ggplot(top10,                               # 자주 사용한 상위 10개 단어가 저장되어 있는 객체 in 1-2
       aes(x = reorder(word, n),            # reorder : top10에서 단어에 따른 평균 사용 빈도를 이용하여 내림차순 정렬
           y = n,                           
           fill = president)) +             # 대통령에 따라 막대 색깔 다르게 표현
  geom_col() +                              # 막대 그래프
  coord_flip() +                            # 막대를 가로로 회전 
  facet_wrap(~president)                    # 변수 president의 항목별로 그래프 작성 -> 두 전 대통령 각각의 막대 그래프 작성

Result! 그래프를 보면 막대가 없는 단어가 존재한다. 이는 축을 구성하는 단어가 한 대통령의 연설문에만 포함되어 있기 때문이다. 예를 들어, “행복”은 박근혜 전 대통령의 연설문에는 존재하지만 문재인 전 대통령의 연설문에는 존재하지 않는다. 이러한 문제를 방지하기 위해 함수 facet_wrap의 옵션 scales = "free_y"을 지정한다.

# y축이 다른 막대 그래프
ggplot(top10,                               # 자주 사용한 상위 10개 단어가 저장되어 있는 객체 in 1-2
       aes(x = reorder(word, n),            # reorder : top10에서 단어에 따른 평균 사용 빈도를 이용하여 내림차순 정렬
           y = n,                           
           fill = president)) +             # 대통령에 따라 막대 색깔 다르게 표현
  geom_col() +                              # 막대 그래프
  coord_flip() +                            # 막대를 가로로 회전 
  facet_wrap(~president,                    # 변수 president의 항목별로 그래프 작성 -> 두 전 대통령 각각의 막대 그래프 작성
             scales = "free_y")             # y축 통일 X

Result! 박근혜 전 대통령의 막대 그래프를 보면, “국민”의 빈도가 너무 높아 다른 단어의 빈도 차이가 잘 드러나지 않는다.

# 전반적인 단어의 빈도가 잘 드러나도록 "국민" 제외
top10 <- frequency %>%                      # 단어 빈도가 저장되어 있는 객체 in 1-1
  filter(word != "국민") %>%                # "국민" 제외
  group_by(president) %>%                   # 변수 president에 대해 그룹화 -> 각각의 연설문에서 빈도가 높은 단어를 추출하기 위해 수행
  slice_max(n,                              # 단어의 빈도가 입력된 변수명
            n = 10,                         # 빈도가 가장 높은 10개의 단어 추출 
            with_ties = F)                  # 빈도가 동일하더라도 옵션 n에 지정한 개수만큼만 단어 추출

top10
# A tibble: 20 × 3
# Groups:   president [2]
   president word         n
   <chr>     <chr>    <int>
 1 moon      일자리      21
 2 moon      나라        19
 3 moon      우리        17
 4 moon      경제        15
 5 moon      사회        14
 6 moon      성장        13
 7 moon      대통령      12
 8 moon      정치        12
 9 moon      하게        12
10 moon      대한민국    11
11 park      행복        23
12 park      여러분      20
13 park      정부        17
14 park      경제        15
15 park      신뢰        11
16 park      국가        10
17 park      우리        10
18 park      교육         9
19 park      사람         9
20 park      사회         9
ggplot(top10,                               # "국민" 제외하고 자주 사용한 상위 10개 단어가 저장되어 있는 객체
       aes(x = reorder(word, n),            # reorder : top10에서 단어에 따른 평균 사용 빈도를 이용하여 내림차순 정렬
           y = n,                           
           fill = president)) +             # 대통령에 따라 막대 색깔 다르게 표현
  geom_col() +                              # 막대 그래프
  coord_flip() +                            # 막대를 가로로 회전 
  facet_wrap(~president,                    # 변수 president의 항목별로 그래프 작성 -> 두 전 대통령 각각의 막대 그래프 작성
             scales = "free_y")             # y축 통일 X

Result! 그래프를 보면 x축을 지정할 때 함수 reorder를 사용하여도 막대가 빈도 기준으로 완벽하게 정렬되지 않았다. 이는 그래프를 작성할 때 객체 “top10”에서 단어에 따른 평균 사용 빈도를 기준으로 x축 순서를 정했기 때문이다. 이러한 문제를 방지하기 위해 Package "tidytext"의 함수 reorder_within(x, by, within)를 사용한다.

# 항목별로 단어 빈도를 정렬한 막대 그래프
ggplot(top10,                                       # "국민" 제외하고 자주 사용한 상위 10개 단어가 저장되어 있는 객체
       aes(x = reorder_within(word, n, president),  # reorder_within : 항목별로 단어 빈도순 정렬
           y = n,                           
           fill = president)) +                     # 대통령에 따라 막대 색깔 다르게 표현
  geom_col() +                                      # 막대 그래프
  coord_flip() +                                    # 막대를 가로로 회전 
  facet_wrap(~president,                            # 변수 president의 항목별로 그래프 작성 -> 두 전 대통령 각각의 막대 그래프 작성
             scales = "free_y")                     # y축 통일 X

# 단어 뒤에 항목 이름을 제거한 막대 그래프 
ggplot(top10,                                       # "국민" 제외하고 자주 사용한 상위 10개 단어가 저장되어 있는 객체
       aes(x = reorder_within(word, n, president),  # reorder_within : 항목별로 단어 빈도순 정렬
           y = n,                           
           fill = president)) +                     # 대통령에 따라 막대 색깔 다르게 표현
  geom_col() +                                      # 막대 그래프
  coord_flip() +                                    # 막대를 가로로 회전 
  facet_wrap(~president,                            # 변수 president의 항목별로 그래프 작성 -> 두 전 대통령 각각의 막대 그래프 작성
             scales = "free_y") +                   # y축 통일 X
  scale_x_reordered()                               # 단어 뒤의 대통령 이름 제거


2. 오즈비


2-1. 데이터 형태 변환

출처 : https://tavareshugo.github.io/r-intro-tidyverse-gapminder/09-reshaping/index.html


# Long Form Dataset
df_long <- frequency %>%                            # 단어 빈도가 저장되어 있는 객체 in 1-1
  group_by(president) %>%                           # 변수 president에 대해 그룹화 -> 각각의 연설문에서 빈도가 높은 단어를 추출하기 위해 수행
  slice_max(n,                                      # 단어의 빈도가 입력된 변수명
            n = 10) %>%                             # 빈도가 가장 높은 10개의 단어 추출    
  filter(word %in% c("국민", "우리",                # "국민", "우리", "정치", "행복" 단어만 추출 
                     "정치", "행복"))

df_long
# A tibble: 6 × 3
# Groups:   president [2]
  president word      n
  <chr>     <chr> <int>
1 moon      국민     21
2 moon      우리     17
3 moon      정치     12
4 park      국민     72
5 park      행복     23
6 park      우리     10

Result! 예를 위해 “국민”, “우리”, “정치”, “행복” 단어만 추출하여 Long Form 데이터셋으로 나타내었다. “국민”은 첫 번째 행을 통해 문재인 전 대통령의 연설문에서 21번 사용하였으며, 네 번째 행을 통해 박근혜 전 대통령의 연설문에서 72번 사용하였음을 알 수 있다. “우리”는 두 번째 행과 여섯 번째 행을 통해 각각의 연설문에서 몇 번 사용하였는지 알 수 있다. 이처럼 Long Form 데이터셋은 같은 단어가 항목별로 다른 행을 구성하기 때문에 각 항목에서 해당 단어를 몇 번씩 사용하였는지 한 번에 비교하기 어렵고, 단어 빈도를 활용해 연산하기도 불편하다.

# Wide Form Dataset으로 변환
df_wide <- df_long %>%                              # Long Form Dataset
  pivot_wider(names_from = president,               # 변수명으로 입력할 값이 들어 있는 변수
              values_from = n)                      # 변수에 채워 넣을 값이 들어 있는 변수

df_wide
# A tibble: 4 × 3
  word   moon  park
  <chr> <int> <int>
1 국민     21    72
2 우리     17    10
3 정치     12    NA
4 행복     NA    23

Result! Wide Form 데이터셋으로 변환하기 위해 Package "tidyr"에서 제공하는 함수 pivot_wider를 사용한다. Wide Form 데이터셋은 한 단어가 한 행으로 구성되어있으며, 두 연설문에서 “국민”, “우리”, “정치”, “행복”을 몇 번씩 사용하였는지 쉽게 비교할 수 있다.


Caution! 앞에서 실행한 코드의 결과를 보면, 결측치 NA가 존재한다. 어떤 단어가 두 연설문 중 한 연설문에 존재하지 않으면 단어의 빈도가 계산되지 않으므로 변수 n의 값이 없어서 NA가 된다. 결측치 NA는 연산할 수 없으므로 0으로 변환해야 한다. 이러한 작업은 함수 pivot_wider의 옵션 values_fill = list(n = 0)을 지정하면 된다.

df_wide <- df_long %>%                              # Long Form Dataset
  pivot_wider(names_from = president,               # 변수명으로 입력할 값이 들어 있는 변수
              values_from = n,                      # 변수에 채워 넣을 값이 들어 있는 변수
              values_fill = list(n = 0))            # 결측치 NA를 0으로 대체

df_wide
# A tibble: 4 × 3
  word   moon  park
  <chr> <int> <int>
1 국민     21    72
2 우리     17    10
3 정치     12     0
4 행복      0    23

# 두 연설문에서 사용한 모든 단어의 빈도를 이용하여 Wide Form 형태로 변환
frequency_wide <- frequency %>%                     # 단어 빈도가 저장되어 있는 객체 in 1-1
  pivot_wider(names_from = president,               # 변수명으로 입력할 값이 들어 있는 변수
              values_from = n,                      # 변수에 채워 넣을 값이 들어 있는 변수
              values_fill = list(n = 0))            # 결측치 NA를 0으로 대체

frequency_wide
# A tibble: 955 × 3
   word      moon  park
   <chr>    <int> <int>
 1 가동         1     0
 2 가사         1     0
 3 가슴         2     0
 4 가족         1     1
 5 가족구조     1     0
 6 가지         4     0
 7 가치         3     1
 8 각종         1     0
 9 감당         1     0
10 강력         3     0
# ℹ 945 more rows

Result! 두 연설문에서 사용한 모든 단어에 대한 Wide Form 데이터셋을 생성하였다.


2-2. 오즈비 계산

\[ \begin{align*} \text{Odds ratio} = \frac{\left( \frac{n+1}{\text{total}+1} \right)_{\text{Text A}} }{ \left( \frac{n+1}{\text{total}+1} \right)_{\text{Text B}} } \end{align*} \]


2-2-1. 단어의 비중을 나타내는 변수 추가

\[ \begin{align*} \text{해당 단어의 비중} = \frac{\text{해당 단어의 빈도} }{ \text{모든 단어의 빈도 합} } \end{align*} \]

odds_df <- frequency_wide %>%                        # Wide Form Dataset in 2-1
  mutate(ratio_moon = ((moon + 1)/(sum(moon + 1))),  # 문재인 전 대통령의 연설문에서 단어의 비중 계산
         ratio_park = ((park + 1)/(sum(park + 1))))  # 박근혜 전 대통령의 연설문에서 단어의 비중 계산

odds_df
# A tibble: 955 × 5
   word      moon  park ratio_moon ratio_park
   <chr>    <int> <int>      <dbl>      <dbl>
 1 가동         1     0   0.000873   0.000552
 2 가사         1     0   0.000873   0.000552
 3 가슴         2     0   0.00131    0.000552
 4 가족         1     1   0.000873   0.00110 
 5 가족구조     1     0   0.000873   0.000552
 6 가지         4     0   0.00218    0.000552
 7 가치         3     1   0.00175    0.00110 
 8 각종         1     0   0.000873   0.000552
 9 감당         1     0   0.000873   0.000552
10 강력         3     0   0.00175    0.000552
# ℹ 945 more rows

Result! “가동”은 박근혜 전 대통령의 연설문에서 사용되지 않아 빈도가 0이지만 값에 1을 더하여 계산하므로써 단어의 비중이 0.000552로 계산되었다.


2-2-2. 오즈비 변수 추가

odds_df <- odds_df %>%
  mutate(odds_ratio = ratio_moon/ratio_park)         # 오즈비 계산 / 박근혜 전 대통령의 연설문에 비해 문재인 전 대통령의 연설문에서 얼마나 비중이 더 큰지를 나타냄
# 문재인 전 대통령의 연설문에서 상대적 비중이 큰 단어
odds_df %>%
  arrange(desc(odds_ratio))                          # 오즈비를 내림차순으로 정렬
# A tibble: 955 × 6
   word      moon  park ratio_moon ratio_park odds_ratio
   <chr>    <int> <int>      <dbl>      <dbl>      <dbl>
 1 복지국가     8     0    0.00393   0.000552       7.12
 2 세상         6     0    0.00306   0.000552       5.54
 3 여성         6     0    0.00306   0.000552       5.54
 4 정의         6     0    0.00306   0.000552       5.54
 5 강자         5     0    0.00262   0.000552       4.75
 6 공평         5     0    0.00262   0.000552       4.75
 7 대통령의     5     0    0.00262   0.000552       4.75
 8 보통         5     0    0.00262   0.000552       4.75
 9 상생         5     0    0.00262   0.000552       4.75
10 지방         5     0    0.00262   0.000552       4.75
# ℹ 945 more rows

Result! 문재인 전 대통령의 연설문에서 상대적으로 많이 사용한 단어일수록 오즈비가 크다. 결과를 보면, “복지국가”가 7.12로 오즈비가 제일 크며, 이는 박근혜 전 대통령의 연설문에 비해 문재인 전 대통령의 연설문에서 상대적으로 많이 사용했다는 것을 의미한다.

# 박근혜 전 대통령의 연설문에서 상대적 비중이 큰 단어
odds_df %>%
  arrange(odds_ratio)                                # 오즈비를 오름차순으로 정렬
# A tibble: 955 × 6
   word      moon  park ratio_moon ratio_park odds_ratio
   <chr>    <int> <int>      <dbl>      <dbl>      <dbl>
 1 박근혜       0     8   0.000436    0.00496     0.0879
 2 여러분       2    20   0.00131     0.0116      0.113 
 3 행복         3    23   0.00175     0.0132      0.132 
 4 실천         0     5   0.000436    0.00331     0.132 
 5 정보         0     5   0.000436    0.00331     0.132 
 6 투명         0     5   0.000436    0.00331     0.132 
 7 과제         0     4   0.000436    0.00276     0.158 
 8 국정운영     0     4   0.000436    0.00276     0.158 
 9 시작         0     4   0.000436    0.00276     0.158 
10 지식         0     4   0.000436    0.00276     0.158 
# ℹ 945 more rows

Result! 박근혜 전 대통령의 연설문에서 상대적으로 많이 사용한 단어일수록 오즈비가 작다. 결과를 보면, “박근혜”가 0.0879로 오즈비가 제일 작으며, 이는 문재인 전 대통령의 연설문에 비해 박근혜 전 대통령의 연설문에서 상대적으로 많이 사용했다는 것을 의미한다.

## 오즈비 변수 한 번에 추가
odds_df <- frequency_wide %>%                        # Wide Form Dataset in 2-1
  mutate(ratio_moon = ((moon + 1)/(sum(moon + 1))),  # 문재인 전 대통령의 연설문에서 단어의 비중 계산
         ratio_park = ((park + 1)/(sum(park + 1))),  # 박근혜 전 대통령의 연설문에서 단어의 비중 계산
         odds_ratio = ratio_moon/ratio_park)         # 오즈비 계산 / 박근혜 전 대통령의 연설문에 비해 문재인 전 대통령의 연설문에서 얼마나 비중이 더 큰지를 나타냄

odds_df
# A tibble: 955 × 6
   word      moon  park ratio_moon ratio_park odds_ratio
   <chr>    <int> <int>      <dbl>      <dbl>      <dbl>
 1 가동         1     0   0.000873   0.000552      1.58 
 2 가사         1     0   0.000873   0.000552      1.58 
 3 가슴         2     0   0.00131    0.000552      2.37 
 4 가족         1     1   0.000873   0.00110       0.791
 5 가족구조     1     0   0.000873   0.000552      1.58 
 6 가지         4     0   0.00218    0.000552      3.96 
 7 가치         3     1   0.00175    0.00110       1.58 
 8 각종         1     0   0.000873   0.000552      1.58 
 9 감당         1     0   0.000873   0.000552      1.58 
10 강력         3     0   0.00175    0.000552      3.17 
# ℹ 945 more rows

2-3. 상대적으로 중요한 단어 추출

top10 <- odds_df %>%                                 # 오즈비를 계산한 결과가 저장되어 있는 객체 in 2-2-2
  filter(rank(odds_ratio) <= 10 |                    # 오즈비가 낮은 하위 10개의 단어 추출
           rank(-odds_ratio) <= 10)                  # 오즈비가 높은 상위 10개의 단어 추출

top10 %>%
  arrange(desc(odds_ratio)) %>%                      # 오즈비를 내림차순으로 정렬
  print(n = Inf)                                     # 모든 행 출력
# A tibble: 20 × 6
   word      moon  park ratio_moon ratio_park odds_ratio
   <chr>    <int> <int>      <dbl>      <dbl>      <dbl>
 1 복지국가     8     0   0.00393    0.000552     7.12  
 2 세상         6     0   0.00306    0.000552     5.54  
 3 여성         6     0   0.00306    0.000552     5.54  
 4 정의         6     0   0.00306    0.000552     5.54  
 5 강자         5     0   0.00262    0.000552     4.75  
 6 공평         5     0   0.00262    0.000552     4.75  
 7 대통령의     5     0   0.00262    0.000552     4.75  
 8 보통         5     0   0.00262    0.000552     4.75  
 9 상생         5     0   0.00262    0.000552     4.75  
10 지방         5     0   0.00262    0.000552     4.75  
11 과제         0     4   0.000436   0.00276      0.158 
12 국정운영     0     4   0.000436   0.00276      0.158 
13 시작         0     4   0.000436   0.00276      0.158 
14 지식         0     4   0.000436   0.00276      0.158 
15 행복         3    23   0.00175    0.0132       0.132 
16 실천         0     5   0.000436   0.00331      0.132 
17 정보         0     5   0.000436   0.00331      0.132 
18 투명         0     5   0.000436   0.00331      0.132 
19 여러분       2    20   0.00131    0.0116       0.113 
20 박근혜       0     8   0.000436   0.00496      0.0879

Result! 함수 filter는 조건에 맞는 행만 추출하는 함수이며, 함수 rank는 값의 순위를 구하는 함수이다. 이 두 함수를 이용하여 오즈비가 높은 상위 10개와 하위 10개의 단어를 추출할 수 있다.
결과를 보면, 추출한 단어 상위 10개는 문재인 전 대통령의 연설문에서 더 자주 사용하여 오즈비가 높은 단어이다. “복지국가”, “여성”, “공평” 같은 단어를 자주 사용함으로써 문재인 전 대통령이 박근혜 전 대통령보다 복지와 평등을 더 강조했다는 것을 알 수 있다.
반대로, 하위 10개는 박근혜 전 대통령의 연설문에서 더 자주 사용하여 오즈비가 낮은 단어이다. “박근혜”, “여러분” 같은 단어를 보면 박근혜 전 대통령이 문재인 전 대통령보다 개인의 정체성과 국민과의 유대감을 더 강조했다는 것을 알 수 있다.
Caution! 단어 빈도 비교에서 단순히 사용 빈도가 높은 단어는 “국민”, “우리”, “사회” 같은 보편적인 단어라 연설문의 차이가 잘 드러나지 않았다. 반면, 오즈비 기준으로 추출한 단어는 두 연설문 중 한쪽에서 비중이 더 큰 단어이므로 이를 통해 연설문의 차이를 분명하게 알 수 있다.


2-4. 시각화

# 그래프 작성을 위한 변수 추가
top10 <- top10 %>%                                           # 오즈비가 높은 상위 10개의 단어와 낮은 하위 10개의 단어가 저장되어 있는 객체 in 2-3
  mutate(president = ifelse(odds_ratio > 1, "moon", "park"), # 오즈비가 1보다 크면 변수 president에 "moon", 그렇지 않으면 "park" 할당 -> 오즈비가 1보다 크면 문재인 전 대통령의 연설문에서 상대적 비중이 높기 때문
         n = ifelse(odds_ratio > 1, moon, park))             # 오즈비가 1보다 크면 변수 n에 변수 moon에 입력된 값 할당, 그렇지 않으면 변수 park에 입력된 값 할당

top10
# A tibble: 20 × 8
   word    moon  park ratio_moon ratio_park odds_ratio president     n
   <chr>  <int> <int>      <dbl>      <dbl>      <dbl> <chr>     <int>
 1 강자       5     0   0.00262    0.000552     4.75   moon          5
 2 공평       5     0   0.00262    0.000552     4.75   moon          5
 3 대통…      5     0   0.00262    0.000552     4.75   moon          5
 4 보통       5     0   0.00262    0.000552     4.75   moon          5
 5 복지…      8     0   0.00393    0.000552     7.12   moon          8
 6 상생       5     0   0.00262    0.000552     4.75   moon          5
 7 세상       6     0   0.00306    0.000552     5.54   moon          6
 8 여러분     2    20   0.00131    0.0116       0.113  park         20
 9 여성       6     0   0.00306    0.000552     5.54   moon          6
10 정의       6     0   0.00306    0.000552     5.54   moon          6
11 지방       5     0   0.00262    0.000552     4.75   moon          5
12 행복       3    23   0.00175    0.0132       0.132  park         23
13 과제       0     4   0.000436   0.00276      0.158  park          4
14 국정…      0     4   0.000436   0.00276      0.158  park          4
15 박근혜     0     8   0.000436   0.00496      0.0879 park          8
16 시작       0     4   0.000436   0.00276      0.158  park          4
17 실천       0     5   0.000436   0.00331      0.132  park          5
18 정보       0     5   0.000436   0.00331      0.132  park          5
19 지식       0     4   0.000436   0.00276      0.158  park          4
20 투명       0     5   0.000436   0.00331      0.132  park          5
ggplot(top10,                                      
       aes(x = reorder_within(word, n, president),  # reorder_within : 항목별로 단어 빈도순 정렬
           y = n,                           
           fill = president)) +                     # 대통령에 따라 막대 색깔 다르게 표현
  geom_col() +                                      # 막대 그래프
  coord_flip() +                                    # 막대를 가로로 회전 
  facet_wrap(~president,                            # 변수 president의 항목별로 그래프 작성 -> 두 전 대통령 각각의 막대 그래프 작성
             scales = "free_y") +                   # y축 통일 X
  scale_x_reordered()                               # 단어 뒤의 대통령 이름 제거

Result! 그래프를 보면, 전반적으로 박근혜 전 대통령의 연설문에서 단어 빈도가 높으며 문재인 전 대통령의 연설문에서는 낮은 것처럼 보인다. 이는 박근혜 전 대통령의 연설문에서 가장 많이 사용한 단어인 “행복”의 빈도를 기준으로 두 그래프의 x축 크기를 똑같이 고정했기 때문이다. 그래프별로 x축 크기를 다르게 정해야 각 연설문의 단어 비중을 제대로 알 수 있다. 이를 위해 함수 facet_wrap의 옵션 scales = "free"을 지정하여 x축과 y축의 크기를 모두 그래프별로 정할 수 있다.

ggplot(top10,                                      
       aes(x = reorder_within(word, n, president),  # reorder_within : 항목별로 단어 빈도순 정렬
           y = n,                           
           fill = president)) +                     # 대통령에 따라 막대 색깔 다르게 표현
  geom_col() +                                      # 막대 그래프
  coord_flip() +                                    # 막대를 가로로 회전 
  facet_wrap(~president,                            # 변수 president의 항목별로 그래프 작성 -> 두 전 대통령 각각의 막대 그래프 작성
             scales = "free") +                     # x축과 y축 통일 X
  scale_x_reordered()                               # 단어 뒤의 대통령 이름 제거

Result! 그래프를 보면, 각 연설문에서 많이 사용한 단어 “복지국가”와 “행복”의 막대 길이는 같지만 빈도가 다르다. 이처럼 x축 크기가 그래프마다 다르면 막대 길이가 같아도 실제 값은 다르기 때문에 해석할 때 조심해야 한다.
Caution! 오즈비를 이용해 만든 막대 그래프는 각 텍스트에서 상대적으로 중요한 단어가 무엇인지 표현하기 위해 만든다. 막대 길이를 보고 두 텍스트의 단어 빈도를 비교하면 안 되고, 각 텍스트에서 상대적으로 중요한 단어가 무엇인지만 살펴봐야 한다.


2-5. 주요 단어가 사용된 문장 추출

speeches_sentence <- bind_speeches %>%              # 두 연설문의 원문을 하나의 데이터셋으로 결합한 Dataset in 1
  as_tibble() %>%                                   # Tibble 형태로 변환
  unnest_tokens(input = value,                      # 토큰화를 수행할 텍스트가 포함된 변수명
                output = sentence,                  # 출력 변수명
                token = "sentences")                # 문장 기준으로 토큰화

head(speeches_sentence)                             # 토큰화 결과 앞부분 출력
# A tibble: 6 × 2
  president sentence                                                 
  <chr>     <chr>                                                    
1 moon      "정권교체 하겠습니다!"                                   
2 moon      "정치교체 하겠습니다!"                                   
3 moon      "시대교체 하겠습니다!"                                   
4 moon      ""                                                       
5 moon      "‘불비불명(不飛不鳴)’이라는 고사가 있습니다."            
6 moon      "남쪽 언덕 나뭇가지에 앉아, 3년 동안 날지도 울지도 않는 …
tail(speeches_sentence)                             # 토큰화 결과 뒷부분 출력
# A tibble: 6 × 2
  president sentence                                                  
  <chr>     <chr>                                                     
1 park      국민 여러분의 행복이 곧 저의 행복입니다.                  
2 park      사랑하는 조국 대한민국과 국민 여러분을 위해, 앞으로 머나 …
3 park      그 길을 함께 해주시길 부탁드립니다.                       
4 park      감사합니다.                                               
5 park      2012년 7월 10일                                           
6 park      새누리당 예비후보 박근혜                                  
speeches_sentence %>%
  filter(president == "moon" & str_detect(sentence, "복지국가"))  # 문재인 전 대통령의 연설문에서 "복지국가"가 포함된 문장 추출
# A tibble: 8 × 2
  president sentence                                                  
  <chr>     <chr>                                                     
1 moon      ‘강한 복지국가’를 향해 담대하게 나아가겠습니다.           
2 moon      2백 년 전 이와 같은 소득재분배, 복지국가의 사상을 가진 위…
3 moon      이제 우리는 복지국가를 향해 담대하게 나아갈 때입니다.     
4 moon      부자감세, 4대강 사업 같은 시대착오적 과오를 청산하고, 하… 
5 moon      우리는 지금 복지국가로 가느냐, 양극화의 분열된 국가로 가… 
6 moon      강한 복지국가일수록 국가 경쟁력도 더 높습니다.            
7 moon      결국 복지국가로 가는 길은 사람에 대한 투자, 일자리 창출, …
8 moon      우리는 과감히 강한 보편적 복지국가로 가야 합니다.         
speeches_sentence %>%
  filter(president == "park" & str_detect(sentence, "행복"))      # 박근혜 전 대통령의 연설문에서 "행복"이 포함된 문장 추출
# A tibble: 19 × 2
   president sentence                                                 
   <chr>     <chr>                                                    
 1 park      저는 오늘, 국민 한 분 한 분의 꿈이 이루어지는 행복한 대… 
 2 park      국가는 발전했고, 경제는 성장했다는데, 나의 삶은 나아지지…
 3 park      과거에는 국가의 발전이 국민의 행복으로 이어졌습니다.     
 4 park      개인의 창의력이 중요한 지식기반사회에서는 국민 한 사람, …
 5 park      이제 국정운영의 패러다임을 국가에서 국민으로, 개인의 삶… 
 6 park      국민 개개인의 꿈을 향한 노력이 국가를 발전시키고 국가 발…
 7 park      저는 ‘경제민주화 실현’, ‘일자리 창출’, 그리고 ‘한국…     
 8 park      국민행복의 길을 열어갈 첫 번째 과제로, 저는 경제민주화를…
 9 park      국민행복의 길을 열어갈 두 번째 과제로, 저는 좋은 일자리 …
10 park      국민행복의 길을 열어갈 세 번째 과제로, 우리의 실정에 맞… 
11 park      저는 국민행복을 위해 ‘경제민주화-일자리-복지’를 아우르…  
12 park      모든 계층의 국민이 함께 참여해 만들고, 정부와 기업, 지역…
13 park      50년 전 경제개발 5개년 계획이 산업화의 기적을 이뤄냈듯,… 
14 park      저는 지속가능한 국민 행복을 만들 수 있도록,사람에 대한 … 
15 park      저 박근혜, 경쟁과 입시에 매몰된 교육을‘함께하는 행복교…  
16 park      존경하는 국민여러분, 국민행복을 위한 노력이 안정적으로 … 
17 park      국민 여러분, 국민행복의 꿈을 이뤄내기 위해서는, 먼저 정… 
18 park      국민들이 꿈으로만 가졌던 행복한 삶을 실제로 이룰 수 있도…
19 park      국민 여러분의 행복이 곧 저의 행복입니다.                 

Result! 함수 filterstr_detect를 이용하여 각 연설문에서 주요 단어를 사용한 문장을 추출할 수 있다. 추출한 문장을 보면 단어가 어떤 의미로 사용되었는지 알 수 있다.


2-6. 중요도가 비슷한 단어 추출

odds_df %>%                                  # 오즈비를 계산한 결과가 저장되어 있는 객체 in 2-2-2
  arrange(abs(1 - odds_ratio)) %>%           # 오즈비가 1에 가까운 단어순으로 정렬
  head(10)                                   # 상위 10개 단어 추출
# A tibble: 10 × 6
   word    moon  park ratio_moon ratio_park odds_ratio
   <chr>  <int> <int>      <dbl>      <dbl>      <dbl>
 1 때문       4     3    0.00218    0.00221      0.989
 2 강화       3     2    0.00175    0.00165      1.06 
 3 부담       3     2    0.00175    0.00165      1.06 
 4 세계       3     2    0.00175    0.00165      1.06 
 5 책임       3     2    0.00175    0.00165      1.06 
 6 협력       3     2    0.00175    0.00165      1.06 
 7 거대       2     1    0.00131    0.00110      1.19 
 8 교체       2     1    0.00131    0.00110      1.19 
 9 근본적     2     1    0.00131    0.00110      1.19 
10 기반       2     1    0.00131    0.00110      1.19 

Result! 출력 결과를 보면 대부분 보편적인 의미를 지니는 단어이며, 이러한 단어들은 빈도가 낮기 때문에 강조한 단어는 아니다.

# 중요도가 비슷하면서 빈도가 높은 단어 추출
odds_df %>%                                  # 오즈비를 계산한 결과가 저장되어 있는 객체 in 2-2-2
  filter(moon >= 5 & park >= 5) %>%          # 두 연설문에서 5번 이상 사용한 단어만 추출
  arrange(abs(1 - odds_ratio)) %>%           # 오즈비가 1에 가까운 단어순으로 정렬
  head(10)                                   # 상위 10개 단어 추출
# A tibble: 10 × 6
   word      moon  park ratio_moon ratio_park odds_ratio
   <chr>    <int> <int>      <dbl>      <dbl>      <dbl>
 1 사회        14     9    0.00655    0.00552      1.19 
 2 사람         9     9    0.00436    0.00552      0.791
 3 경제        15    15    0.00698    0.00883      0.791
 4 지원         5     5    0.00262    0.00331      0.791
 5 우리        17    10    0.00786    0.00607      1.29 
 6 불안         7     8    0.00349    0.00496      0.703
 7 산업         9     5    0.00436    0.00331      1.32 
 8 대한민국    11     6    0.00524    0.00386      1.36 
 9 국가         7    10    0.00349    0.00607      0.576
10 교육         6     9    0.00306    0.00552      0.554

Result! 두 연설문 모두 “사회”, “사람”, “경제” 등을 강조했음을 알 수 있다.


3. 로그 오즈비



3-1. 로그 오즈비 변수 추가

logodds_df <- odds_df %>%                    # 오즈비를 계산한 결과가 저장되어 있는 객체 in 2-2-2
  mutate(log_odds_ratio = log(odds_ratio))   # 로그 오즈비 계산

# 문재인 전 대통령의 연설문에서 상대적 비중이 큰 단어
logodds_df %>%
  arrange(desc(log_odds_ratio))              # 로그 오즈비 내림차순 정렬
# A tibble: 955 × 7
   word     moon  park ratio_moon ratio_park odds_ratio log_odds_ratio
   <chr>   <int> <int>      <dbl>      <dbl>      <dbl>          <dbl>
 1 복지국…     8     0    0.00393   0.000552       7.12           1.96
 2 세상        6     0    0.00306   0.000552       5.54           1.71
 3 여성        6     0    0.00306   0.000552       5.54           1.71
 4 정의        6     0    0.00306   0.000552       5.54           1.71
 5 강자        5     0    0.00262   0.000552       4.75           1.56
 6 공평        5     0    0.00262   0.000552       4.75           1.56
 7 대통령…     5     0    0.00262   0.000552       4.75           1.56
 8 보통        5     0    0.00262   0.000552       4.75           1.56
 9 상생        5     0    0.00262   0.000552       4.75           1.56
10 지방        5     0    0.00262   0.000552       4.75           1.56
# ℹ 945 more rows
# 박근혜 전 대통령의 연설문에서 상대적 비중이 큰 단어
logodds_df %>%
  arrange(log_odds_ratio)                    # 로그 오즈비 오름차순 정렬
# A tibble: 955 × 7
   word     moon  park ratio_moon ratio_park odds_ratio log_odds_ratio
   <chr>   <int> <int>      <dbl>      <dbl>      <dbl>          <dbl>
 1 박근혜      0     8   0.000436    0.00496     0.0879          -2.43
 2 여러분      2    20   0.00131     0.0116      0.113           -2.18
 3 행복        3    23   0.00175     0.0132      0.132           -2.03
 4 실천        0     5   0.000436    0.00331     0.132           -2.03
 5 정보        0     5   0.000436    0.00331     0.132           -2.03
 6 투명        0     5   0.000436    0.00331     0.132           -2.03
 7 과제        0     4   0.000436    0.00276     0.158           -1.84
 8 국정운…     0     4   0.000436    0.00276     0.158           -1.84
 9 시작        0     4   0.000436    0.00276     0.158           -1.84
10 지식        0     4   0.000436    0.00276     0.158           -1.84
# ℹ 945 more rows
# 두 연설문에서 비중이 비슷한 단어
logodds_df %>%
  arrange(abs(log_odds_ratio))
# A tibble: 955 × 7
   word    moon  park ratio_moon ratio_park odds_ratio log_odds_ratio
   <chr>  <int> <int>      <dbl>      <dbl>      <dbl>          <dbl>
 1 때문       4     3    0.00218    0.00221      0.989        -0.0109
 2 강화       3     2    0.00175    0.00165      1.06          0.0537
 3 부담       3     2    0.00175    0.00165      1.06          0.0537
 4 세계       3     2    0.00175    0.00165      1.06          0.0537
 5 책임       3     2    0.00175    0.00165      1.06          0.0537
 6 협력       3     2    0.00175    0.00165      1.06          0.0537
 7 거대       2     1    0.00131    0.00110      1.19          0.171 
 8 교체       2     1    0.00131    0.00110      1.19          0.171 
 9 근본적     2     1    0.00131    0.00110      1.19          0.171 
10 기반       2     1    0.00131    0.00110      1.19          0.171 
# ℹ 945 more rows

3-2. 시각화

# 그래프 작성을 위한 변수 추가
top10 <- logodds_df %>%                                                 # 로그 오즈비가 저장되어 있는 객체 in 3-1
  group_by(president = ifelse(log_odds_ratio > 0, "moon", "park")) %>%  # 로그 오즈비가 양수이면 변수 president에 "moon", 그렇지 않으면 "park" 할당한 후 그룹화
  slice_max(abs(log_odds_ratio), n = 10,                                # 로그 오즈비의 절댓값 기준으로 상위 10개의 단어 추출 -> 앞에서 그룹화를 수행했기 때문에 각 연설문에서 상위 10개의 단어 추출
            with_ties = F)                                              # 빈도가 동일하더라도 옵션 n에 지정한 개수만큼만 단어 추출

top10 %>% 
  arrange(desc(log_odds_ratio)) %>%                                     # 로그 오즈비 내림차순 정렬
  select(word, log_odds_ratio, president) %>%                           # 변수 word, log_odds_ratio, president 선택
  print(n = Inf)                                                        # 모든 행 출력
# A tibble: 20 × 3
# Groups:   president [2]
   word     log_odds_ratio president
   <chr>             <dbl> <chr>    
 1 복지국가           1.96 moon     
 2 세상               1.71 moon     
 3 여성               1.71 moon     
 4 정의               1.71 moon     
 5 강자               1.56 moon     
 6 공평               1.56 moon     
 7 대통령의           1.56 moon     
 8 보통               1.56 moon     
 9 상생               1.56 moon     
10 지방               1.56 moon     
11 과제              -1.84 park     
12 국정운영          -1.84 park     
13 시작              -1.84 park     
14 지식              -1.84 park     
15 행복              -2.03 park     
16 실천              -2.03 park     
17 정보              -2.03 park     
18 투명              -2.03 park     
19 여러분            -2.18 park     
20 박근혜            -2.43 park     
ggplot(top10, 
       aes(x = reorder(word, log_odds_ratio),        # reorder : top10에서 단어에 따른 평균 로그 오즈비를 이용하여 내림차순 정렬
           y = log_odds_ratio,             
           fill = president)) +                      # 대통령에 따라 막대 색깔을 다르게 표현
  geom_col() +                                       # 막대 그래프
  coord_flip()                                       # 막대를 가로로 회전

Caution! 오즈비/로그 오즈비는 두 조건의 확률을 이용해 계산하므로 세 개 이상의 텍스트를 비교할 때 적절하지 않다는 단점이 있다. 텍스트를 둘씩 짝지어 따로 비교할 수도 있지만, 비교할 텍스트가 많으면 계산 절차가 번거롭고 결과를 해석하기 어렵기 때문에 효율적이지 않다.


4. TF-IDF





# 데이터 불러오기
# speeches_presidents : 역대 대통령의 대선 출마 선언문을 담은 데이터 파일
raw_speeches <- read_csv(".../speeches_presidents.csv")
raw_speeches
# A tibble: 4 × 2
  president value                                                     
  <chr>     <chr>                                                     
1 문재인    "정권교체 하겠습니다!   정치교체 하겠습니다!   시대교체 … 
2 박근혜    "존경하는 국민 여러분! 저는 오늘, 국민 한 분 한 분의 꿈이…
3 이명박    "존경하는 국민 여러분, 사랑하는 한나라당 당원 동지 여러분…
4 노무현    "어느때인가 부터 제가 대통령이 되겠다고 말을 하기 시작했… 
# 전처리
speeches <- raw_speeches %>%
  mutate(value = str_replace_all(value,      
                                 "[^가-힣]", # [^가-힣] : 한글을 제외한 모든 문자를 의미하는 정규 표현식
                                 " "),       # 공백으로 변경
         value = str_squish(value))          # 연속된 공백 제거

speeches
# A tibble: 4 × 2
  president value                                                     
  <chr>     <chr>                                                     
1 문재인    정권교체 하겠습니다 정치교체 하겠습니다 시대교체 하겠습니…
2 박근혜    존경하는 국민 여러분 저는 오늘 국민 한 분 한 분의 꿈이 이…
3 이명박    존경하는 국민 여러분 사랑하는 한나라당 당원 동지 여러분 … 
4 노무현    어느때인가 부터 제가 대통령이 되겠다고 말을 하기 시작했습…
# 토큰화
speeches <- speeches %>%
  unnest_tokens(input = value,               # 토큰화를 수행할 텍스트가 포함된 변수명
                output = word,               # 출력 변수명
                token = extractNoun)         # 명사 기준으로 토큰화
speeches
# A tibble: 3,838 × 2
   president word    
   <chr>     <chr>   
 1 문재인    정권교체
 2 문재인    정치    
 3 문재인    교체    
 4 문재인    시대    
 5 문재인    교체    
 6 문재인    불비불명
 7 문재인    고사    
 8 문재인    남쪽    
 9 문재인    언덕    
10 문재인    나뭇가지
# ℹ 3,828 more rows

4-1. 단어 빈도 계산

# 단어 빈도 구하기
frequency <- speeches %>%                    # 전처리 & 토큰화를 수행한 결과가 저장되어 있는 객체 "speeches"
  count(president, word) %>%                 # 연설문 각각의 단어 빈도 계산
  filter(str_count(word) > 1)                # 두 글자 이상의 단어만 추출 -> 한 글자로 된 단어 제거

frequency   
# A tibble: 1,513 × 3
   president word      n
   <chr>     <chr> <int>
 1 노무현    가슴      2
 2 노무현    가훈      2
 3 노무현    갈등      1
 4 노무현    감옥      1
 5 노무현    강자      1
 6 노무현    개편      4
 7 노무현    개혁      4
 8 노무현    건국      1
 9 노무현    경선      1
10 노무현    경쟁      1
# ℹ 1,503 more rows

4-2. TF-IDF 계산

frequency <- frequency %>%           # 단어 빈도가 저장되어 있는 객체 in 4-1
  bind_tf_idf(term = word,           # 단어가 입력되어 있는 변수
              document = president,  # 텍스트 구분 변수
              n = n) %>%             # 단어 빈도가 입력되어 있는 변수
  arrange(desc(tf_idf))              # TF-IDF 내림차순 정렬

frequency
# A tibble: 1,513 × 6
   president word         n      tf   idf tf_idf
   <chr>     <chr>    <int>   <dbl> <dbl>  <dbl>
 1 노무현    공식         6 0.0163  1.39  0.0227
 2 노무현    비젼         6 0.0163  1.39  0.0227
 3 노무현    정계         6 0.0163  1.39  0.0227
 4 이명박    리더십       6 0.0158  1.39  0.0219
 5 노무현    권력         9 0.0245  0.693 0.0170
 6 노무현    개편         4 0.0109  1.39  0.0151
 7 이명박    당원         4 0.0105  1.39  0.0146
 8 이명박    동지         4 0.0105  1.39  0.0146
 9 이명박    일류국가     4 0.0105  1.39  0.0146
10 박근혜    박근혜       8 0.00962 1.39  0.0133
# ℹ 1,503 more rows

Result! 결과를 보면 변수 tf, idf, tf-idf가 추가되었다. 변수 tf에 입력된 값은 해당 문서에서 몇 번 사용하였는지를 나타내는 단순 빈도가 아니라 단어 사용 비율(“해당 단어의 빈도 수/모든 단어의 빈도 합”)을 의미한다.


Caution! TF-IDF를 이용하면 텍스트의 특징을 드러내는 중요한 단어가 무엇인지 파악할 수 있다. 변수 tf-idf가 높은 단어를 살펴보면 각 대통령이 다른 대통령들과 달리 무엇을 강조했는지 알 수 있다. 반면, TF-IDF가 낮은 단어를 살펴보면 역대 대통령들이 공통으로 사용한 흔한 단어가 무엇인지 파악할 수 있다.

# 문재인 전 대통령의 연설문만 추출
frequency %>% 
  filter(president == "문재인") %>%
  arrange(desc(tf_idf))                   # TF-IDF가 높은 단어순으로 정렬
# A tibble: 688 × 6
   president word         n      tf   idf  tf_idf
   <chr>     <chr>    <int>   <dbl> <dbl>   <dbl>
 1 문재인    복지국가     8 0.00608 1.39  0.00843
 2 문재인    여성         6 0.00456 1.39  0.00633
 3 문재인    공평         5 0.00380 1.39  0.00527
 4 문재인    담쟁이       5 0.00380 1.39  0.00527
 5 문재인    대통령의     5 0.00380 1.39  0.00527
 6 문재인    보통         5 0.00380 1.39  0.00527
 7 문재인    상생         5 0.00380 1.39  0.00527
 8 문재인    우리나라    10 0.00760 0.693 0.00527
 9 문재인    지방         5 0.00380 1.39  0.00527
10 문재인    확대        10 0.00760 0.693 0.00527
# ℹ 678 more rows
frequency %>% 
  filter(president == "문재인") %>%
  arrange(tf_idf)                         # TF-IDF가 낮은 단어순으로 정렬
# A tibble: 688 × 6
   president word       n       tf   idf tf_idf
   <chr>     <chr>  <int>    <dbl> <dbl>  <dbl>
 1 문재인    경쟁       6 0.00456      0      0
 2 문재인    경제      15 0.0114       0      0
 3 문재인    고통       4 0.00304      0      0
 4 문재인    과거       1 0.000760     0      0
 5 문재인    국민      21 0.0160       0      0
 6 문재인    기회       5 0.00380      0      0
 7 문재인    대통령    12 0.00913      0      0
 8 문재인    동안       2 0.00152      0      0
 9 문재인    들이       9 0.00684      0      0
10 문재인    마음       2 0.00152      0      0
# ℹ 678 more rows
# 박근혜 전 대통령의 연설문만 추출
frequency %>% 
  filter(president == "박근혜") %>%
  arrange(desc(tf_idf))                   # TF-IDF가 높은 단어순으로 정렬
# A tibble: 407 × 6
   president word         n      tf   idf  tf_idf
   <chr>     <chr>    <int>   <dbl> <dbl>   <dbl>
 1 박근혜    박근혜       8 0.00962 1.39  0.0133 
 2 박근혜    정보         5 0.00601 1.39  0.00833
 3 박근혜    투명         5 0.00601 1.39  0.00833
 4 박근혜    행복        23 0.0276  0.288 0.00795
 5 박근혜    교육         9 0.0108  0.693 0.00750
 6 박근혜    국정운영     4 0.00481 1.39  0.00666
 7 박근혜    정부        17 0.0204  0.288 0.00588
 8 박근혜    개개인       3 0.00361 1.39  0.00500
 9 박근혜    개인         3 0.00361 1.39  0.00500
10 박근혜    공개         3 0.00361 1.39  0.00500
# ℹ 397 more rows
frequency %>% 
  filter(president == "박근혜") %>%
  arrange(tf_idf)                         # TF-IDF가 낮은 단어순으로 정렬
# A tibble: 407 × 6
   president word       n      tf   idf tf_idf
   <chr>     <chr>  <int>   <dbl> <dbl>  <dbl>
 1 박근혜    경쟁       1 0.00120     0      0
 2 박근혜    경제      15 0.0180      0      0
 3 박근혜    고통       4 0.00481     0      0
 4 박근혜    과거       2 0.00240     0      0
 5 박근혜    국민      72 0.0865      0      0
 6 박근혜    기회       1 0.00120     0      0
 7 박근혜    대통령     3 0.00361     0      0
 8 박근혜    동안       3 0.00361     0      0
 9 박근혜    들이       3 0.00361     0      0
10 박근혜    마음       3 0.00361     0      0
# ℹ 397 more rows
# 이명박 전 대통령의 연설문만 추출
frequency %>% 
  filter(president == "이명박") %>%
  arrange(desc(tf_idf))                   # TF-IDF가 높은 단어순으로 정렬
# A tibble: 202 × 6
   president word         n      tf   idf  tf_idf
   <chr>     <chr>    <int>   <dbl> <dbl>   <dbl>
 1 이명박    리더십       6 0.0158  1.39  0.0219 
 2 이명박    당원         4 0.0105  1.39  0.0146 
 3 이명박    동지         4 0.0105  1.39  0.0146 
 4 이명박    일류국가     4 0.0105  1.39  0.0146 
 5 이명박    한나라       7 0.0184  0.693 0.0128 
 6 이명박    나라        15 0.0395  0.288 0.0114 
 7 이명박    도약         3 0.00789 1.39  0.0109 
 8 이명박    일하         3 0.00789 1.39  0.0109 
 9 이명박    사랑         5 0.0132  0.693 0.00912
10 이명박    인생         5 0.0132  0.693 0.00912
# ℹ 192 more rows
frequency %>% 
  filter(president == "이명박") %>%
  arrange(tf_idf)                         # TF-IDF가 낮은 단어순으로 정렬
# A tibble: 202 × 6
   president word       n      tf   idf tf_idf
   <chr>     <chr>  <int>   <dbl> <dbl>  <dbl>
 1 이명박    경쟁       3 0.00789     0      0
 2 이명박    경제       5 0.0132      0      0
 3 이명박    고통       1 0.00263     0      0
 4 이명박    과거       1 0.00263     0      0
 5 이명박    국민      13 0.0342      0      0
 6 이명박    기회       3 0.00789     0      0
 7 이명박    대통령     4 0.0105      0      0
 8 이명박    동안       1 0.00263     0      0
 9 이명박    들이       1 0.00263     0      0
10 이명박    마음       1 0.00263     0      0
# ℹ 192 more rows
# 노무현 전 대통령의 연설문만 추출
frequency %>% 
  filter(president == "노무현") %>%
  arrange(desc(tf_idf))                   # TF-IDF가 높은 단어순으로 정렬
# A tibble: 216 × 6
   president word         n      tf   idf  tf_idf
   <chr>     <chr>    <int>   <dbl> <dbl>   <dbl>
 1 노무현    공식         6 0.0163  1.39  0.0227 
 2 노무현    비젼         6 0.0163  1.39  0.0227 
 3 노무현    정계         6 0.0163  1.39  0.0227 
 4 노무현    권력         9 0.0245  0.693 0.0170 
 5 노무현    개편         4 0.0109  1.39  0.0151 
 6 노무현    국회의원     3 0.00817 1.39  0.0113 
 7 노무현    남북대화     3 0.00817 1.39  0.0113 
 8 노무현    총리         3 0.00817 1.39  0.0113 
 9 노무현    가훈         2 0.00545 1.39  0.00755
10 노무현    개혁         4 0.0109  0.693 0.00755
# ℹ 206 more rows
frequency %>% 
  filter(president == "노무현") %>%
  arrange(tf_idf)                         # TF-IDF가 낮은 단어순으로 정렬
# A tibble: 216 × 6
   president word       n      tf   idf tf_idf
   <chr>     <chr>  <int>   <dbl> <dbl>  <dbl>
 1 노무현    경쟁       1 0.00272     0      0
 2 노무현    경제       1 0.00272     0      0
 3 노무현    고통       1 0.00272     0      0
 4 노무현    과거       1 0.00272     0      0
 5 노무현    국민       7 0.0191      0      0
 6 노무현    기회       1 0.00272     0      0
 7 노무현    대통령     6 0.0163      0      0
 8 노무현    동안       2 0.00545     0      0
 9 노무현    들이       4 0.0109      0      0
10 노무현    마음       1 0.00272     0      0
# ℹ 206 more rows

4-3. 시각화

# 1. 주요 단어 추출 (TF-IDF가 높은 상위 단어 추출)
top10 <- frequency %>%                    # TF-IDF가 저장되어 있는 객체 in 4-2
  group_by(president) %>%                 # 변수 president에 대해 그룹화 -> 각각의 연설문에서 빈도가 높은 단어를 추출하기 위해 수행
  slice_max(tf_idf, 
            n = 10,                       # TF-IDF가 높은 상위 10개 단어 추출
            with_ties = F)                # 빈도가 동일하더라도 옵션 n에 지정한 개수만큼만 단어 추출

top10
# A tibble: 40 × 6
# Groups:   president [4]
   president word         n      tf   idf  tf_idf
   <chr>     <chr>    <int>   <dbl> <dbl>   <dbl>
 1 노무현    공식         6 0.0163  1.39  0.0227 
 2 노무현    비젼         6 0.0163  1.39  0.0227 
 3 노무현    정계         6 0.0163  1.39  0.0227 
 4 노무현    권력         9 0.0245  0.693 0.0170 
 5 노무현    개편         4 0.0109  1.39  0.0151 
 6 노무현    국회의원     3 0.00817 1.39  0.0113 
 7 노무현    남북대화     3 0.00817 1.39  0.0113 
 8 노무현    총리         3 0.00817 1.39  0.0113 
 9 노무현    가훈         2 0.00545 1.39  0.00755
10 노무현    개혁         4 0.0109  0.693 0.00755
# ℹ 30 more rows
# 2. 그래프 순서를 정하기 위한 Factor 변환
top10$president <- factor(top10$president,                                    # factor : 범주형으로 변환
                          levels = c("문재인", "박근혜", "이명박", "노무현")) # levels = 그래프 순서

# 3. 막대 그래프
ggplot(top10, 
       aes(x = reorder_within(word, tf_idf, president),  # reorder_within : 항목별로 단어 빈도순 정렬
           y = tf_idf,          
            fill = president)) +                         # 대통령에 따라 막대 색깔을 다르게 표현  
  geom_col(show.legend = F) +                            # 막대 그래프 
  coord_flip() +                                         # 막대를 가로로 회전
  facet_wrap(~president,                                 # 변수 president의 항목별로 그래프 작성 -> 대통령 각각의 막대 그래프 작성
             scales = "free",                            # x축과 y축 통일 X
             ncol = 2) +                                 # 한 행에 나타낼 그래프 개수
  scale_x_reordered()                                    # 단어 뒤의 대통령 이름 제거

Result! 그래프를 보면, 역대 대통령의 개성을 드러내는 단어를 파악할 수 있다.
Caution! 모든 문서에 사용된 단어는 IDF가 0이므로 TF-IDF도 0이 된다. 따라서 TF-IDF를 활용하면 어떤 단어가 특정 문서에 특출나게 많이 사용되더라도 모든 문서에 사용되면 발견할 수 없는 한계가 있다. “Weighted log odds”를 활용하면 이런 한계를 극복할 수 있다. Weighted log odds는 단어 등장 확률을 가중치로 이용하기 때문에 어떤 단어가 모든 문서에 사용되더라도 특정 문서에 많이 사용되면 발견할 수 있다. 또한, 오즈비와 달리 셋 이상의 문서를 비교할 때도 사용할 수 있는 장점이 있다. Weighted log odds는 Package "tidylo"를 이용하면 쉽게 구할 수 있다. 해당 패키지의 자세한 설명은 여기를 참고한다.


요약

# 1. 단어 빈도 비교
speeches <- bind_speeches %>%                # 두 연설문의 원문을 하나의 데이터셋으로 결합한 Dataset
  # 전처리
  mutate(value = str_replace_all(value,      
                                 "[^가-힣]", # [^가-힣] : 한글을 제외한 모든 문자를 의미하는 정규 표현식
                                 " "),       # 공백으로 변경
         value = str_squish(value)) %>%      # 연속된 공백 제거
  # 토큰화
  unnest_tokens(input = value,               # 토큰화를 수행할 텍스트가 포함된 변수명
                output = word,               # 출력 변수명
                token = extractNoun)         # 명사 기준으로 토큰화

speeches
# A tibble: 2,997 × 2
   president word      
   <chr>     <chr>     
 1 moon      "정권교체"
 2 moon      "하겠습니"
 3 moon      "정치"    
 4 moon      "교체"    
 5 moon      "하겠습니"
 6 moon      "시대"    
 7 moon      "교체"    
 8 moon      "하겠습니"
 9 moon      ""        
10 moon      "불비불명"
# ℹ 2,987 more rows
# 연설문별 단어 빈도 구하기
frequency <- speeches %>%
  count(president, word) %>%                  # 연설문 각각의 단어 빈도 계산
  filter(str_count(word) > 1)                 # 두 글자 이상의 단어만 추출 -> 한 글자로 된 단어 제거

frequency
# A tibble: 1,131 × 3
   president word         n
   <chr>     <chr>    <int>
 1 moon      가동         1
 2 moon      가사         1
 3 moon      가슴         2
 4 moon      가족         1
 5 moon      가족구조     1
 6 moon      가지         4
 7 moon      가치         3
 8 moon      각종         1
 9 moon      감당         1
10 moon      강력         3
# ℹ 1,121 more rows
# 가장 많이 사용된 단어 추출
top10 <- frequency %>%
  group_by(president) %>%                    # 변수 president에 대해 그룹화 -> 각각의 연설문에서 빈도가 높은 단어를 추출하기 위해 수행
  slice_max(n, n = 10,                       # 빈도가 가장 높은 10개의 단어 추출 
            with_ties = F)                   # 빈도가 동일하더라도 옵션 n에 지정한 개수만큼만 단어 추출

top10
# A tibble: 20 × 3
# Groups:   president [2]
   president word       n
   <chr>     <chr>  <int>
 1 moon      국민      21
 2 moon      일자리    21
 3 moon      나라      19
 4 moon      우리      17
 5 moon      경제      15
 6 moon      사회      14
 7 moon      성장      13
 8 moon      대통령    12
 9 moon      정치      12
10 moon      하게      12
11 park      국민      72
12 park      행복      23
13 park      여러분    20
14 park      정부      17
15 park      경제      15
16 park      신뢰      11
17 park      국가      10
18 park      우리      10
19 park      교육       9
20 park      사람       9
# 2. 오즈비
# Wide Form 형태로 변환
frequency_wide <- frequency %>%
  pivot_wider(names_from = president,               # 변수명으로 입력할 값이 들어 있는 변수
              values_from = n,                      # 변수에 채워 넣을 값이 들어 있는 변수
              values_fill = list(n = 0))            # 결측치 NA를 0으로 대체

frequency_wide
# A tibble: 955 × 3
   word      moon  park
   <chr>    <int> <int>
 1 가동         1     0
 2 가사         1     0
 3 가슴         2     0
 4 가족         1     1
 5 가족구조     1     0
 6 가지         4     0
 7 가치         3     1
 8 각종         1     0
 9 감당         1     0
10 강력         3     0
# ℹ 945 more rows
# 오즈비/로그 오즈비 계산
frequency_wide <- frequency_wide %>%
  mutate(ratio_moon = ((moon + 1)/(sum(moon + 1))),  # 문재인 전 대통령의 연설문에서 단어의 비중 계산
         ratio_park = ((park + 1)/(sum(park + 1))),  # 박근혜 전 대통령의 연설문에서 단어의 비중 계산
         odds_ratio = ratio_moon/ratio_park,         # 오즈비 계산
         log_odds_ratio = log(odds_ratio))           # 로그 오즈비 계산

frequency_wide
# A tibble: 955 × 7
   word     moon  park ratio_moon ratio_park odds_ratio log_odds_ratio
   <chr>   <int> <int>      <dbl>      <dbl>      <dbl>          <dbl>
 1 가동        1     0   0.000873   0.000552      1.58           0.459
 2 가사        1     0   0.000873   0.000552      1.58           0.459
 3 가슴        2     0   0.00131    0.000552      2.37           0.865
 4 가족        1     1   0.000873   0.00110       0.791         -0.234
 5 가족구…     1     0   0.000873   0.000552      1.58           0.459
 6 가지        4     0   0.00218    0.000552      3.96           1.38 
 7 가치        3     1   0.00175    0.00110       1.58           0.459
 8 각종        1     0   0.000873   0.000552      1.58           0.459
 9 감당        1     0   0.000873   0.000552      1.58           0.459
10 강력        3     0   0.00175    0.000552      3.17           1.15 
# ℹ 945 more rows
# 로그 오즈비를 기준으로 상대적으로 중요한 단어 추출
top10 <- frequency_wide %>%
  group_by(president = ifelse(log_odds_ratio > 0, "moon", "park")) %>%  # 로그 오즈비가 양수이면 변수 president에 "moon", 그렇지 않으면 "park" 할당한 후 그룹화
  slice_max(abs(log_odds_ratio), n = 10,                                # 로그 오즈비의 절댓값 기준으로 상위 10개의 단어 추출 -> 앞에서 그룹화를 수행했기 때문에 각 연설문에서 상위 10개의 단어 추출
            with_ties = F)                                              # 빈도가 동일하더라도 옵션 n에 지정한 개수만큼만 단어 추출

top10
# A tibble: 20 × 8
# Groups:   president [2]
   word     moon  park ratio_moon ratio_park odds_ratio log_odds_ratio
   <chr>   <int> <int>      <dbl>      <dbl>      <dbl>          <dbl>
 1 복지국…     8     0   0.00393    0.000552     7.12             1.96
 2 세상        6     0   0.00306    0.000552     5.54             1.71
 3 여성        6     0   0.00306    0.000552     5.54             1.71
 4 정의        6     0   0.00306    0.000552     5.54             1.71
 5 강자        5     0   0.00262    0.000552     4.75             1.56
 6 공평        5     0   0.00262    0.000552     4.75             1.56
 7 대통령…     5     0   0.00262    0.000552     4.75             1.56
 8 보통        5     0   0.00262    0.000552     4.75             1.56
 9 상생        5     0   0.00262    0.000552     4.75             1.56
10 지방        5     0   0.00262    0.000552     4.75             1.56
11 박근혜      0     8   0.000436   0.00496      0.0879          -2.43
12 여러분      2    20   0.00131    0.0116       0.113           -2.18
13 행복        3    23   0.00175    0.0132       0.132           -2.03
14 실천        0     5   0.000436   0.00331      0.132           -2.03
15 정보        0     5   0.000436   0.00331      0.132           -2.03
16 투명        0     5   0.000436   0.00331      0.132           -2.03
17 과제        0     4   0.000436   0.00276      0.158           -1.84
18 국정운…     0     4   0.000436   0.00276      0.158           -1.84
19 시작        0     4   0.000436   0.00276      0.158           -1.84
20 지식        0     4   0.000436   0.00276      0.158           -1.84
# ℹ 1 more variable: president <chr>
# 3. TF-IDF 계산
frequency <- frequency %>%           # 단어 빈도가 저장되어 있는 객체  
  bind_tf_idf(term = word,           # 단어가 입력되어 있는 변수
              document = president,  # 텍스트 구분 변수
              n = n) %>%             # 단어 빈도가 입력되어 있는 변수
  arrange(desc(tf_idf))              # TF-IDF 내림차순 정렬

frequency
# A tibble: 1,131 × 6
   president word         n      tf   idf  tf_idf
   <chr>     <chr>    <int>   <dbl> <dbl>   <dbl>
 1 park      박근혜       8 0.00932 0.693 0.00646
 2 moon      복지국가     8 0.00599 0.693 0.00415
 3 park      실천         5 0.00583 0.693 0.00404
 4 park      정보         5 0.00583 0.693 0.00404
 5 park      투명         5 0.00583 0.693 0.00404
 6 park      과제         4 0.00466 0.693 0.00323
 7 park      국정운영     4 0.00466 0.693 0.00323
 8 park      시작         4 0.00466 0.693 0.00323
 9 park      지식         4 0.00466 0.693 0.00323
10 moon      세상         6 0.00449 0.693 0.00311
# ℹ 1,121 more rows
# TF-IDF 기준으로 상대적으로 중요한 단어 추출
top10 <- frequency %>%
  group_by(president) %>%            # 변수 president에 대해 그룹화
  slice_max(n, n = 10,               # TF-IDF가 가장 높은 10개의 단어 추출 
            with_ties = F)           # 빈도가 동일하더라도 옵션 n에 지정한 개수만큼만 단어 추출

top10
# A tibble: 20 × 6
# Groups:   president [2]
   president word       n      tf   idf tf_idf
   <chr>     <chr>  <int>   <dbl> <dbl>  <dbl>
 1 moon      국민      21 0.0157      0      0
 2 moon      일자리    21 0.0157      0      0
 3 moon      나라      19 0.0142      0      0
 4 moon      우리      17 0.0127      0      0
 5 moon      경제      15 0.0112      0      0
 6 moon      사회      14 0.0105      0      0
 7 moon      성장      13 0.00973     0      0
 8 moon      대통령    12 0.00898     0      0
 9 moon      정치      12 0.00898     0      0
10 moon      하게      12 0.00898     0      0
11 park      국민      72 0.0839      0      0
12 park      행복      23 0.0268      0      0
13 park      여러분    20 0.0233      0      0
14 park      정부      17 0.0198      0      0
15 park      경제      15 0.0175      0      0
16 park      신뢰      11 0.0128      0      0
17 park      국가      10 0.0117      0      0
18 park      우리      10 0.0117      0      0
19 park      교육       9 0.0105      0      0
20 park      사람       9 0.0105      0      0

Reuse

Text and figures are licensed under Creative Commons Attribution CC BY 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".