1 제7회 지방 선거

위키백과 대한민국 제7회 지방 선거 여론 조사 웹페이지에서 최신 내용을 확인할 수 있다. 데이터를 분석하는 현재 시점(2018-05-22) 지방선거는 앞으로 22일 남았다.

더불어 민주당으로 기울어진 운동장에서 자유한국당, 바른미래당, 민주평화당, 정의당을 비롯한 각당이 각축을 벌이고 있다. 결국 제6회 지방선거와 제7회 6.13지방선거 후 정계개편 방향에 대해서 파악할 필요가 있다.

이를 위해서 제6회 지방선거 결과와 현재 시점(2018-05-22) 제7회 지방선거 여론조사를 비교하여 약 22일 남은 시점 지지층 결집과 향후 정계지형도를 파악해 보자.

2 데이터

2.1 제6회 지방선거 데이터

위키백과 제6히 지방선거 웹페이지에서 선거 결과를 받아와서 이를 데이터테이블로 표현한다. 경상북도지사를 3선 연임에 성공한 김관용 경북지사가 눈에 들어온다. 현 국무총리인 이낙연 총리는 78% 득표율로 가장 높은 득표율을 기록했으며, 박원순 서울특별시장이 가장 많은 득표를 했으며, 세종특별자치시장은 36,203표를 득표하고 광역단체장에 당당히 이름을 올렸다.

# 1. 데이터 -----
## 1.1. 제 6 회 지방선거 결과 -----
### 데이터 가져오기
Sys.setlocale("LC_ALL", "C")

six_url <- "https://ko.wikipedia.org/wiki/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD_%EC%A0%9C6%ED%9A%8C_%EC%A7%80%EB%B0%A9_%EC%84%A0%EA%B1%B0"

sido_six_df <- read_html(six_url) %>% 
    html_node(xpath='//*[@id="mw-content-text"]/div/table[4]') %>% 
    html_table(fill=TRUE)

Sys.setlocale("LC_ALL", "Korean")

### 데이터 정제
sido_six_df <- sido_six_df %>% 
    filter(!str_detect(지역, "투표율")) %>% 
    mutate(득표수 = str_replace_all(득표수, ",", "") %>% as.integer,
              득표율 = str_replace_all(득표율, "%", "") %>% as.numeric) %>% 
    mutate(선수 = ifelse(비고 == "", "초선", 비고)) %>% 
    mutate(선수 = factor(선수, levels = c("초선", "재선", "3선"))) %>% 
    rename(시도명=지역)
### 데이터 저장
DT::datatable(sido_six_df)

2.2 제7회 지방선거 여론조사

위키백과 제7회 지방선거 여론조사 웹페이지에서 현재시점 여론조사 결과를 가져온다. 현재시점(2018-05-22) 여론조사결과가 공표되지 않은 곳도 다수 있고, 경쟁이 치열한 일부 시도의 경우 다양한 여론조사기관을 통해 복수의 여론조사 결과도 공표되어 있다.

# 1. 데이터 -----
## 1.1. 제 7 회 지방선거 여론조사 -----
### 데이터 가져오기
seven_url <- "https://ko.wikipedia.org/wiki/대한민국_제7회_지방_선거_여론_조사"
sido_v <- c("서울특별시장","인천광역시장","경기도지사","강원도지사","대전광역시장","세종특별자치시장","충청남도지사","충청북도지사","광주광역시장","전라남도지사","전라북도지사","부산광역시장","대구광역시장","울산광역시장","경상남도지사","경상북도지사","제주특별자치도지사")

