비교분석 #3 TF-IDF 로그 오즈비
로그 오즈비
로그 오즈비(log odds ratio)
오즈비에 로그를 취한 값
단어의 오즈비가 1보다 크면 + ,1보다 작으면 -가 됨
단어가 두 텍스트 중 어디에서 비중이 큰지에 따라 서로 다른 부호
"moon"에서 비중이 커서 odds_ratio가 1보다 큰 단어 +
"park"에서 비중이 커서 odds_ratio가 1보다 작은 단어 -
텍스트 차이 분명하게 드러나도록 시각화하는데 활용
단어가 어느 텍스트에서 중요한지에 따라 막대를 반대 방향으로 표현
로그 오즈비 구하기
frequency_wide <- frequency_wide %>% mutate(log_odds_ratio = log(odds_ratio)) frequency_wide # # A tibble: 950 x 7 # word moon park ratio_moon ratio_park odds_ratio log_odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> <dbl> # 1 가동 1 0 0.000879 0.000555 1.59 0.461 # 2 가사 1 0 0.000879 0.000555 1.59 0.461 # 3 가슴 2 1 0.00132 0.00111 1.19 0.173 # 4 가족 1 1 0.000879 0.00111 0.793 -0.233 # 5 가족구조 1 0 0.000879 0.000555 1.59 0.461 # 6 가지 4 0 0.00220 0.000555 3.96 1.38 # 7 가치 3 1 0.00176 0.00111 1.59 0.461 # 8 각종 1 0 0.000879 0.000555 1.59 0.461 # 9 감당 1 0 0.000879 0.000555 1.59 0.461 # 10 강력 3 0 0.00176 0.000555 3.17 1.15 # # ... with 940 more rows |
부호와 크기를 보면 단어가 어느 연설문에서 더 중요한지 알 수 있음
0보다 큰 양수일수록 "moon "에서 비중이 큼
0보다 작은 음수일수록 "park"에서 비중이 큼
0에 가까우면 두 연설문에서 비중 비슷함
#0보다 큰 양수일수록 "moon "에서 비중이 큼
frequency_wide %>% arrange(-log_odds_ratio) # # A tibble: 950 x 7 # word moon park ratio_moon ratio_park odds_ratio log_odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> <dbl> # 1 복지국가 8 0 0.00396 0.000555 7.13 1.96 # 2 세상 6 0 0.00308 0.000555 5.55 1.71 # 3 여성 6 0 0.00308 0.000555 5.55 1.71 # 4 정의 6 0 0.00308 0.000555 5.55 1.71 # 5 강자 5 0 0.00264 0.000555 4.76 1.56 # 6 공평 5 0 0.00264 0.000555 4.76 1.56 # 7 대통령의 5 0 0.00264 0.000555 4.76 1.56 # 8 보통 5 0 0.00264 0.000555 4.76 1.56 # 9 상생 5 0 0.00264 0.000555 4.76 1.56 # 10 지방 5 0 0.00264 0.000555 4.76 1.56 # # ... with 940 more rows |
#0보다 작은 음수일수록 "park"에서 비중이 큼
frequency_wide %>% arrange(log_odds_ratio) # # A tibble: 950 x 7 # word moon park ratio_moon ratio_park odds_ratio log_odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> <dbl> # 1 박근혜 0 8 0.000440 0.00499 0.0881 -2.43 # 2 여러분 2 20 0.00132 0.0116 0.113 -2.18 # 3 행복 3 23 0.00176 0.0133 0.132 -2.02 # 4 실천 0 5 0.000440 0.00333 0.132 -2.02 # 5 정보 0 5 0.000440 0.00333 0.132 -2.02 # 6 투명 0 5 0.000440 0.00333 0.132 -2.02 # 7 과제 0 4 0.000440 0.00277 0.159 -1.84 # 8 국정운영 0 4 0.000440 0.00277 0.159 -1.84 # 9 시작 0 4 0.000440 0.00277 0.159 -1.84 # 10 지식 0 4 0.000440 0.00277 0.159 -1.84 # # ... with 940 more rows |
# 0에 가까우면 두 연설문에서 비중 비슷함
frequency_wide %>% arrange(abs(log_odds_ratio)) # # A tibble: 950 x 7 # word moon park ratio_moon ratio_park odds_ratio log_odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> <dbl> # 1 때문 4 3 0.00220 0.00222 0.991 -0.00938 # 2 강화 3 2 0.00176 0.00166 1.06 0.0552 # 3 부담 3 2 0.00176 0.00166 1.06 0.0552 # 4 세계 3 2 0.00176 0.00166 1.06 0.0552 # 5 책임 3 2 0.00176 0.00166 1.06 0.0552 # 6 협력 3 2 0.00176 0.00166 1.06 0.0552 # 7 가슴 2 1 0.00132 0.00111 1.19 0.173 # 8 거대 2 1 0.00132 0.00111 1.19 0.173 # 9 교체 2 1 0.00132 0.00111 1.19 0.173 # 10 근본적 2 1 0.00132 0.00111 1.19 0.173 # # ... with 940 more rows |
로그오즈비를 이용해 중요한 단어 비교하기
# 1 두 연설문 각각 log_odds_ratio Top 10 추출
top10 <- frequency_wide %>% group_by(president = ifelse(log_odds_ratio > 0, "moon", "park")) %>% slice_max(abs(log_odds_ratio), n = 10, with_ties = F) top10 # # A tibble: 20 x 8 # # Groups: president [2] # word moon park ratio_moon ratio_park odds_ratio log_odds_ratio president # <chr> <int> <int> <dbl> <dbl> <dbl> <dbl> <chr> # 1 복지국가 8 0 0.00396 0.000555 7.13 1.96 moon # 2 세상 6 0 0.00308 0.000555 5.55 1.71 moon # 3 여성 6 0 0.00308 0.000555 5.55 1.71 moon # 4 정의 6 0 0.00308 0.000555 5.55 1.71 moon # 5 강자 5 0 0.00264 0.000555 4.76 1.56 moon # 6 공평 5 0 0.00264 0.000555 4.76 1.56 moon # 7 대통령의 5 0 0.00264 0.000555 4.76 1.56 moon # 8 보통 5 0 0.00264 0.000555 4.76 1.56 moon # 9 상생 5 0 0.00264 0.000555 4.76 1.56 moon # 10 지방 5 0 0.00264 0.000555 4.76 1.56 moon # 11 박근혜 0 8 0.000440 0.00499 0.0881 -2.43 park # 12 여러분 2 20 0.00132 0.0116 0.113 -2.18 park # 13 행복 3 23 0.00176 0.0133 0.132 -2.02 park # 14 실천 0 5 0.000440 0.00333 0.132 -2.02 park # 15 정보 0 5 0.000440 0.00333 0.132 -2.02 park # 16 투명 0 5 0.000440 0.00333 0.132 -2.02 park # 17 과제 0 4 0.000440 0.00277 0.159 -1.84 park # 18 국정운영 0 4 0.000440 0.00277 0.159 -1.84 park # 19 시작 0 4 0.000440 0.00277 0.159 -1.84 park # 20 지식 0 4 0.000440 0.00277 0.159 -1.84 park |
slice_max(.data, order_by, n, with_ties = TRUE)
with_ties = F
기본값 'TRUE', 요청한 것보다 더 많은 행을 반환할 수 있다. 'FALSE'를 사용하여 관계를 무시. 첫 번째 'n'행을 반환
#2 주요 변수 추출
top10 %>% arrange(-log_odds_ratio) %>% select(word, log_odds_ratio, president) # A tibble: 20 x 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.02 park # 16 실천 -2.02 park # 17 정보 -2.02 park # 18 투명 -2.02 park # 19 여러분 -2.18 park # 20 박근혜 -2.43 park |
#3 막대 그래프 만들기
단어가 어느 연설문에서 중요한지에 따라 서로 다른 축 방향으로 표현됨
ggplot(top10 , aes(x=reorder(word, log_odds_ratio), y = log_odds_ratio, fill = president)) + geom_col() + coord_flip()+labs(x=NULL) + theme(text=element_text(family="nanumgothic")) |
x 축에는 redorder(word, log_odds_ratio) : word가 로그오즈비 작은것부터 앞에 오도록 순서를 조정하라는 뜻
y에는 log_odds_ratio 입력
coord_flip() : 축 변경
labs(x=NULL) : x축 제목 지우기 => lab(x='x축 제목',y='y축 제목')
여러 텍스트의 단어 비교하기
오즈비의 한계
두 조건의 확률을 이용해 계산
여러 텍스트 비교하기 불편
두 개 이상의 텍스트 비교할 때는 TF-IDF 활용
중요한 단어
흔하지 않으면서도 특정 텍스트에서는 자주 사용된 단어
텍스트가 다른 텍스트와 구별되는 특징, 개성을 드러내는 단어
TF-IDF(Term Frequency - Inverse Document Frequency)
어떤 단어가 흔하지 않으면서도 특정 텍스트에서는 자주 사용된 정도를 나타낸 지표
텍스트의 개성을 드러내는 주요 단어를 찾는데 활용
TF(Term Frequency)
단어가 특정 텍스트에 사용된 횟수 - 단어 빈도
DF(Document Frequency)
단어가 사용된 텍스트 수 - '문서 빈도'
클수록 여러 문서에 흔하게 사용된 일반적인 단어
IDF(Inverse Document Frequency)
'역문서 빈도'
(1) 전체 문서 수(N)에서 DF가 차지하는 비중을 구함
(2) 그 값의 역수를 취함
(3) 로그를 취함
DF가 클수록 작아지고, 반대로 DF가 작을수록 커짐
클수록 드물게 사용되는 특이한 단어, 작을수록 흔하게 사용되는 일반적인 단어
DF와 IDF : 소수점 둘째 자리에서 반올림하여 표기
TF-IDF
TF(단어 빈도)와 IDF(역 문서 빈도)를 곱한 값
TF: 단어가 분석 대상이 되는 텍스트 내에서 많이 사용될수록 커짐
IDF: 단어가 사용된 텍스트가 드물수록 커짐
흔하지 않은 단어인데 특정 텍스트에서 자주 사용될수록 큰 값
TF-IDF가 큰 단어를 보면 다른 텍스트와 구별되는 특징을 알 수 있음
speeches_presidents.csv : 역대 대통령의 대선 출마 선언문
TF-IDF 구하기
#1 단어 빈도 구하기
readr::read_csv() 데이터를 다루기 편한 tibble 구조로 만들어 줌, read.csv()보다 빠름
R version 4.0 에서 실행 x ,
library(dplyr)
library(tidytext)
library(KoNLP)
library(stringr)
(1) 데이터 불러오기
# 오류 # raw_speeches <- read.csv("C:/speeches_presidents.csv",header = T) |
raw_speeches <- read_csv("C:/speeches_presidents.csv") raw_speeches ## # A tibble: 4 x 2 ## president value ## <chr> <chr> ## 1 문재인 "정권교체 하겠습니다! 정치교체 하겠습니다! 시대교체 하겠습… ## 2 박근혜 "존경하는 국민 여러분! 저는 오늘, 국민 한 분 한 분의 꿈이 이… ## 3 이명박 "존경하는 국민 여러분, 사랑하는 한나라당 당원 동지 여러분! 저는… ## 4 노무현 "어느때인가 부터 제가 대통령이 되겠다고 말을 하기 시작했습니다. … |
(2) 기본적인 전처리
speeches <- raw_speeches %>% mutate(value=str_replace_all(value,"[^가-힣]"," "), vaalue = str_squish(value)) |
(3) 토큰화
speeches <- speeches %>% unnest_tokens(input=value, output=word, token=extractNoun) |
(4) 단어 빈도 구하기
frequecy <- speeches %>% count(president, word) %>% filter(str_count(word) > 1) frequecy # # A tibble: 1,509 x 3 # president word n # <chr> <chr> <int> # 1 노무현 가론 1 # 2 노무현 가슴 2 # 3 노무현 가훈 2 # 4 노무현 갈등 1 # 5 노무현 감옥 1 # 6 노무현 강자 1 # 7 노무현 개편 4 # 8 노무현 개혁 4 # 9 노무현 건국 1 # 10 노무현 경선 1 # # ... with 1,499 more rows # # i Use `print(n = ...)` to see more rows |
#2 TF-IDF 구하기
tidytext::bind_tf_idf()
term : 단어
document : 텍스트 구분 기준
n : 단어 빈도
frequecy <- frequecy %>% bind_tf_idf(term=word, document = president, n=n) %>% arrange(-tf_idf) frequecy |
tf :
대상 텍스트의 전체 단어 수에서 해당 단어의 수가 차지하는 '비중'.
텍스트에 사용된 전체 단어 수가 많을수록 작아짐
#3 TF-IDF가 높은 단어 살펴보기
텍스트의 특징을 드러내는 중요한 단어
# 문 frequecy %>% filter(president == "문재인") # # A tibble: 686 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 문재인 복지국가 8 0.00613 1.39 0.00850 # 2 문재인 여성 6 0.00460 1.39 0.00638 # 3 문재인 공평 5 0.00383 1.39 0.00532 # 4 문재인 담쟁이 5 0.00383 1.39 0.00532 # 5 문재인 대통령의 5 0.00383 1.39 0.00532 # 6 문재인 보통 5 0.00383 1.39 0.00532 # 7 문재인 상생 5 0.00383 1.39 0.00532 # 8 문재인 우리나라 10 0.00767 0.693 0.00532 # 9 문재인 지방 5 0.00383 1.39 0.00532 # 10 문재인 확대 10 0.00767 0.693 0.00532 # # ... with 676 more rows # # i Use `print(n = ...)` to see more rows # 박 frequecy %>% filter(president == "박근혜") # # A tibble: 406 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 박근혜 박근혜 8 0.00967 1.39 0.0134 # 2 박근혜 정보 5 0.00605 1.39 0.00838 # 3 박근혜 투명 5 0.00605 1.39 0.00838 # 4 박근혜 행복 23 0.0278 0.288 0.00800 # 5 박근혜 교육 9 0.0109 0.693 0.00754 # 6 박근혜 국정운영 4 0.00484 1.39 0.00671 # 7 박근혜 정부 17 0.0206 0.288 0.00591 # 8 박근혜 개개인 3 0.00363 1.39 0.00503 # 9 박근혜 개인 3 0.00363 1.39 0.00503 # 10 박근혜 공개 3 0.00363 1.39 0.00503 # # ... with 396 more rows # # i Use `print(n = ...)` to see more rows # 이 frequecy %>% filter(president == "이명박") # # A tibble: 200 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 이명박 리더십 6 0.0159 1.39 0.0221 # 2 이명박 당원 4 0.0106 1.39 0.0147 # 3 이명박 동지 4 0.0106 1.39 0.0147 # 4 이명박 일류국가 4 0.0106 1.39 0.0147 # 5 이명박 한나라 7 0.0186 0.693 0.0129 # 6 이명박 나라 15 0.0398 0.288 0.0114 # 7 이명박 도약 3 0.00796 1.39 0.0110 # 8 이명박 일하 3 0.00796 1.39 0.0110 # 9 이명박 사랑 5 0.0133 0.693 0.00919 # 10 이명박 인생 5 0.0133 0.693 0.00919 # # ... with 190 more rows # # i Use `print(n = ...)` to see more rows # 노 frequecy %>% filter(president == "노무현") # # A tibble: 217 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 노무현 공식 6 0.0166 1.39 0.0230 # 2 노무현 비젼 6 0.0166 1.39 0.0230 # 3 노무현 정계 6 0.0166 1.39 0.0230 # 4 노무현 권력 9 0.0249 0.693 0.0172 # 5 노무현 개편 4 0.0110 1.39 0.0153 # 6 노무현 국회의원 3 0.00829 1.39 0.0115 # 7 노무현 남북대화 3 0.00829 1.39 0.0115 # 8 노무현 총리 3 0.00829 1.39 0.0115 # 9 노무현 가훈 2 0.00552 1.39 0.00766 # 10 노무현 개혁 4 0.0110 0.693 0.00766 # # ... with 207 more rows # # i Use `print(n = ...)` to see more rows |
각 대통령이 다른 대통령들과 달리 무엇을 강조했는지 알 수 있음
#4 TF-IDF가 낮은 단어 살펴보기
# 문 frequecy %>% filter(president == "문재인") %>% arrange(tf_idf) # # A tibble: 686 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 문재인 경쟁 6 0.00460 0 0 # 2 문재인 경제 15 0.0115 0 0 # 3 문재인 고통 4 0.00307 0 0 # 4 문재인 과거 1 0.000767 0 0 # 5 문재인 국민 21 0.0161 0 0 # 6 문재인 기회 5 0.00383 0 0 # 7 문재인 대통령 12 0.00920 0 0 # 8 문재인 동안 2 0.00153 0 0 # 9 문재인 들이 9 0.00690 0 0 # 10 문재인 마음 2 0.00153 0 0 # # ... with 676 more rows # # i Use `print(n = ...)` to see more rows # 박 frequecy %>% filter(president == "박근혜") %>% arrange(tf_idf) # # A tibble: 406 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 박근혜 경쟁 1 0.00121 0 0 # 2 박근혜 경제 15 0.0181 0 0 # 3 박근혜 고통 4 0.00484 0 0 # 4 박근혜 과거 2 0.00242 0 0 # 5 박근혜 국민 72 0.0871 0 0 # 6 박근혜 기회 1 0.00121 0 0 # 7 박근혜 대통령 3 0.00363 0 0 # 8 박근혜 동안 3 0.00363 0 0 # 9 박근혜 들이 3 0.00363 0 0 # 10 박근혜 마음 3 0.00363 0 0 # # ... with 396 more rows # # i Use `print(n = ...)` to see more rows # 이 frequecy %>% filter(president == "이명박") %>% arrange(tf_idf) # # A tibble: 200 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 이명박 경쟁 3 0.00796 0 0 # 2 이명박 경제 5 0.0133 0 0 # 3 이명박 고통 1 0.00265 0 0 # 4 이명박 과거 1 0.00265 0 0 # 5 이명박 국민 13 0.0345 0 0 # 6 이명박 기회 3 0.00796 0 0 # 7 이명박 대통령 4 0.0106 0 0 # 8 이명박 동안 1 0.00265 0 0 # 9 이명박 들이 1 0.00265 0 0 # 10 이명박 마음 1 0.00265 0 0 # # ... with 190 more rows # # i Use `print(n = ...)` to see more rows # 노 frequecy %>% filter(president == "노무현") %>% arrange(tf_idf) # # A tibble: 217 x 6 # president word n tf idf tf_idf # <chr> <chr> <int> <dbl> <dbl> <dbl> # 1 노무현 경쟁 1 0.00276 0 0 # 2 노무현 경제 1 0.00276 0 0 # 3 노무현 고통 1 0.00276 0 0 # 4 노무현 과거 1 0.00276 0 0 # 5 노무현 국민 7 0.0193 0 0 # 6 노무현 기회 1 0.00276 0 0 # 7 노무현 대통령 6 0.0166 0 0 # 8 노무현 동안 2 0.00552 0 0 # 9 노무현 들이 4 0.0110 0 0 # 10 노무현 마음 1 0.00276 0 0 # # ... with 207 more rows # # i Use `print(n = ...)` to see more rows |
역대 대통령들이 공통적으로 사용한 흔한 단어, 범용 단어
#5 막대 그래프 만들기
(1) 주요 단어 추출
top10 <- frequecy %>% group_by(president) %>% slice_max(tf_idf, n = 10, with_ties = F) |
(2) 그래프 순서 정하기
top10$president <- factor(top10$president, levels = c("문재인", "박근혜", "이명박", "노무현")) |
(3) 막대 그래프 만들기
ggplot(top10, aes(x = reorder_within(word, tf_idf, president), y = tf_idf, fill = president)) + geom_col(show.legend = F) + coord_flip() + facet_wrap(~ president, scales = "free", ncol = 2) + scale_x_reordered() + labs(x = NULL) + theme(text = element_text(family = "nanumgothic")) |
reorder_within(x, by, within, fun = mean, sep = "___", ...)
x : 벡터를 다시 정렬
by : 재정렬에 사용할 동일한 길이의 벡터
within : 변수
fun : 결과 결정하기 위한 각 하위 집합 내에서 수행하는 함수, 기본적으로 mean
sep : 구별 할 구분 기호
show.legend = F
범례(legend)가 기본적으로 표시되지만 때때로 범례 표시를 원치 않는 상황이 있다.
ggplot(...)+ geom_그래프유형(show.legend=FALSE)
범례가 그래프에 나타나지 않는다.
