준비 작업

작업폴더 지정 및 확인

rm(list=ls())        # 기존에 작업하던 데이터, 변량 모두 제거 

getwd()              # 작업폴더 확인
setwd("C:/study")    # 작업폴더 지정
getwd()              # 작업폴더 재확인

사용할 패키지 장착

## In Ch 9 --------------
# library(foreign)
# library(dplyr)
# library(ggplot2)
# library(readxl)
# library(descr)
# library(readxl)
# library(gridExtra)

## In Ch 10 --------------
# library(KoNLP)
# library(dplyr)
# library(stringr)
# library(rJava)
# library(memoise)
# library(wordcloud)
# library(RColorBrewer)

Ch 9 데이터 분석 프로젝트

  • 한국 복지패널 데이터 수집, 전처리
  • 각 주제별 데이터 분석과정

09 - 1 데이터 취득, 확인

# 데이터 불러오기
raw_welfare <- foreign::read.spss(file = "Koweps_hpc10_2015_beta1.sav",
                                  to.data.frame = T)
## Warning in foreign::read.spss(file = "Koweps_hpc10_2015_beta1.sav",
## to.data.frame = T): Koweps_hpc10_2015_beta1.sav: Compression bias (0) is
## not the usual value of 100
# 복사본 만들기
welfare <- raw_welfare

# 데이터 확인 
class(welfare)
## [1] "data.frame"
dim(welfare)
## [1] 16664   957

관측치 16664개, 변수가 957개임을 알 수 있다.

# 데이터가 너무 커서 dplyr::sample_n 을 사용해서 샘플링 한 데이터만 확인
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
welfare %>%
  sample_n(1000) %>%
  DT::datatable()
## Warning in instance$preRenderHook(instance): It seems your data is too
## big for client-side DataTables. You may consider server-side processing:
## http://rstudio.github.io/DT/server.html

데이터 전처리

library(dplyr)
library(ggplot2)
welfare <- welfare %>%
  rename(sex         = h10_g3,      # 성별
         birth       = h10_g4,      # 출생년도
         marriage    = h10_g10,     # 혼인 상태
         religion    = h10_g11,     # 종교
         income      = p1002_8aq1,  # 월급(수입)
         code_job    = h10_eco9,    # 직업 코드
         code_region = h10_reg7)    # 지역 코드 

# 성별 변수 결측치, 이상치 확인 및 처리
descr::CrossTable(welfare$sex)    
##    Cell Contents 
## |-------------------------|
## |                       N | 
## |           N / Row Total | 
## |-------------------------|
## 
## |        1 |        2 |
## |----------|----------|
## |     7578 |     9086 |
## |    0.455 |    0.545 |
## |----------|----------|
# output 중에서 1은 남성, 2는 여성을 뜻함 
# 결측치가 존재하지 않음을 알 수 있다. 

# 성별 변수 factor 변수로 변환
welfare <- welfare %>%
  mutate(sex = ifelse(sex == 1, "male", "female"))

descr::CrossTable(welfare$sex)
##    Cell Contents 
## |-------------------------|
## |                       N | 
## |           N / Row Total | 
## |-------------------------|
## 
## |   female |     male |
## |----------|----------|
## |     9086 |     7578 |
## |    0.545 |    0.455 |
## |----------|----------|
qplot(welfare$sex)

# 월급 변수
# qplot으로는 0, 9999값(결측치)의 존재성과 갯수 파악이 힘들어서 다음과 같이 실행.
summary(welfare$income)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##     0.0   122.0   192.5   241.6   316.6  2400.0   12030
# 연속형 변수라서 확인하기 힘들어서 histogram으로 확인
welfare %>%
  ggplot(aes(income)) + 
  geom_histogram()

# 이상치 결측 처리
welfare <- welfare %>%
  mutate(income = ifelse(income %in% c(0, 9999), NA, income))

# 결측 처리 후의 income 변수 분포 확인
summary(welfare$income)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    0.46  123.00  193.00  242.35  316.70 2400.00   12044