crawl_survey <- function(sido_index) {
    sido_df <- read_html(seven_url) %>% 
        html_nodes("table") %>% 
        .[[sido_index+1]] %>% 
        html_table(fill=TRUE)
    
    sido_df <- sido_df %>% 
        mutate_at(vars(contains("당")), funs(str_replace_all(., "%", ""))) %>% 
        mutate_at(vars(contains("기타")), funs(str_replace_all(., "%", ""))) %>% 
        mutate_at(vars(contains("응답")), funs(str_replace_all(., "명|,", "")))
    
    sido_df <- sido_df %>% filter(! row_number() %in% c(1)) %>% 
        separate("조사 기간", into=c("시작일", "종료일"), sep="\\~") %>% 
        separate(시작일, into=c("월", "일"), sep=" ") %>% 
        mutate(월 = str_extract(월, "[0-9]+"),
                일 = str_extract(일, "[0-9]+"))  %>% 
        mutate(조사일 = ymd(str_c("2018-", 월, "-", 일))) %>% 
        select(-월, -일, -종료일) %>% 
        mutate("시도명" = sido_v[sido_index])
    
    return(sido_df)
}

assign(sido_v[1], crawl_survey(1)) # 1.1    서울특별시장
assign(sido_v[2], crawl_survey(2)) # 1.2    인천광역시장
assign(sido_v[3], crawl_survey(3)) # 1.3    경기도지사
assign(sido_v[4], crawl_survey(4)) # 1.4    강원도지사
assign(sido_v[5], crawl_survey(5)) # 1.5    대전광역시장
assign(sido_v[6], crawl_survey(6)) # 1.6    세종특별자치시장
assign(sido_v[7], crawl_survey(7)) # 1.7    충청남도지사
assign(sido_v[8], crawl_survey(8)) # 1.8    충청북도지사
assign(sido_v[9], crawl_survey(9)) # 1.9    광주광역시장
assign(sido_v[10], crawl_survey(10)) # 1.10 전라남도지사
assign(sido_v[11], crawl_survey(11)) # 1.11 전라북도지사
assign(sido_v[12], crawl_survey(12)) # 1.12 부산광역시장
assign(sido_v[13], crawl_survey(13)) # 1.13 대구광역시장
assign(sido_v[14], crawl_survey(14)) # 1.14 울산광역시장
assign(sido_v[15], crawl_survey(15)) # 1.15 경상남도지사
assign(sido_v[16], crawl_survey(16)) # 1.16 경상북도지사
assign(sido_v[17], crawl_survey(17)) # 1.17 제주특별자치도지사

sido_list <-  mget(sido_v)
sido_survey_df <- map_df(sido_list, bind_rows)
DT::datatable(sido_seven_df)

2.3 지방선거 정계개편 1

Slopegraph를 통해서 제6회 지방선거와 제7회 지방선거 전후변화를 시각적으로 표현하는데 적절할 것으로 판단된다. 이를 위해서 Edward Tufte’s Slopegraphs and political fortunes in Ontario의 코드를 참조해서 정계개편 전후를 비교하여 시각화한다.

bumpchart2<-function (y, top.labels = colnames(y), labels = rep(rownames(y),2), 
                      rank = TRUE, mar = c(2, 8, 5, 8), pch = 19, col = par("fg"), 
                      lty = 1, lwd = 1, arrows = FALSE, ...) {
    
    if (missing(y)) stop("Usage: bumpchart(y,top.labels,labels,...)")
    ydim <- dim(y)
    if (is.null(ydim)) stop("y must be a matrix or data frame")
    oldmar <- par("mar")
    par(mar = mar)
    if (rank) 
        y <- apply(y, 2, rank)
    labels <- rev(labels)
    pch = rev(pch)
    col = rev(col)
    lty = rev(lty)
    lwd = rev(lwd)
    y <- apply(y, 2, rev)
    if (arrows) {
        matplot(t(y), ylab = "", type = "p", pch = pch, col = col, 
                axes = FALSE)
        for (row in 1:(ydim[2] - 1)) p2p_arrows(rep(row, ydim[1]), 
                                                y[, row], rep(row + 1, ydim[1]), y[, row + 1], col = col, 
                                                lty = lty, lwd = lwd, ...)
    }
    else matplot(t(y), ylab = "", type = "b", pch = pch, col = col, 
                 lty = lty, lwd = lwd, axes = FALSE, ...)
    par(xpd = TRUE)
    xylim <- par("usr")
    minspacing <- strheight("M") * 1.5
    text(1:ydim[2], xylim[4], top.labels)
    text(xylim[1], y[,1], labels[(ydim[1]+1):(2*ydim[1])], adj = 1)
    cat(y[,1],"\n")
    text(xylim[2], y[,2], labels[1:ydim[1]], adj = 0)
    cat(y[,2],"\n")
    par(mar = oldmar, xpd = FALSE)
}

