1 역대 지방 선거

역대지방선거 광역단체장에 대한 역대 지방선거(제5회, 제6회)의 더불어민주당, 자유한국당 양당의 득표율 추이를 비교하고, 제6회 지방선거와 제7회 지방선거 여론조사를 현재시점(2018-05-22) 기준 비교한다.

2 데이터

2.1 제5회 지방선거

나무위키 제5회 전국동시지방선거 웹사이트에서 광역단체장 선거결과를 받아온다.

함수형 프로그래밍을 통해 각시도를 키값으로 삼아 list column 티블 자료구조로 구현한 후 후속 데이터 분석을 위해서 데이터프레임으로 변환시킨다.

# 0. 환경설정 -----
library(tidyverse)
library(rvest)
library(glue)
library(plotrix)
library(lubridate)

# 1. 데이터: 제 5 회 지방선거 -----
## 1.1. 제 5 회 지방선거 -----
crawl_five_data <- function(index_id) {
    Sys.setlocale("LC_ALL", "C")
    five_url <- "https://namu.wiki/w/%EC%A0%9C5%ED%9A%8C%20%EC%A0%84%EA%B5%AD%EB%8F%99%EC%8B%9C%EC%A7%80%EB%B0%A9%EC%84%A0%EA%B1%B0"
    
    tmp_tbl <- read_html(five_url) %>% 
        html_nodes('table') %>% 
        .[[index_id]] %>% 
        html_table(fill=TRUE) 

    Sys.setlocale("LC_ALL", "Korean")
    
    sido_name <- tmp_tbl$X1[1]
    names(tmp_tbl) <- tmp_tbl[2,]
    
    res_tbl <- tmp_tbl %>% 
        filter(str_detect(기호, "[0-9]+")) %>% 
        mutate(시도명 = sido_name)
    
    res_list <- list(sido=sido_name, data=res_tbl)
    return(res_list)
}

five_sido_list <- vector("list", length=16) 

for(i in 1:length(five_sido_list)) {
    five_sido_list[[i]] <- crawl_five_data(10+i)
}

## 1.2. 데이터 정제 -----

five_df <- tibble(
    sido = map_chr(five_sido_list, ~.$sido),
    data = map(five_sido_list, ~.$data)
)

five_df <- map_df(five_df$data, bind_rows)

## 1.3. 데이터 내보내기 -----
five_df <- five_df %>% as_tibble() %>% 
    mutate(득표수 = str_replace_all(득표수, ",", "") %>% as.integer,
              득표율 = str_replace(득표율, "%", "") %>% as.numeric,
              순위 = as.integer(순위))

five_df %>% 
    select(시도명, everything()) %>% 
    DT::datatable()

2.2 제6회 지방선거

나무위키 제6회 전국동시지방선거 웹사이트에서 광역단체장 선거결과를 받아온다.

동일한 방식으로 함수형 프로그래밍을 통해 각시도를 키값으로 삼아 list column 티블 자료구조로 구현한 후 후속 데이터 분석을 위해서 데이터프레임으로 변환시킨다.

# 2. 데이터: 제 6 회 지방선거 -----
## 2.1. 제 6 회 지방선거 -----

crawl_six_data <- function(index_id) {
    Sys.setlocale("LC_ALL", "C")
    six_url <- "https://namu.wiki/w/%EC%A0%9C6%ED%9A%8C%20%EC%A0%84%EA%B5%AD%EB%8F%99%EC%8B%9C%EC%A7%80%EB%B0%A9%EC%84%A0%EA%B1%B0/%EA%B4%91%EC%97%AD%EC%9E%90%EC%B9%98%EB%8B%A8%EC%B2%B4%EC%9E%A5"
    
    tmp_tbl <- read_html(six_url) %>% 
        html_nodes('table') %>% 
        .[[index_id]] %>% 
        html_table(fill=TRUE) 
    
    Sys.setlocale("LC_ALL", "Korean")
    
    tmp_tbl
    
    sido_name <- tmp_tbl$X1[1]
    names(tmp_tbl) <- tmp_tbl[2,]
    
    res_tbl <- tmp_tbl %>% 
        filter(str_detect(기호, "[0-9]+")) %>% 
        mutate(시도명 = sido_name)
    
    res_list <- list(sido=sido_name, data=res_tbl)
    
    return(res_list)
}