09 - 2 성별에 따른 월급 차이 분석하기

성별에 따른 월급의 차이가 있을까?

# 성별 월급 평균표 만들기
sex_income <- welfare %>%
  filter(!is.na(income)) %>%
  group_by(sex) %>%
  summarise(mean_income = mean(income))

# 시각화 
sex_income %>% 
  ggplot(aes(sex, mean_income)) +
  geom_col()

# 데이터 해석 : 평균 월급 값과 그래프를 통해서 평균적으로 남성의 월급이 여성의 두 배 가까이 된다.

# 결측치 제외 한 값들의 평균 월급에 대한 비교이므로 아래처럼 실행 
welfare %>%
  filter(!is.na(income)) %>%              # 결측치 아닌 값들만 필터링
  group_by(sex) %>%                       # 성별 그룹핑
  summarise(mean_income = mean(income))   # 각 성별의 평균 월급
## # A tibble: 2 x 2
##      sex mean_income
##    <chr>       <dbl>
## 1 female    163.2471
## 2   male    312.2932
welfare %>%
  filter(!is.na(income)) %>%              # 결측치 아닌 관측치만 필터링
  ggplot(aes(sex, income)) +              # 성별과 월급
  geom_jitter(col = "gray") +             # 관측치 분포 보기위해서 
  geom_boxplot(alpha = .5)                # 상자를 반투명하게 출력 

09 - 3 나이와 월급의 관계

몇 살 때 월급을 가장 많이 받을까?

library(dplyr)

# 이상치 확인
summary(welfare$birth)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1907    1946    1966    1968    1988    2014
# 결측치 확인
descr::CrossTable(is.na(welfare$birth))
##    Cell Contents 
## |-------------------------|
## |                       N | 
## |-------------------------|
## 
## |     FALSE |
## |-----------|
## |     16664 |
## |-----------|
# 파생변수 만들기 - 출생년도를 이용해서 나이 변수 추가하기 
# 데이터 범위가 1900년부터 2014까지이므로 2015에서 빼는게 맞음.
# 책에 있는 코드 : welfare$age <- 2015 - welfare$birth + 1    
welfare <- welfare %>%
  mutate(age = 2016 - birth)