# 1. 데이터 -----
## 1.1. 제6회 
sido_six_df <- read_rds("data/sido_six_df.rds")
sido_six_viz_df <- sido_six_df %>% 
    count(정당)

## 1.2. 제7회 여론조사
sido_seven_df <- read_rds("data/sido_survey_df.rds")

names(sido_seven_df) <- c("조사 의뢰", "조사 기관", "응답 인원", "더불어민주당", "자유한국당", "바른미래당", "정의당", "기타·무응답", "참고", "조사일", "시도명", "민주평화당", "무소속")

sido_seven_now_df <- sido_seven_df %>% 
    mutate(무소속 = str_replace_all(무소속, "%", "") %>% as.numeric,
              민주평화당 = str_replace_all(민주평화당, "%", "") %>% as.numeric) %>% 
    group_by(시도명) %>% 
    filter(조사일 == max(조사일)) %>% 
    select(시도명, contains("당"), contains("무소속")) %>% 
    ungroup()

current_df <- sido_seven_now_df %>% 
    gather(정당, 지지율, -시도명) %>% 
    mutate(지지율 = as.numeric(지지율)) %>% 
    group_by(시도명) %>% 
    summarise(지지율 = max(지지율, na.rm=TRUE))

sido_current_df <- sido_seven_now_df %>% 
    gather(정당, 지지율, -시도명) %>% 
    mutate(지지율 = as.numeric(지지율)) %>% 
    inner_join(current_df)

sido_seven_viz_df <- sido_current_df %>% 
    count(정당)

# 2. 시각화 -----
sido_six_viz_df <- sido_six_viz_df %>% 
    mutate(정당 = case_when(str_detect(정당, "새누리당") ~ "자유한국당",
                            str_detect(정당, "새정치민주연합") ~ "더불어민주당")) %>% 
    rename(제6회 = n)

sido_seven_viz_df <- sido_seven_viz_df %>% 
    rename(제7회 = n)

sido_viz_df <- full_join(sido_six_viz_df, sido_seven_viz_df)  %>% 
    mutate(제6회 = ifelse(is.na(제6회), 0, 제6회) %>% as.integer) %>% 
    as.data.frame() %>% 
    column_to_rownames("정당")

bumpchart2(sido_viz_df, mar=c(6, 12, 6, 12),col=c("red", "blue", "darkgray"),
           labels=c(paste(rownames(sido_viz_df),sido_viz_df[,1],sep=" "),
                    paste(sido_viz_df[,2],rownames(sido_viz_df),sep=" ")),rank=FALSE,lwd=3,
           top.labels = c("제6회", "제7회"))
0 9 8 
1 8 1 
title(main="지방선거 전후 정계 개편", line=1.5, col.main="navy blue")
title(sub="https://ko.wikipedia.org/wiki/대한민국_제6회_지방_선거\nhttps://ko.wikipedia.org/wiki/대한민국_제7회_지방_선거",
      line=2, cex.sub =.8, col.sub="blue")

2.4 정계개편 지도

지방선거 전후 정치지형변화를 공간정보로 지도위에 표현해 보자. 물론, 충청북도, 대전, 세종, 광주, 전라남북 등 여론조사가 공표되고 있지않아 공란으로 남아있지만, 조만간 여론조사가 공포되면 지방선거 판세예측과 더불어 정계개편 방향을 한눈에 파악하는데 유용할 것으로 생각됩니다.

