---
title: "지도제작 대회"
subtitle: "재보궐: 강서구 위치"
description: |
강서구청장 선거 위치
author:
- name: 이광춘
url: https://www.linkedin.com/in/kwangchunlee/
affiliation: 한국 R 사용자회
affiliation-url: https://github.com/bit2r
title-block-banner: true
format:
html:
theme: flatly
code-fold: true
code-overflow: wrap
toc: true
toc-depth: 3
toc-title: 목차
number-sections: true
highlight-style: github
self-contained: false
default-image-extension: jpg
filters:
- lightbox
lightbox: auto
link-citations: true
knitr:
opts_chunk:
eval: true
message: false
warning: false
collapse: true
comment: "#>"
R.options:
knitr.graphics.auto_pdf: true
editor_options:
chunk_output_type: console
---
# 데이터셋
## GRDP
[서울시 자치구별 지역내총생산(2015년 기준) 통계](https://data.seoul.go.kr/dataList/11057/S/2/datasetView.do)
```{r}
library(tidyverse)
library(readxl)
grdp_raw <- read_excel("data/자치구별+지역내총생산(2015년+기준)_20231011134915.xlsx", skip = 0)
grdp_tbl <- grdp_raw |>
janitor::clean_names(ascii = FALSE) |>
select(자치구 = 자치구별_2, contains("x2020")) |>
janitor::row_to_names(row_number = 1) |>
set_names(c("자치구", "GRDP", "GRDP_pcnt", "GRDP_2", "GRDP_pcnt2")) |>
select(자치구, GRDP) |>
mutate( GRDP = parse_number(GRDP)) |>
filter(자치구 != "소계") |> mutate(GRDP = GRDP / 1000000 )
grdp_tbl
```
## 인구수
```{r}
pop_raw <- read_csv("data/202308_202308_주민등록인구및세대현황_월간.csv",
locale = locale('ko',encoding='euc-kr'))
seoul_pop <- pop_raw |>
select(구명 = 행정구역, 인구수 = `2023년08월_총인구수`) |>
mutate(구명 = str_extract(구명, "\\w+구")) |>
drop_na() |>
mutate(강북강남 = ifelse(구명 %in% c("은평구", "서대문구", "마포구", "종로구", "중구", "용산구", "동대문구", "성동구", "성북구", "중랑구", "광진구", "강북구", "도봉구", "노원구"), "강북", "강남")) |>
mutate(인구수 = 인구수 / 10000)
seoul_pop
```
## 인구 GRDP
```{r}
pop_grdp <- seoul_pop |>
left_join(grdp_tbl, by = c("구명" = "자치구"))
pop_grdp
```
# 분석
## 인구수와 GRDP
```{r}
library(ggrepel)
pop_grdp |>
ggplot(aes(x = 인구수, y = GRDP)) +
geom_point() +
theme_minimal(base_family="MaruBrui") +
theme(legend.position = "none") +
labs(title = "서울시 자치구 인구수와 지역내총생산",
x = "인구수(만명)",
y = "지역내총생산(조원)") +
scale_x_continuous(labels = scales::unit_format(unit = " 만명", sep = "")) +
scale_y_continuous(labels = scales::unit_format(unit = " 조원", sep = "")) +
geom_text_repel(aes(label = 구명),
size = 4.5, min.segment.length = 0, force = 0.5, max.overlaps=Inf,
color = "black")
```
# 선거결과
## 2021 재보궐 선거
```{r}
by_election_2021_vote <- krvote::by_election_2021 |>
filter(시도명 == "서울특별시",
구시군 == "강서구") |>
filter(후보 != "후보_계",
읍면동명 != "합계",
구분 != "계") |>
select(읍면동명, 구분, 후보, 득표수) |>
mutate(정당 = case_when(
str_detect(후보, "국민의힘") ~ "국민의힘",
str_detect(후보, "더불어민주당") ~ "민주당",
TRUE ~ "기타")) |>
group_by(정당) |>
summarise(득표수 = sum(득표수)) |>
pivot_wider(names_from = 정당, values_from = 득표수) |>
mutate(선거 = "2021 서울시장")
by_election_2021_cast <- krvote::by_election_2021 |>
filter(시도명 == "서울특별시",
구시군 == "강서구") |>
select(구시군, 읍면동명, 선거인수, 투표수) |>
filter(읍면동명 == "합계") |>
distinct_all() |>
mutate(투표율 = 투표수 / 선거인수) |>
select(선거인수, 투표수)
by_election_2021 <- bind_cols(by_election_2021_cast, by_election_2021_vote) |>
select(선거, everything())
by_election_2021
```
## 2022 대통령선거
```{r}
# 투표율
president_2022_cast <- krvote::election_20220309$득표율 |>
filter(시도명 == "서울특별시",
구시군명 == "강서구") |>
summarise(선거인수 = sum(선거인수),
투표수 = sum(투표수))
president_2022_vote <- krvote::election_20220309$득표율 |>
filter(시도명 == "서울특별시",
구시군명 == "강서구") |>
pivot_longer(이재명:김민찬, names_to = "후보", values_to = "득표수") |>
mutate(정당 = case_when(
str_detect(후보, "윤석열") ~ "국민의힘",
str_detect(후보, "이재명") ~ "민주당",
TRUE ~ "기타")) |>
group_by(정당) |>
summarise(득표수 = sum(득표수)) |>
pivot_wider(names_from = 정당, values_from = 득표수) |>
mutate(선거 = "2022 대통령선거")
president_2022 <- bind_cols(president_2022_cast, president_2022_vote) |>
select(선거, everything())
president_2022
```
## 2020 국회의원선거
```{r}
chongsun_2020 <- krvote::general_2020 |>
filter(시도 == "서울",
str_detect(선거구, "^강서")) |>
unnest(data) |>
mutate(구분 = ifelse(stringi::stri_enc_isutf8(구분), 구분,
iconv(구분, from = "euc-kr", to ="utf-8") )) |>
filter(!구분 %in% c("계", "기권수", "무표투표수")) |>
mutate(후보 = case_when(
str_detect(구분, "더불어민주당") ~ "민주당",
str_detect(구분, "미래통합당") ~ "국민의힘",
str_detect(구분, "선거인수") ~ "선거인수",
str_detect(구분, "투표수") ~ "투표수",
TRUE ~ "기타")) |>
group_by(후보) |>
summarise(사람수 = sum(사람수)) |>
pivot_wider(names_from = 후보, values_from = 사람수) |>
mutate(선거 = "2020 국회의원") |>
select(선거, 선거인수, 투표수, 국민의힘, 기타, 민주당)
chongsun_2020
```
## 2023년 보궐선거
```{r}
library(readxl)
by_election_2023_raw <- read_excel("data/[2023년_하반기_재·보궐선거]_개표진행상황.xlsx",
skip = 5)
by_election_2023 <- by_election_2023_raw |>
set_names(c("선거구명", "구시군명", "선거인수", "투표수", "더불어민주당\n진교훈",
"국민의힘\n김태우", "정의당\n권수정", "x8", "진보당\n권혜인",
"녹색당\n김유리", "자유통일당\n고영일", "계",
"무효투표수", "기권수", "개표율")) |>
filter(!is.na(구시군명)) |>
pivot_longer(cols = contains("\n"), names_to = "후보",
values_to = "사람수") |>
select(구시군명, 선거인수, 투표수, 후보, 사람수) |>
mutate(사람수 = parse_number(사람수)) |>
mutate(후보 = case_when(
str_detect(후보, "더불어민주당") ~ "민주당",
str_detect(후보, "국민의힘") ~ "국민의힘",
TRUE ~ "기타")) |>
group_by(선거인수, 투표수, 후보) |>
summarise(사람수 = sum(사람수)) |>
ungroup() |>
pivot_wider(names_from = 후보, values_from = 사람수) |>
mutate(선거 = "2023 재보궐") |>
select(선거, 선거인수, 투표수, 국민의힘, 기타, 민주당) |>
mutate(선거인수 = parse_number(선거인수),
투표수 = parse_number(투표수))
```
## 2022년 지방선거
```{r}
local_2022_vote <- krvote::local_sido_20220601 |>
filter(선거구명 == "서울특별시") |>
unnest(data) |>
filter(구시군명 == "강서구") |>
pivot_longer(더불어민주당_송영길:무소속_김광종) |>
mutate(정당 = case_when(
str_detect(name, "더불어민주당") ~ "민주당",
str_detect(name, "국민의힘") ~ "국민의힘",
TRUE ~ "기타")) |>
group_by(정당) |>
summarise(사람수 = sum(value)) |>
pivot_wider(names_from = 정당, values_from = 사람수) |>
mutate(선거 = "2022 서울시장")
local_2022_cast <- krvote::local_sido_20220601 |>
filter(선거구명 == "서울특별시") |>
unnest(data) |>
filter(구시군명 == "강서구") |>
summarise(선거인수 = sum(선거인수), 투표수 = sum(투표수))
local_2023 <- bind_cols(local_2022_cast, local_2022_vote) |>
select(선거, everything())
local_2023
```
## 데이터 결합
```{r}
gs_tbl <- bind_rows(by_election_2021, president_2022, chongsun_2020, by_election_2023, local_2023) |>
mutate(선거 = factor(선거, levels = c("2020 국회의원", "2021 서울시장",
"2022 대통령선거", "2022 서울시장", "2023 재보궐"))) |>
arrange(선거)
gs_tbl
```
# 시각화
## 투표율
```{r}
gs_tbl |>
select(선거, 선거인수, 투표수) |>
pivot_longer(-선거) |>
mutate(name = factor(name, levels = c("투표수", "선거인수"))) |>
ggplot(aes(x = 선거, y = value, fill = name,
width = ifelse(name == "투표수", 0.2, 0.4))) +
geom_col(stat = "identity") +
theme_bw(base_family = "NanumGothic") +
theme(legend.position = "top",
axis.text.y = element_text(size = rel(1.5), colour = "gray35", family = "NanumBarunpen", face="bold"),
axis.text.x = element_text(size = rel(1.0), colour = "black", family = "NanumBarunpen", face="bold",
angle = 0, vjust = 0.5, hjust=0.5),
strip.background=element_rect(fill="gray95"),
plot.title=element_text(size=18, face="bold", family = "NanumBarunpen"),
plot.subtitle=element_text(face="bold", size=13, colour="grey10", family = "NanumBarunpen")) +
labs(x = "",
y = "",
title = "강서구 선거인수와 투표수",
caption = "자료 출처: 중앙선거관리위원회 선거통계 시스템",
fill = "") +
scale_y_continuous(labels = scales::comma, limits = c(0, 530000)) +
scale_fill_manual(values = c("darkblue", "gray77")) +
coord_flip() +
geom_text(aes(label = scales::comma(value)),
hjust = -0.1, size = 2.5,
family = "NanumBarunpen")
```
## 득표수
```{r}
gs_tbl |>
select(-선거인수, -투표수) |>
pivot_longer(-선거) |>
filter(name != "기타") |>
mutate(value = ifelse(name == "민주당", value, -value)) |>
ggplot(aes(x = 선거, y = value, fill = name)) +
geom_bar(stat = "identity", width = 0.5) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray10") + # 0을 기준으로 빨간색 점선 추가
labs(
title = "강서구 주요선거 득표수",
x = "",
y = "득표수",
fill = "정당"
) +
scale_y_continuous(labels = function(x) scales::comma(abs(x))) +
coord_flip() +
scale_fill_manual(values = c("red", "darkblue")) +
theme_bw(base_family = "NanumGothic") +
theme(legend.position = "top",
axis.text.y = element_text(size = rel(1.5), colour = "gray35", family = "NanumBarunpen", face="bold"),
axis.text.x = element_text(size = rel(1.0), colour = "black", family = "NanumBarunpen", face="bold",
angle = 0, vjust = 0.5, hjust=0.5),
strip.background=element_rect(fill="gray95"),
plot.title=element_text(size=18, face="bold", family = "NanumBarunpen"),
plot.subtitle=element_text(face="bold", size=13, colour="grey10", family = "NanumBarunpen"))
```
## 득표차
```{r}
gs_tbl |>
mutate(표차이 = 민주당 - 국민의힘) |>
select(-선거인수, -투표수, - 기타, -국민의힘, -민주당) |>
ggplot(aes(x = 선거, y = 표차이, fill = ifelse(표차이>0, "민주당", "국민의힘"))) +
geom_bar(stat = "identity", width = 0.5) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray10") + # 0을 기준으로 빨간색 점선 추가
labs(
title = "강서구 주요선거 득표차",
x = "",
y = "양당 득표차이",
fill = "정당"
) +
scale_y_continuous(labels = function(x) scales::comma(abs(x)),
limits = c(-70000, 70000)) +
coord_flip() +
scale_fill_manual(values = c("red", "darkblue")) +
theme_bw(base_family = "NanumGothic") +
theme(legend.position = "top",
axis.text.y = element_text(size = rel(1.5), colour = "gray35", family = "NanumBarunpen", face="bold"),
axis.text.x = element_text(size = rel(1.0), colour = "black", family = "NanumBarunpen", face="bold",
angle = 0, vjust = 0.5, hjust=0.5),
strip.background=element_rect(fill="gray95"),
plot.title=element_text(size=18, face="bold", family = "NanumBarunpen"),
plot.subtitle=element_text(face="bold", size=13, colour="grey10", family = "NanumBarunpen"))
```