텍스트마이닝

비교분석 #3 TF-IDF 로그 오즈비

yul_S2 2022. 12. 19. 10:24
반응형

 로그 오즈비 

 

로그 오즈비(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
0.05MB

 

 

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)

범례가 그래프에 나타나지 않는다.

 

 

 

 

반응형