# 1. 데이터 -----
# 1.0. 지도정보 룩업표
spatial_tbl <- tribble(
    ~"CTPRVN_CD", ~"CTP_KOR_NM", ~"시도명",
    11,  "서울특별시",    "서울특별시장",
    26,  "부산광역시",  "부산광역시장",
    27,  "대구광역시",    "대구광역시장",
    28,  "인천광역시",    "인천광역시장",
    29,  "광주광역시",    "광주광역시장",
    30,  "대전광역시",    "대전광역시장",
    31,  "울산광역시",   "울산광역시장",
    36,  "세종특별자치시", "세종특별자치시장",
    41,  "경기도",         "경기도지사",
    42,  "강원도",         "강원도지사",
    43,  "충청북도",      "충청북도지사",
    44,  "충청남도",      "충청남도지사",
    45,  "전라북도",      "전라북도지사",
    46,  "전라남도",      "전라남도지사",
    47,  "경상북도",      "경상북도지사",
    48,  "경상남도",      "경상남도지사",
    50,  "제주특별자치도", "제주특별자치도지사"
)

## 1.1. 제6회 지방선거
sido_six_df <- left_join(sido_six_df, spatial_tbl) %>% 
    mutate(CTPRVN_CD = factor(CTPRVN_CD))

## 1.2. 제7회 여론조사
sido_seven_now_df <- sido_seven_df %>% 
    mutate(무소속 = str_replace_all(무소속, "%", "") %>% as.numeric,
              민주평화당 = str_replace_all(민주평화당, "%", "") %>% as.numeric) %>% 
    group_by(시도명) %>% 
    filter(조사일 == max(조사일)) %>% 
    select(시도명, contains("당"), contains("무소속")) %>% 
    ungroup()

current_df <- sido_seven_now_df %>% 
    gather(정당, 지지율, -시도명) %>% 
    mutate(지지율 = as.numeric(지지율)) %>% 
    group_by(시도명) %>% 
    summarise(지지율 = max(지지율, na.rm=TRUE))

sido_current_df <- sido_seven_now_df %>% 
    gather(정당, 지지율, -시도명) %>% 
    mutate(지지율 = as.numeric(지지율)) %>% 
    inner_join(current_df)

## 1.3. 지방선거 지도 -----

## 1.2. shp 파일 불러오기 --------
# sido_shp <- st_read("data/shapefile_sido/TL_SCCO_CTPRVN.shp")
# sido_shp <- st_read("data/TL_SCCO_CTPRVN.shp")
sido_shp$CTP_KOR_NM <- iconv(sido_shp$CTP_KOR_NM, from = "CP949", to = "UTF-8", sub = NA, mark = TRUE, toRaw = FALSE)
sido_simp_shp <- st_simplify(sido_shp, dTolerance = 100)

# 2. 시각화 -----
## 2.1. 제6회 지방선거
sido_six_shp <- left_join(sido_simp_shp, sido_six_df)

sido_six_g <- sido_six_shp %>% ggplot(aes(fill=sido_six_shp$정당)) +
    geom_sf() +
    theme_minimal(base_family="NanumGothic") +
    labs(title="제6회 지방선거 - 광역자치단체장") + 
    theme(legend.position = "right") +
    scale_fill_manual(values =c("red", "blue")) +
    labs(fill="정당") +
    theme(legend.position = "top")

## 2.2. 제7회 지방선거
sido_current_df <- left_join(sido_current_df, spatial_tbl) %>% 
    mutate(CTPRVN_CD = factor(CTPRVN_CD))

sido_seven_shp <- left_join(sido_simp_shp, sido_current_df)

sido_seven_g <- sido_seven_shp %>% ggplot(aes(fill=sido_seven_shp$정당)) +
    geom_sf() +
    theme_minimal(base_family="NanumGothic") +
    labs(title="제7회 지방선거 - 광역자치단체장") + 
    theme(legend.position = "right") +
    scale_fill_manual(values =c("blue", "darkgray", "red")) +
    labs(fill="정당") +
    theme(legend.position = "top")

## 2.3. 제6회, 제7회 지방선거 비교

grid.arrange(sido_six_g, sido_seven_g, nrow=1)