six_sido_list <- vector("list", length=17) 

for(i in 1:length(six_sido_list)) {
    six_sido_list[[i]] <- crawl_six_data(2+i)
}

## 2.2. 데이터 정제 -----

six_df <- tibble(
    sido = map_chr(six_sido_list, ~.$sido),
    data = map(six_sido_list, ~.$data)
)

six_df <- map_df(six_df$data, bind_rows)

## 1.3. 데이터 내보내기 -----
six_df <- six_df %>% as_tibble() %>% 
    mutate(득표수 = str_replace_all(득표수, ",", "") %>% as.integer,
              득표율 = str_replace(득표율, "%", "") %>% as.numeric,
              순위 = as.integer(순위))

six_df %>% 
    select(시도명, everything()) %>% 
    DT::datatable()

3 제5,6회 지선 비교

3.1 데이터 병합

Slopegraph를 통해서 제5회 지방선거와 제6회 지방선거 전후변화를 시각적으로 표현하도록 적절한 형태로 데이터를 변환한다. 특히, 간결한 시작적 내용 전달을 위해서 시도명과 득표율을 단순화시킨다.

## 1.2. 데이터 병합: 제 5,6 회 지방선거 -----
five_df <- five_df %>% 
    mutate(선수="제5회")
six_df <- six_df %>% 
    mutate(선수="제6회")

local_df <- bind_rows(five_df, six_df)

names(local_df) <- c("기호", "후보명", "정당", "득표수", "득표율", "순위", "당선여부", "시도명", "선수")

# 2. 데이터 정제 -----
local_df <- local_df %>% 
    filter(정당 !="평화민주당") %>% 
    mutate(신정당 = case_when(str_detect(정당, "새누리") ~ "자한당",
                              str_detect(정당, "한나라") ~ "자한당",
                           str_detect(정당, "새정치민주연합") ~ "더민주",
                           str_detect(정당, "민주당")     ~ "더민주",
                           TRUE ~ "기타")) %>% 
    as_tibble() %>% 
    filter(!str_detect(신정당, "기타")) %>% 
    mutate(시도명 = case_when(
                        str_detect(시도명, "서울특별시장") ~ "서울",
                        str_detect(시도명, "인천광역시장") ~ "인천",
                        str_detect(시도명, "경기도지사") ~ "경기",
                        str_detect(시도명, "강원도지사") ~ "강원",
                        str_detect(시도명, "대전광역시장") ~ "대전",
                        str_detect(시도명, "세종특별자치시장") ~ "세종",
                        str_detect(시도명, "충청남도지사") ~ "충남",
                        str_detect(시도명, "충청북도지사") ~ "충북",
                        str_detect(시도명, "광주광역시장") ~ "광주",
                        str_detect(시도명, "전라남도지사") ~ "전남",
                        str_detect(시도명, "전라북도지사") ~ "전북",
                        str_detect(시도명, "부산광역시장") ~ "부산",
                        str_detect(시도명, "대구광역시장") ~ "대구",
                        str_detect(시도명, "울산광역시장") ~ "울산",
                        str_detect(시도명, "경상남도지사") ~ "경남",
                        str_detect(시도명, "경상북도지사") ~ "경북",
                        str_detect(시도명, "제주특별자치도지사") ~ "제주"))


local_viz_df <- local_df %>% 
    mutate(득표수 = str_replace_all(득표수, ",", "") %>% as.integer,
              득표율 = str_replace(득표율, "%", "") %>% as.numeric,
              순위 = as.integer(순위))

