비교분석 #2 오즈비
오즈비
상대적으로 중요한 단어 비교하기
(1) Long from Wide form 으로 변환하기
여러 텍스트 비교하기 편하게 데이터 구조 바꾸기
# 1 Long from
df_long <- frequency %>% group_by(president) %>% slice_max(n, n = 10) %>% filter(word %in% c("국민", "우리", "정치", "행복")) df_long ## # A tibble: 6 x 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 |
frequency : president가 "moon"인 행과 "park"인 행이 세로로 길게 나열
< Long form 데이터 >
- 같은 단어가 범주별로 다른 행을 구성
- 범주별 빈도 비교 어려움
- 빈도를 활용해 연산하기 불편
< wide form 데이터 > 가로로 넓은 형태의 데이터
- 범주별로 단어 빈도 비교하기 편함
- 변수간 연산하기 편함
# 2 Wide form
library(tidyr) df_wide <- df_long %>% pivot_wider(names_from = president, values_from = n) df_wide ## # A tibble: 4 x 3 ## word moon park ## ## 1 국민 21 72 ## 2 우리 17 10 ## 3 정치 12 NA ## 4 행복 NA 23 |
tidyr::pivot_wider() : long form을 wide form으로 변환
names_from : 변수명으로 만들 값이 들어 있는 변수
values_from : 변수에 채워넣을 값이 들어 있는 변수
(2) NA를 0으로 바꾸기
어떤 단어가 둘 중 한 범주에만 있으면 NA
오즈비 계산하기 위해 0으로 변환해야 함
df_wide <- df_long %>% pivot_wider(names_from = president, values_from = n, values_fill = list(n = 0)) df_wide ## # A tibble: 4 x 3 ## word moon park ## <chr> <int> <int> ## 1 국민 21 72 ## 2 우리 17 10 ## 3 정치 12 0 ## 4 행복 0 23 df_long ## # A tibble: 6 x 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 |
(3) 연설문 단어 빈도를 Wide form 으로 변환하기
frequency_wide <- frequency %>% pivot_wider(names_from = president, values_from = n, values_fill = list(n = 0)) frequency_wide |
(4) 오즈비 구하기
어떤 사건이 A 조건에서 발생할 확률이 B 조건에서 발생할 확률에 비해 얼마나 더 큰지를 나타냄
단어가 두 텍스트 중 어디에 등장할 확률이 높은지, 상대적인 중요도를 알 수 있음
# n : 각단어의 빈도
# total : 전체 단어 빈도
# 1. 단어의 비중을 나타낸 변수 추가하기
- 각 단어가 두 연설문에서 차지하는 비중을 나타낸 변수
- 연설문별로 '각 단어의 빈도'를 '모든 단어 빈도의 합'으로 나눔
frequency_wide <- frequency_wide %>% mutate(ratio_moon = ((moon)/(sum(moon))), # moon 에서 단어의 비중 ratio_park = ((park)/(sum(park)))) # park 에서 단어의 비중 frequency_wide # # A tibble: 950 x 5 # word moon park ratio_moon ratio_park # # 1 가동 1 0 0.000755 0 # 2 가사 1 0 0.000755 0 # 3 가슴 2 1 0.00151 0.00117 # 4 가족 1 1 0.000755 0.00117 # 5 가족구조 1 0 0.000755 0 # 6 가지 4 0 0.00302 0 # 7 가치 3 1 0.00226 0.00117 # 8 각종 1 0 0.000755 0 # 9 감당 1 0 0.000755 0 # 10 강력 3 0 0.00226 0 # # ... with 940 more rows # # i Use `print(n = ...)` to see more rows |
- 어떤 단어가 한 연설문에 전혀 사용되지 않으면 빈도 0, 오즈비 0, 단어 비중 비교 불가
- 빈도가 0보다 큰 값이 되도록 모든 값에 +1
frequency_wide <- frequency_wide %>% mutate(ratio_moon = ((moon + 1)/(sum(moon + 1))), # moon에서 단어의 비중 ratio_park = ((park + 1)/(sum(park + 1)))) # park에서 단어의 비중 frequency_wide # # A tibble: 950 x 5 # word moon park ratio_moon ratio_park # # 1 가동 1 0 0.000879 0.000555 # 2 가사 1 0 0.000879 0.000555 # 3 가슴 2 1 0.00132 0.00111 # 4 가족 1 1 0.000879 0.00111 # 5 가족구조 1 0 0.000879 0.000555 # 6 가지 4 0 0.00220 0.000555 # 7 가치 3 1 0.00176 0.00111 # 8 각종 1 0 0.000879 0.000555 # 9 감당 1 0 0.000879 0.000555 # 10 강력 3 0 0.00176 0.000555 # # ... with 940 more rows # # i Use `print(n = ...)` to see more rows |
# 2 오즈비 변수 추가하기
- 오즈비를 보면 단어가 어떤 텍스트에서 상대적으로 더 많이 사용됐는지 알 수 있음
- "moon"에서 상대적인 비중 클수록 1보다 큰 값
- "park"에서 상대적인 비중 클수록 1보다 작은 값
- 두 연설문에서 단어 비중 같으면 1
frequency_wide <- frequency_wide %>% mutate(odds_ratio = ratio_moon/ratio_park) frequency_wide # # A tibble: 950 x 6 # word moon park ratio_moon ratio_park odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> # 1 가동 1 0 0.000879 0.000555 1.59 # 2 가사 1 0 0.000879 0.000555 1.59 # 3 가슴 2 1 0.00132 0.00111 1.19 # 4 가족 1 1 0.000879 0.00111 0.793 # 5 가족구조 1 0 0.000879 0.000555 1.59 # 6 가지 4 0 0.00220 0.000555 3.96 # 7 가치 3 1 0.00176 0.00111 1.59 # 8 각종 1 0 0.000879 0.000555 1.59 # 9 감당 1 0 0.000879 0.000555 1.59 # 10 강력 3 0 0.00176 0.000555 3.17 # # ... with 940 more rows |
# "moon"에서 상대적인 비중 클수록 1보다 큰 값
frequency_wide %>% arrange(-odds_ratio) # # A tibble: 950 x 6 # word moon park ratio_moon ratio_park odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> # 1 복지국가 8 0 0.00396 0.000555 7.13 # 2 세상 6 0 0.00308 0.000555 5.55 # 3 여성 6 0 0.00308 0.000555 5.55 # 4 정의 6 0 0.00308 0.000555 5.55 # 5 강자 5 0 0.00264 0.000555 4.76 # 6 공평 5 0 0.00264 0.000555 4.76 # 7 대통령의 5 0 0.00264 0.000555 4.76 # 8 보통 5 0 0.00264 0.000555 4.76 # 9 상생 5 0 0.00264 0.000555 4.76 # 10 지방 5 0 0.00264 0.000555 4.76 # # ... with 940 more rows |
# "park"에서 상대적인 비중 클수록 1보다 작은 값
frequency_wide %>% arrange(odds_ratio) # # A tibble: 950 x 6 # word moon park ratio_moon ratio_park odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> # 1 박근혜 0 8 0.000440 0.00499 0.0881 # 2 여러분 2 20 0.00132 0.0116 0.113 # 3 행복 3 23 0.00176 0.0133 0.132 # 4 실천 0 5 0.000440 0.00333 0.132 # 5 정보 0 5 0.000440 0.00333 0.132 # 6 투명 0 5 0.000440 0.00333 0.132 # 7 과제 0 4 0.000440 0.00277 0.159 # 8 국정운영 0 4 0.000440 0.00277 0.159 # 9 시작 0 4 0.000440 0.00277 0.159 # 10 지식 0 4 0.000440 0.00277 0.159 # # ... with 940 more rows |
# 두 연설문에서 단어 비중 같으면 1
frequency_wide %>% arrange(abs(1 - odds_ratio)) # # A tibble: 950 x 6 # word moon park ratio_moon ratio_park odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> # 1 때문 4 3 0.00220 0.00222 0.991 # 2 강화 3 2 0.00176 0.00166 1.06 # 3 부담 3 2 0.00176 0.00166 1.06 # 4 세계 3 2 0.00176 0.00166 1.06 # 5 책임 3 2 0.00176 0.00166 1.06 # 6 협력 3 2 0.00176 0.00166 1.06 # 7 가슴 2 1 0.00132 0.00111 1.19 # 8 거대 2 1 0.00132 0.00111 1.19 # 9 교체 2 1 0.00132 0.00111 1.19 # 10 근본적 2 1 0.00132 0.00111 1.19 # # ... with 940 more rows |
(5) 상대적으로 중요한 단어 추출하기
오즈비가 가장 높거나 가장 낮은 단어 추출하기
top10 <- frequency_wide %>% filter(rank(odds_ratio) <= 10 | rank(-odds_ratio) <= 10) top10 %>% arrange(-odds_ratio) # # A tibble: 20 x 6 # word moon park ratio_moon ratio_park odds_ratio # # 1 복지국가 8 0 0.00396 0.000555 7.13 # 2 세상 6 0 0.00308 0.000555 5.55 # 3 여성 6 0 0.00308 0.000555 5.55 # 4 정의 6 0 0.00308 0.000555 5.55 # 5 강자 5 0 0.00264 0.000555 4.76 # 6 공평 5 0 0.00264 0.000555 4.76 # 7 대통령의 5 0 0.00264 0.000555 4.76 # 8 보통 5 0 0.00264 0.000555 4.76 # 9 상생 5 0 0.00264 0.000555 4.76 # 10 지방 5 0 0.00264 0.000555 4.76 # 11 과제 0 4 0.000440 0.00277 0.159 # 12 국정운영 0 4 0.000440 0.00277 0.159 # 13 시작 0 4 0.000440 0.00277 0.159 # 14 지식 0 4 0.000440 0.00277 0.159 # 15 행복 3 23 0.00176 0.0133 0.132 # 16 실천 0 5 0.000440 0.00333 0.132 # 17 정보 0 5 0.000440 0.00333 0.132 # 18 투명 0 5 0.000440 0.00333 0.132 # 19 여러분 2 20 0.00132 0.0116 0.113 # 20 박근혜 0 8 0.000440 0.00499 0.0881 |
상위 10개: "moon"에서 더 자주 사용되어 odds_ratio가 높은 단어
하위 10개: "park"에서 더 자주 사용되어 odds_ratio가 낮은 단어
(6) 막대 그래프 그리기
#1 비중이 큰 연설문을 나타낸 변수 추가하기
top10 <- top10 %>% mutate(president = ifelse(odds_ratio > 1, "moon", "park"), n = ifelse(odds_ratio > 1, moon, park)) top10 |
#2 막대 그래프 만들기
ggplot(top10, aes(x = reorder_within(word, n, president), y = n, fill = president)) + geom_col() + coord_flip() + facet_wrap(~ president, scales = "free_y") + scale_x_reordered() |
#3 그래프별로 축 설정하기
범주별로 단어 비중 알 수 있도록 x축 크기 각각 정하기
ggplot(top10, aes(x = reorder_within(word, n, president), y = n, fill = president)) + geom_col() + coord_flip() + facet_wrap(~ president, scales = "free") + scale_x_reordered() + labs(x = NULL) + # x축 삭제 theme(text = element_text(family = "nanumgothic")) # 폰트 |
- x축 크기가 그래프마다 다르므로 해석 조심
- 막대 길이 같아도 단어 빈도 다름
- 두 텍스트 단어 빈도 비교 X
- 각 텍스트에서 상대적으로 중요한 단어가 무엇인지 중심으로 해석
(7) 주요 단어가 사용된 문장 보기
speeches_sentence <- bind_speeches %>% as_tibble() %>% unnest_tokens(input = value, output = sentence, token = "sentences") speeches_sentence head(speeches_sentence) ## # A tibble: 6 x 2 ## president sentence ## <chr> <chr> ## 1 moon "정권교체 하겠습니다!" ## 2 moon "정치교체 하겠습니다!" ## 3 moon "시대교체 하겠습니다!" ## 4 moon "" ## 5 moon "‘불비불명(不飛不鳴)’이라는 고사가 있습니다."… ## 6 moon "남쪽 언덕 나뭇가지에 앉아, 3년 동안 날지도 … tail(speeches_sentence) ## # A tibble: 6 x 2 ## president sentence ## ## 1 park 국민 여러분의 행복이 곧 저의 행복입니다.… ## 2 park 사랑하는 조국 대한민국과 국민 여러분을 위해, 앞… ## 3 park 그 길을 함께 해주시길 부탁드립니다.… ## 4 park 감사합니다. ## 5 park 2012년 7월 10일 ## 6 park 새누리당 예비후보 박근혜 |
(8) 주요단어가 사용된 문장 추출 - str_detect()
speeches_sentence %>% filter(president == "moon" & str_detect(sentence, "복지국가")) ## # A tibble: 8 x 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 x 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 국민행복의 길을 열어갈 세 번째 과제로, 우리의 실정에 맞으면서 … ## # … with 9 more rows |
(9) 중요도가 비슷한 단어 살펴보기
- odds_ratio가 1에 가까운 단어 추출
- 대부분 보편적인 의미를 지니는 단어
frequency_wide %>% arrange(abs(1 - odds_ratio)) %>% head(10) ## # A tibble: 10 x 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 |
- 중요도가 비슷하면서 빈도가 높은 단어: 두 텍스트에서 모두 강조한 단어
frequency_wide %>% filter(moon >= 5 & park >= 5) %>% arrange(abs(1 - odds_ratio)) %>% head(10) # # A tibble: 10 x 6 # word moon park ratio_moon ratio_park odds_ratio # <chr> <int> <int> <dbl> <dbl> <dbl> # 1 사회 14 9 0.00659 0.00555 1.19 # 2 경제 15 15 0.00703 0.00887 0.793 # 3 사람 9 9 0.00440 0.00555 0.793 # 4 지원 5 5 0.00264 0.00333 0.793 # 5 불안 7 8 0.00352 0.00499 0.704 # 6 우리 17 10 0.00791 0.00610 1.30 # 7 산업 9 5 0.00440 0.00333 1.32 # 8 대한민국 11 6 0.00527 0.00388 1.36 # 9 국가 7 10 0.00352 0.00610 0.576 # 10 교육 6 9 0.00308 0.00555 0.555 |