# 나이 변수 확인 
summary(welfare$age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    2.00   28.00   50.00   48.43   70.00  109.00
# 나이에 따른 월급 평균표 만들기
age_income <- welfare %>%
  filter(!is.na(income)) %>%               # 결측치 제거
  group_by(age) %>%                        # 나이별로 그룹핑
  summarise(mean_income = mean(income))    # 요약통계량 구한 것을 최종적으로 age_income 에 저장

# 시각화 - 나이에 따른 평균 월급 추세선 그리기
age_income %>%
  ggplot(aes(age, mean_income)) +
  geom_line()

# 시각화 - 나이에 따른 평균 월급 산점도 그리기
welfare %>%
  filter(!is.na(income)) %>%     # 결측치 제거 
  ggplot(aes(age, income)) +
  geom_point()

09 - 4 연령대에 따른 월급 차이

어떤 연령대의 월급이 가장 많을까?

# 연령대 파생변수 만들기 
welfare <- welfare %>%
  mutate(ageg = factor(ifelse(age < 30, "young",
                              ifelse(age <= 59, "middle", "old")),
                       levels = c("young", "middle", "old")))

# 뒤에서 연령대와 평균 월급 등 작업할 때 반복적으로 levels 주기 귀찮아서 처음부터 부여함. 

# 빈도수와 비율 확인 
descr::CrossTable(welfare$ageg)
##    Cell Contents 
## |-------------------------|
## |                       N | 
## |           N / Row Total | 
## |-------------------------|
## 
## |    young |   middle |      old |
## |----------|----------|----------|
## |     4334 |     6049 |     6281 |
## |    0.260 |    0.363 |    0.377 |
## |----------|----------|----------|
qplot(welfare$ageg)

# 연령대에 따른 월급차이 분석하기
# 연령대별 평균 월급표 만들기
ageg_income <- welfare %>%
  filter(!is.na(income)) %>%
  group_by(ageg) %>%
  summarise(mean_income = mean(income))

# 연령대별 평균 월급표 시각화
ageg_income %>%
  ggplot(aes(ageg, mean_income)) +
  geom_col()

09 - 5 연령대 및 성별 월급 차이

성별 월급 차이는 연령대별로 다를까?

09 - 5 - 1 연령대 및 성별 월급 차이

# 연령대 및 성별 월급 평균표 만들기
sex_income <- welfare %>%
  filter(!is.na(income)) %>%
  group_by(ageg, sex) %>%
  summarise(mean_income = mean(income))

# 그래프 만들기 - 연령대, 성별에 따른 평균 월급 막대그래프 
sex_income %>%
  ggplot(aes(ageg, mean_income,
             fill = sex)) +
  geom_col()

# 그래프 만들기 - 남녀 따로
sex_income %>%
  ggplot(aes(ageg, mean_income,
             fill = sex)) +
  geom_col(position = "dodge")

09 - 5 - 2 나이 및 성별 월급 차이 분석하기

# 성별, 나이에 따른 평균 월급표 만들기 
sex_age <- welfare %>%
  filter(!is.na(income)) %>%
  group_by(age, sex) %>%
  summarise(mean_income = mean(income))

# 생성한 데이터 확인
glimpse(sex_age)
## Observations: 134
## Variables: 3
## $ age         <dbl> 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26...
## $ sex         <chr> "female", "male", "female", "male", "female", "mal...
## $ mean_income <dbl> 147.4500, 69.0000, 106.9789, 102.0500, 139.8547, 1...
# Book Code
sex_age %>%
  ggplot(aes(age, mean_income,
             col = sex)) +
  geom_line()

# My Code
sex_age %>%
  ggplot(aes(age, mean_income,
             col = sex)) +
  geom_jitter(col = "gray") +
  geom_smooth()
## `geom_smooth()` using method = 'loess'

09 - 6 직업별 월급 차이

어떤 직업이 월급을 가장 많이 받을까?

# 기존에 작업하던 welfare 데이터에는 직업 코드(code_job)만 존재함.
# 따라서 코드 넘버로 보기 힘든 이유로 job 변수 데이터를 welfare에 추가함.
library(readxl)
list_job <- read_excel("Koweps_Codebook.xlsx",
                       col_names = T,
                       sheet = 2)

# list_job 데이터 확인
head(list_job)
## # A tibble: 6 x 2
##   code_job                                 job
##      <dbl>                               <chr>
## 1      111 의회의원 고위공무원 및 공공단체임원
## 2      112                        기업고위임원
## 3      120             행정 및 경영지원 관리자
## 4      131       연구 교육 및 법률 관련 관리자
## 5      132                 보험 및 금융 관리자
## 6      133        보건 및 사회복지 관련 관리자
glimpse(list_job)
## Observations: 149
## Variables: 2
## $ code_job <dbl> 111, 112, 120, 131, 132, 133, 134, 135, 139, 141, 149...
## $ job      <chr> "의회의원 고위공무원 및 공공단체임원", "기업고위임원", "행정 및 경영지원 관리자", "연구...
# welfare 와 list_job 데이터 합치기 
welfare <- welfare %>%
  left_join(list_job,           # 기존 welfare 데이터에 list_job 합치기 
            id = "code_job")    # code_job을 기준으로 합침
## Joining, by = "code_job"
# 추가한 최종 데이터 확인
welfare %>%
  filter(!is.na(code_job)) %>%
  select(code_job, job) %>%
  head(10)
##    code_job                                job
## 1       942                   경비원 및 검표원
## 2       762                             전기공
## 3       530 방문 노점 및 통신 판매 관련 종사자
## 4       999        기타 서비스관련 단순 종사원
## 5       312                    경영관련 사무원
## 6       254             문리 기술 및 예능 강사
## 7       510                        영업 종사자
## 8       530 방문 노점 및 통신 판매 관련 종사자
## 9       286   스포츠 및 레크레이션 관련 전문가
## 10      521                   매장 판매 종사자
### 분석 시작 ###

# 직업별 평균 월급표 만들기
# welfare 데이터가 data.frame 이므로 job_income 데이터도 데이터 프레임 속성을 물려받음.
job_income <- welfare %>%
  filter(!is.na(job) & !is.na(income)) %>%
  group_by(job) %>%
  summarise(mean_income = mean(income))

평균 월급 상위 10개, 하위 10개를 막대그래프로 출력

# 상위 10개 직업 시각화
top10 <- job_income %>%            # job_income 데이터에서
  arrange(desc(mean_income)) %>%   # 평균 월급을 내림차순으로 나열
  head(10)                         # 상위 10개만 추출 후, 최종적으로 top10 에 저장
  
top10 %>%
  ggplot(aes(reorder(job, mean_income),
             mean_income)) +
  geom_col() + 
  coord_flip()

# 상위 10개 직업 요약통계량
summary(top10)
##      job             mean_income   
##  Length:10          Min.   :531.7  
##  Class :character   1st Qu.:551.8  
##  Mode  :character   Median :568.1  
##                     Mean   :647.6  
##                     3rd Qu.:744.0  
##                     Max.   :845.1
# 평균 월급 오름차순 정렬 후 하위 10개를 저장 및 출력
bottom10 <- job_income %>%
  arrange(mean_income) %>%
  head(10)

bottom10 %>%
  ggplot(aes(reorder(job, -mean_income),
             mean_income)) +
  geom_col() +
  coord_flip() +
  ylim(0, 850)     # 상위 10개 직업 요약통계량에 최댓값이 845임을 이용함.

평균월급 상위 10개 직업과 하위 10개 직업 한눈에 같이 보기

library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
job_top <- top10 %>%
  ggplot(aes(reorder(job, mean_income),
             mean_income)) +
  geom_col() + 
  coord_flip()

job_bottom <- bottom10 %>%
  ggplot(aes(reorder(job, -mean_income),
             mean_income)) +
  geom_col() +
  coord_flip() +
  ylim(0, 850)

grid.arrange(job_top, job_bottom,
             ncol=2)

09 - 7 성별 직업 빈도

성별로 어떤 직업이 가장 많을까?

# 1. 성별 직업 빈도표 작성하기 
# 1 - 1 남성 직업 빈도 상위 10 개 추출
job_male <- welfare %>%
  filter(!is.na(job) & sex == "male") %>%
  group_by(job) %>%
  summarise(n = n()) %>%
  arrange(desc(n)) %>%
  head(10)

job_male
## # A tibble: 10 x 2
##                         job     n
##                       <chr> <int>
##  1          작물재배 종사자   640
##  2            자동차 운전원   251
##  3          경영관련 사무원   213
##  4              영업 종사자   141
##  5         매장 판매 종사자   132
##  6     제조관련 단순 종사원   104
##  7    청소원 및 환경 미화원    97
##  8 건설 및 광업 단순 종사원    95
##  9         경비원 및 검표원    95
## 10              행정 사무원    92
# 1 - 2 여성 직업 빈도 상위 10 개 추출
job_female <- welfare %>%
  filter(!is.na(job) & sex == "female") %>%
  group_by(job) %>%
  summarise(n = n()) %>%
  arrange(desc(n)) %>%
  head(10)

job_female
## # A tibble: 10 x 2
##                             job     n
##                           <chr> <int>
##  1              작물재배 종사자   680
##  2        청소원 및 환경 미화원   228
##  3             매장 판매 종사자   221
##  4         제조관련 단순 종사원   185
##  5          회계 및 경리 사무원   176
##  6            음식서비스 종사자   149
##  7             주방장 및 조리사   126
##  8          가사 및 육아 도우미   125
##  9 의료 복지 관련 서비스 종사자   121
## 10         음식관련 단순 종사원   104
# 2. 그래프 만들기
# 2 - 1 남성 직업 빈도 상위 10개 직업 시각화
job_male %>%
  ggplot(aes(reorder(job, n),
             n)) +
  geom_col() +
  coord_flip()                  # 막대그래프 전치 시키는 설정

# 2 - 2 여성 직업 빈도 상위 10개 직업 시각화
job_female %>%
  ggplot(aes(reorder(job, n),
             n)) +
  geom_col() +
  coord_flip()

각 성별 직업 상위 10개 한눈에 같이 보기

library(gridExtra)

p1 <- job_male %>%
  ggplot(aes(reorder(job, n),
             n)) +
  geom_col() +
  coord_flip()

p2 <- job_female %>%
  ggplot(aes(reorder(job, n),
             n)) +
  geom_col() +
  coord_flip()

grid.arrange(p1, p2,
             ncol=2)

09 - 8 종교 유무에 따른 이혼율

종교가 있는 사람들이 이혼을 덜 할까?

library(descr)

# 09 - 8 - 1 종교 변수
class(welfare$religion)
## [1] "numeric"
descr::CrossTable(welfare$religion)
##    Cell Contents 
## |-------------------------|
## |                       N | 
## |           N / Row Total | 
## |-------------------------|
## 
## |        1 |        2 |
## |----------|----------|
## |     8047 |     8617 |
## |    0.483 |    0.517 |
## |----------|----------|
# 1 == 종교 있음 / 2 == 무교 / 3 == 모름 or 무응답 

## 책에서는 종교의 유무만 factor로 변환했는데 3번이라 답한것도 업다고 변환함.
## 3이라고 응답한 변수에 대해서는 No 라고 하기보다 아예 결측치로 처리해도 될듯함.

# religion 변수 전처리 - 책에 있는 코드
# welfare$religion <- ifelse(welfare$religion == 1, "Yes", "No")

# My code
welfare <- welfare %>%
  mutate(religion = ifelse(religion == 1, "Yes", "No"))

# 전처리한 종교 변수 확인 
glimpse(welfare$religion)
##  chr [1:16664] "No" "No" "No" "Yes" "Yes" "Yes" "Yes" "Yes" "Yes" ...
descr::CrossTable(welfare$religion)
##    Cell Contents 
## |-------------------------|
## |                       N | 
## |           N / Row Total | 
## |-------------------------|
## 
## |       No |      Yes |
## |----------|----------|
## |     8617 |     8047 |
## |    0.517 |    0.483 |
## |----------|----------|
# 09 - 8 - 2 혼인 변수
# 파생변수로 이혼 변수를 만드는 과정이다.
# marriage 변수에는 0부터 6까지 6가지 경우인데,
# 그중에 혼인과 이혼만 factor 변수로 부여하고 그 외에는 결측처리 한다.
welfare <- welfare %>%
  mutate(group_marriage = ifelse(welfare$marriage == 1, "marriage",
                                 ifelse(welfare$marriage == 3, "divorce", NA)))

# 혼인 변수 빈도수 확인 
CrossTable(welfare$group_marriage)
##    Cell Contents 
## |-------------------------|
## |                       N | 
## |           N / Row Total | 
## |-------------------------|
## 
## |  divorce | marriage |
## |----------|----------|
## |      712 |     8431 |
## |    0.078 |    0.922 |
## |----------|----------|
qplot(welfare$group_marriage)

# 09 - 8 - 3 종교 유무에 따른 이혼율 표 만들기
religion_marriage <- welfare %>%
  filter(!is.na(group_marriage)) %>%
  group_by(religion, group_marriage) %>%
  summarise(n = n()) %>%
  mutate(pct = round(n/sum(n)*100, 1))

religion_marriage
## # A tibble: 4 x 4
## # Groups:   religion [2]
##   religion group_marriage     n   pct
##      <chr>          <chr> <int> <dbl>
## 1       No        divorce   384   8.3
## 2       No       marriage  4218  91.7
## 3      Yes        divorce   328   7.2
## 4      Yes       marriage  4213  92.8
# 앞서 만든 religion_marriage data 에서 이혼에 해당하는 data만 추출
divorce <- religion_marriage %>%
  filter(group_marriage == "divorce") %>%
  select(religion, pct)

divorce
## # A tibble: 2 x 2
## # Groups:   religion [2]
##   religion   pct
##      <chr> <dbl>
## 1       No   8.3
## 2      Yes   7.2
# 이혼한 대상 시각화 - 막대 그래프 
divorce %>%
  ggplot(aes(religion, pct)) +
  geom_col()

# 연령대 및 종교 유무에 따른 이혼율 분석하기
# - 이혼에 연령과 종교유무가 연관이 있는지 관찰하는 과정 

# 1. 연령대별 이혼율 표 만들기
ageg_marriage <- welfare %>%
  filter(!is.na(group_marriage)) %>%       # 결측치 필터링 
  group_by(ageg, group_marriage) %>%       # ageg == 연령대 
  summarise(n = n()) %>%                   # 각 그룹별 관측치 갯수 파악
  mutate(pct = round(n/sum(n)*100, 1))     # pct == percent : 비율 

ageg_marriage
## # A tibble: 6 x 4
## # Groups:   ageg [3]
##     ageg group_marriage     n   pct
##   <fctr>          <chr> <int> <dbl>
## 1  young        divorce     2   3.3
## 2  young       marriage    58  96.7
## 3 middle        divorce   437   8.9
## 4 middle       marriage  4481  91.1
## 5    old        divorce   273   6.6
## 6    old       marriage  3892  93.4
# 연령대별 이혼율 그래프 만들기
ageg_divorce <- ageg_marriage %>%
  filter(ageg != "young" & group_marriage == "divorce") %>%
  select(ageg, pct)

ageg_divorce
## # A tibble: 2 x 2
## # Groups:   ageg [2]
##     ageg   pct
##   <fctr> <dbl>
## 1 middle   8.9
## 2    old   6.6
ageg_divorce %>%
  ggplot(aes(ageg, pct)) +
  geom_col()

# 연령대, 종교 유무, 결혼 상태별 비율표 만들기
ageg_religion_marriage <- welfare %>%
  filter(!is.na(group_marriage) & ageg != "young") %>%
  group_by(ageg, religion, group_marriage) %>%
  summarise(n = n()) %>%
  mutate(pct = round(n/sum(n)*100, 1))

# 연령대 및 종교 유무별 이혼율 표 만들기
df_divorce <- ageg_religion_marriage %>%
  filter(group_marriage == "divorce") %>%
  select(ageg, religion, pct)

df_divorce
## # A tibble: 4 x 3
## # Groups:   ageg, religion [4]
##     ageg religion   pct
##   <fctr>    <chr> <dbl>
## 1 middle       No   9.7
## 2 middle      Yes   7.9
## 3    old       No   6.5
## 4    old      Yes   6.6
# 연령대 및 종교 유무에 따른 이혼율 그래프 만들기 
df_divorce %>%
  ggplot(aes(ageg, pct,
             fill = religion)) +
  geom_col(position = "dodge")

09 - 9 지역별 연령대 비율

# 변수 검토 생략
# 전처리 - 지역 코드 목록 만들기
list_region <- data.frame(code_region = c(1:7),
                          region      = c("서울",
                                          "수도권(인천/경기)",
                                          "부산/경남/울산",
                                          "대구/경북",
                                          "대전/충남",
                                          "강원/충북",
                                          "광주/전남/전북/제주도"))

list_region
##   code_region                region
## 1           1                  서울
## 2           2     수도권(인천/경기)
## 3           3        부산/경남/울산
## 4           4             대구/경북
## 5           5             대전/충남
## 6           6             강원/충북
## 7           7 광주/전남/전북/제주도
# 기존 데이터에 새로 생성한 지역 변수 추가하기 
welfare <- welfare %>%
  left_join(list_region,
            id = "code_region")
## Joining, by = "code_region"
welfare %>%
  select(code_region, region) %>%
  head()
##   code_region region
## 1           1   서울
## 2           1   서울
## 3           1   서울
## 4           1   서울
## 5           1   서울
## 6           1   서울
# 지역별 연령대 비율 분석하기 
# 지역별 연령대 비율표 만들기
region_ageg <- welfare %>%
  group_by(region, ageg) %>%
  summarise(n = n()) %>%
  mutate(pct = round(n/sum(n)*100, 2))

# 그래프 그리기
region_ageg %>%
  ggplot(aes(region, pct,
             fill = ageg)) +
  geom_col() +
  coord_flip()

# 노년층 비율 높은 순으로 막대 정렬하기
list_order_old <- region_ageg %>%
  filter(ageg == "old") %>%
  arrange(pct)

order <- list_order_old$region

region_ageg %>%
  ggplot(aes(region, pct,
             fill = ageg)) +
  geom_col() + 
  coord_flip() +
  scale_x_discrete(limits = order)

Ch 10 Text Mining

  • 텍스트, 트위터 데이터 전처리
  • 워드 클라우드 (Word Cloud)

10 - 0 데이터 수집, 전처리

텍스트 마이닝, 워드 클라우드 준비작업

  • 자연어 처리 패키지인 KoNLP를 사용하기 위해서 Java를 설치.
  • 추가적으로 컴퓨터 설정 - 환경변수에 Java 경로가 올바른지 확인.

10 - 1 힙합 가사 텍스트 마이닝

# 필요한 패키지 장착 
library(KoNLP)
## Checking user defined dictionary!
library(dplyr)
library(stringr)
library(rJava)
library(memoise)

# 사용할 언어 사전 설정 
useNIADic()
## Backup was just finished!
## 983012 words dictionary was built.
# Sejong 사전도 있지만 등록되어있는 단어 수의 차이가 심함. 
# 만약 사용한다면 useSejongDic() 으로 설정해주면 된다.

txt <- readLines("hiphop.txt")
## Warning in readLines("hiphop.txt"): 'hiphop.txt'에서 불완전한 마지막 행이
## 발견되었습니다
head(txt)
## [1] "\"보고 싶다"                  "이렇게 말하니까 더 보고 싶다"
## [3] "너희 사진을 보고 있어도"      "보고 싶다"                   
## [5] "너무 야속한 시간"             "나는 우리가 밉다"
# 특수문자 제거하기 
txt <- stringr::str_replace_all(txt,
                                "\\W", " ")
# "\\W" 는 정규표현식의 하나
# str_replace_all 은 base::gsub() 혹은 replace 와 동일한 기능으로써
# 해당 데이터 내의 문자열을 치환하는 것이다.

# 노래 가사에서 명사 추출
nouns <- KoNLP::extractNoun(txt)

# 추출한 명사 List를 문자열 벡터로 변환, 단어별 빈도표 생성하기 
wordcount <- table(unlist(nouns))

# Data Frame 으로 변환하기 
df_word <- as.data.frame(wordcount,
                         stringsAsFactors = F)

# 변수명 수정하기 
df_word <- df_word %>%
  rename(word = Var1,
         freq = Freq)

# 자주 사용된 단어 빈도표 만들기 
# 두 글자 이상 단어 추출
df_word <- df_word %>%
  filter(nchar(word) >= 2)

# 빈도 순으로 정렬한 후 상위 20개 단어 추출
top_20 <- df_word %>%
  arrange(desc(freq)) %>%
  head(20)

# 시각화 - Word Cloud

# 패키지 장착
library(wordcloud)
## Loading required package: RColorBrewer
library(RColorBrewer)

# 색상 목록 및 그 내부에서 사용할 색상까지 고정해서 시각화하기
pal <- RColorBrewer::brewer.pal(8, "Blues")[5:9]
set.seed(0115)    
# 0115번 워드 클라우드 - 즉 워드 클라우드에 네이밍하는 거라고 보면 됨.

wordcloud(words = df_word$word,    # 단어
          freq = df_word$freq,     # 빈도
          min.freq = 2,            # 최소 단어 빈도
          max.words = 200,         # 최대 시각화 할 단어 수
          random.order = F,        # 고빈도 단어 중앙배치 (F : 중앙배치 / T : 랜덤배치)
          rot.per = .1,            # 회전 단어 비율
          scale = c(4, .3),        # 단어 크기 범위
          colors = pal)            # 색상 목록

10 - 2 국정원 트윗 텍스트 마이닝

library(dplyr)
twitter <- read.csv("twitter.csv",
                    header = T,
                    stringsAsFactors = F,
                    fileEncoding = "UTF-8")

# 전처리 - 뒤에 데이터 프레임 변환하는 과정을 선행해서 함.
twitter <- twitter %>%
  rename(no   = 번호,
         id   = 계정이름,
         date = 작성일,
         tw   = 내용)

twitter <- str_replace_all(twitter$tw,
                           "\\W", " ")

head(twitter)
## [1] "민주당의 ISD관련 주장이 전부 거짓으로 속속 드러나고있다  미국이 ISD를 장악하고 있다고 주장하지만 중재인 123명 가운데 미국인은 10명뿐이라고 한다 "                                                                               
## [2] "말로만  미제타도   사실은  미제환장   김정일 운구차가 링컨 컨티넬탈이던데 북한의 독재자나 우리나라 종북들이나 겉으로는 노동자  서민을 대변한다면서 고급 외제차  아이팟에 자식들 미국 유학에 환장하는 위선자들인거죠"            
## [3] "한나라당이 보수를 버린다네요 뭔가착각하는모냥인에 국민들이보수를싫어하는게 아니라뻘짓거리하는분들을싫어하는겁니다야당이진보어쩌고저쩌고한다고해서그들을조아한다고생각하면대착각"                                                
## [4] "FTA를 대하는 현명한 자세  사실 자유주의 경제의 가장 큰 수해자는 한국이죠  농어업분야 피해를 줄이는 정부대안을 최대한  보완하고 일자리 창출 등 실익을 최대화해 나가는게 현실적인 대처자세일듯 "                                  
## [5] "곽노현씨 갈수록 가관입니다  뇌물질에 아들 병역 의혹까지  도대체 아이들이 뮐 보고 배우겠습니까  이래도 자리 연연하시겠습니까 "                                                                                                   
## [6] "과거 집권시 한미FTA를 적극 추진하던 세력이 이제 집권하면 폐기하겠다고 주장합니다  어이없어 말도 안 나오네요  표만 얻을 수 있다면 국가 안보나 경제가 어떻게 되든 상관없다는 무책임한 행태들  우리 정치의 후진성을 드러내는 거죠 "
# 명사 추출

nouns_twitter <- extractNoun(twitter)

class(twitter)
## [1] "character"
# 추출한 명사 list 를 문자열 벡터로 변환, 단어별 빈도표 생성
wordcount_twitter <- table(unlist(nouns_twitter))

df_word_twitter <- as.data.frame(wordcount_twitter,
                                 stringsAsFactors = F)

# 변수명 수정 
df_word_twitter <- rename(df_word_twitter,
                          word = Var1,
                          freq = Freq)

# 두 글자 이상만 추출하게 조건 제한 
df_word_twitter <- filter(df_word_twitter,
                          nchar(word) >= 2)

# 상위 20개 추출
top_20_twitter <- df_word_twitter %>%
  arrange(desc(freq)) %>%
  head(20)
library(wordcloud)
library(RColorBrewer)

pal <- RColorBrewer::brewer.pal(8, "Blues")[5:9]
set.seed(123)    # 123번 워드 클라우드 - 즉 워드 클라우드에 네이밍하는 거라고 보면 됨.

wordcloud(words = df_word_twitter$word, # 단어
          freq = df_word_twitter$freq,  # 빈도
          min.freq = 10,                # 최소 단어 빈도
          max.words = 200,              # 최대 시각화 할 단어 수
          random.order = F,             # 고빈도 단어 중앙배치 (F : 중앙배치 / T : 랜덤배치)
          rot.per = .1,                 # 회전 단어 비율
          scale = c(4, .5),             # 단어 크기 범위
          colors = pal)                 # 색상 목록