DT::datatable(local_viz_df)

3.2 경사그래프 시각화

Slopegraph를 적용해서 더불어 민주당과 자유한국당으로 나눠서 시도별로 제5회, 제6회 지방선거 사이 변화한 정치지형변화를 살펴본다.

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)
}

3.2.1 더불어 민주당

# 3. 데이터 시각화 -----
## 3.1. 민주당 ----
local_viz_minju_df <- local_viz_df %>% 
    select(시도명, 신정당, 득표율, 선수) %>% 
    mutate(득표율 = round(득표율,1)) %>% 
    spread(선수, 득표율) %>% 
    filter(신정당 == "더민주") %>% 
    as.data.frame() %>% 
    column_to_rownames(var="시도명") %>% 
    select(-신정당)


bumpchart2(local_viz_minju_df, mar=c(6, 12, 6, 12),col=RColorBrewer::brewer.pal(12, "Set3"),
           labels=c(paste(rownames(local_viz_minju_df), local_viz_minju_df[,1], sep="   "),
                    paste(local_viz_minju_df[,2], rownames(local_viz_minju_df), sep="   ")),rank=FALSE,lwd=3,
           top.labels = c("제5회", "제6회"))
title(main="[민주당] 제5,6회 지방선거 비교", line=1.5, col.main="navy blue")

3.2.2 자유한국당

## 3.2. 자한당 ----
local_viz_free_df <- local_viz_df %>% 
    select(시도명, 신정당, 득표율, 선수) %>% 
    mutate(득표율 = round(득표율,1)) %>% 
    spread(선수, 득표율) %>% 
    filter(신정당 == "자한당") %>% 
    as.data.frame() %>% 
    column_to_rownames(var="시도명") %>% 
    select(-신정당)

bumpchart2(local_viz_free_df, mar=c(6, 12, 6, 12),col=RColorBrewer::brewer.pal(12, "Set3"),
           labels=c(paste(rownames(local_viz_free_df), local_viz_free_df[,1], sep="   "),
                    paste(local_viz_free_df[,2], rownames(local_viz_free_df), sep="   ")),rank=FALSE,lwd=3,
           top.labels = c("제5회", "제6회"))
title(main="[자한당] 제5,6회 지방선거 비교", line=1.5, col.main="navy blue")

4 제6,7회 지선 비교

4.1 데이터 병합

제6회 지방선거 결과와 제7회 지방선거 가장 최근 여론조사 전후변화를 시각적으로 표현하도록 적절한 형태로 데이터를 변환한다. 특히, 간결한 시작적 내용 전달을 위해서 시도명과 득표율을 단순화시킨다.

# 1. 데이터 -----
## 1.1. 데이터 가져오기:제6회 지방선거 결과 -----
names(six_df) <- c("기호", "후보명", "정당", "득표수", "득표율", "순위", "당선여부", "시도명")

## 1.2. 데이터 가져오기:제7회 지방선거 여론조사 -----
seven_df  <- read_rds("data/sido_survey_df.rds")
names(seven_df)  <- c("조사 의뢰", "조사 기관", "응답 인원", "더불어민주당", "자유한국당", "바른미래당", 
                      "정의당", "기타·무응답", "참고", "조사일", "시도명", "민주평화당", "무소속")

seven_df <- seven_df %>% 
    mutate(무소속 = str_replace_all(무소속, "%", "") %>% as.numeric,
              민주평화당 = str_replace_all(민주평화당, "%", "") %>% as.numeric) %>% 
    group_by(시도명) %>% 
    arrange(조사일) %>% 
    mutate(조사일_id = row_number()) %>% 
    filter(조사일_id == max(조사일_id)) %>% 
    select(시도명, contains("당"), contains("무소속")) %>% 
    ungroup() %>% 
    gather(정당, 득표율, -시도명) %>% 
    mutate(득표율 = round(as.numeric(득표율),1))

