Description for Comparison Analysis
pacman::p_load("readr",
"dplyr", "tidyr",
"stringr",
"tidytext",
"KoNLP",
"ggplot2")
# 데이터 불러오기
# 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
"dplyr"
의 함수 count
를 이용하여 두 전 대통령의 연설문에서 사용한 단어의 빈도를 계산한다.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
"dplyr"
의 함수 slice_max
를 이용하여 연설문에서 가장 많이 사용한 단어를 추출한다.
slice_max
: 값이 큰 상위 n개의 행을 추출해 내림차순으로 정렬하는 함수slice_min
: 값이 작은 하위 n개의 행을 추출하여 정렬하는 함수group_by
를 이용하여 변수 president
에 대해 그룹화를 수행한다.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번씩 사용한 단어 “교육”, “사람”, “사회”, “일자리” 중 “교육”과 “사람”이 포함된 이유는 빈도가 동일한 단어의 경우 원본 데이터의 정렬 순서에 따라 출력하기 때문이다.
# 기본 막대 그래프
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)
를 사용한다.
x
: 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() # 단어 뒤의 대통령 이름 제거
보편적으로 자주 사용
하고 별다른 특징이 없기 때문에 텍스트의 차이를 잘 드러내지 못한다.
특정 텍스트에서만 많이 사용
하고 다른 텍스트에서는 적게 사용한 단어
, 즉, '상대적으로 많이 사용한 단어'
를 살펴봐야 한다."frequency"
는 변수 president
가 “moon”인 행과 “park”인 행이 세로로 길게 나열된 형태이다.
# 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 데이터셋을 생성하였다.
\[ \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*} \]
\[ \begin{align*} \text{해당 단어의 비중} = \frac{\text{해당 단어의 빈도} }{ \text{모든 단어의 빈도 합} } \end{align*} \]
빈도가 0보다 큰 값이 되도록 모든 값에 1
을 더한다.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로 계산되었다.
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
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!
단어 빈도 비교에서 단순히 사용 빈도가 높은 단어는 “국민”, “우리”, “사회” 같은 보편적인 단어라 연설문의 차이가 잘 드러나지 않았다. 반면, 오즈비 기준으로 추출한 단어는 두 연설문 중 한쪽에서 비중이 더 큰 단어이므로 이를 통해 연설문의 차이를 분명하게 알 수 있다.
# 그래프 작성을 위한 변수 추가
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!
오즈비를 이용해 만든 막대 그래프는 각 텍스트에서 상대적으로 중요한 단어가 무엇인지 표현하기 위해 만든다. 막대 길이를 보고 두 텍스트의 단어 빈도를 비교하면 안 되고, 각 텍스트에서 상대적으로 중요한 단어가 무엇인지만 살펴봐야 한다.
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!
함수 filter
와 str_detect
를 이용하여 각 연설문에서 주요 단어를 사용한 문장을 추출할 수 있다. 추출한 문장을 보면 단어가 어떤 의미로 사용되었는지 알 수 있다.
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!
두 연설문 모두 “사회”, “사람”, “경제” 등을 강조했음을 알 수 있다.
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
# 그래프 작성을 위한 변수 추가
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!
오즈비/로그 오즈비는 두 조건의 확률을 이용해 계산하므로 세 개 이상의 텍스트를 비교할 때 적절하지 않다는 단점이 있다. 텍스트를 둘씩 짝지어 따로 비교할 수도 있지만, 비교할 텍스트가 많으면 계산 절차가 번거롭고 결과를 해석하기 어렵기 때문에 효율적이지 않다.
DF가 클수록 여러 문서에서 흔하게 사용한 일반적인 단어
라고 할 수 있다.IDF는 DF의 역수
이므로 DF가 클수록 작아지고, 반대로 DF가 작을수록 커진다.IDF가 클수록 드물게 사용한 특이한 단어, 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
# 단어 빈도 구하기
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
"tidytext"
의 함수 bind_tf_idf(tbl, term, document, n)
를 이용하여 TF-IDF를 계산할 수 있다.
tbl
: TF-IDF를 수행할 객체term
: 단어가 입력되어 있는 변수document
: 텍스트 구분 변수n
: 단어 빈도가 입력되어 있는 변수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
# 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
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 ...".