## 1.3. 데이터 병합 -----
six_df <- six_df %>% 
    mutate(선수="제6회") %>% 
    select(선수, 시도명, 정당, 득표율) %>% 
    as_tibble() %>% 
    mutate(득표율 = str_replace_all(득표율, "%", "") %>% as.numeric %>%  round(1)) %>% 
    filter(str_detect(정당, pattern="새누리당|새정치민주연합"))

seven_df <- seven_df %>% 
    mutate(선수="제7회") %>% 
    select(선수, 시도명, 정당, 득표율) %>% 
    filter(str_detect(정당, pattern="더불어민주당|자유한국당"))
    
local_now_df <- bind_rows(six_df, seven_df)

# 2. 데이터 정제 -----
local_now_df <- local_now_df %>% 
    mutate(시도명 = case_when(
        str_detect(시도명, "서울특별시장") ~ "서울",
        str_detect(시도명, "인천광역시장") ~ "인천",
        str_detect(시도명, "경기도지사") ~ "경기",
        str_detect(시도명, "강원도지사") ~ "강원",
        str_detect(시도명, "대전광역시장") ~ "대전",
        str_detect(시도명, "세종특별자치시장") ~ "세종",
        str_detect(시도명, "충청남도지사") ~ "충남",
        str_detect(시도명, "충청북도지사") ~ "충북",
        str_detect(시도명, "광주광역시장") ~ "광주",
        str_detect(시도명, "전라남도지사") ~ "전남",
        str_detect(시도명, "전라북도지사") ~ "전북",
        str_detect(시도명, "부산광역시장") ~ "부산",
        str_detect(시도명, "대구광역시장") ~ "대구",
        str_detect(시도명, "울산광역시장") ~ "울산",
        str_detect(시도명, "경상남도지사") ~ "경남",
        str_detect(시도명, "경상북도지사") ~ "경북",
        str_detect(시도명, "제주특별자치도지사") ~ "제주"))

DT::datatable(local_now_df)

4.1.1 더불어 민주당

# 3. 데이터 시각화 -----
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)
}
## 3.1. 민주당 ----
local_now_minju_df <- local_now_df %>% 
    filter(str_detect(정당, "새정치민주연합|더불어민주당")) %>% 
    select(-정당) %>% 
    spread(선수, 득표율) %>% 
    as.data.frame() %>% 
    column_to_rownames(var="시도명") 

bumpchart2(local_now_minju_df, mar=c(6, 12, 6, 12),col=RColorBrewer::brewer.pal(12, "Set3"),
           labels=c(paste(rownames(local_now_minju_df), local_now_minju_df[,1], sep="   "),
                    paste(local_now_minju_df[,2], rownames(local_now_minju_df), sep="   ")),rank=FALSE,lwd=3,
           top.labels = c("제6회", "제7회"))
title(main="[민주당] 제6,7회 지방선거 비교", line=1.5, col.main="navy blue")

4.1.2 자유한국당

## 3.2. 자유한국당 ----
local_now_free_df <- local_now_df %>% 
    filter(str_detect(정당, "새누리당|자유한국당")) %>% 
    select(-정당) %>% 
    spread(선수, 득표율) %>% 
    as.data.frame() %>% 
    column_to_rownames(var="시도명") 

bumpchart2(local_now_free_df, mar=c(6, 12, 6, 12),col=RColorBrewer::brewer.pal(12, "Set3"),
           labels=c(paste(rownames(local_now_free_df), local_now_free_df[,1], sep="   "),
                    paste(local_now_free_df[,2], rownames(local_now_free_df), sep="   ")),rank=FALSE,lwd=3,
           top.labels = c("제6회", "제7회"))
title(main="[자한당] 제6,7회 지방선거 비교", line=1.5, col.main="navy blue")