1 유권자 데이터

행정안전부 시도별 연령별 인구현황 파일에서 인구수 데이터를 가져온다. 제20대 대통령선거에 맞춰 0-19세는 제외하고 유권자만 분석의 초점으로 정의한다.

library(tidyverse)
library(readxl)

mois_sido_raw <- read_excel("data/202110_202110_연령별인구현황_월간.xlsx", skip = 3, sheet = "연령별인구현황",
                            range = "B4:B22")
mois_male_raw <- read_excel("data/202110_202110_연령별인구현황_월간.xlsx", skip = 3, sheet = "연령별인구현황",
                       range = "R4:AB22")
mois_female_raw <- read_excel("data/202110_202110_연령별인구현황_월간.xlsx", skip = 3, sheet = "연령별인구현황",
                       range = "AE4:AO22")

mois_male_tbl <- bind_cols(mois_sido_raw, mois_male_raw) %>% 
  pivot_longer(cols = -행정기관, names_to = "연령", values_to = "인구수") %>% 
  mutate(성별 = "남자") %>% 
  select(행정기관, 성별, 연령, 인구수)

mois_female_tbl <- bind_cols(mois_sido_raw, mois_female_raw) %>% 
  pivot_longer(cols = -행정기관, names_to = "연령", values_to = "인구수") %>% 
  mutate(성별 = "여자") %>% 
  select(행정기관, 성별, 연령, 인구수)
  
mois_raw <- bind_rows(mois_male_tbl, mois_female_tbl)  

mois_tbl <- mois_raw  %>% 
  mutate(인구수 = parse_number(인구수)) %>% 
  filter(!str_detect(행정기관, "전국")) %>% 
  mutate(연령 = factor(연령, levels = c("0~9세", "10~19세", "20~29세", "30~39세", "40~49세", 
"50~59세", "60~69세", "70~79세", "80~89세", "90~99세", "100세 이상"))) %>% 
  mutate(연령대 = case_when(연령 %in% c("0~9세", "10~19세") ~ "00~19",
                           연령 %in% c("20~29세", "30~39세") ~ "20~30",
                           연령 %in% c("40~49세", "50~59세") ~ "40~50",
                           TRUE ~ "60+")) %>% 
  mutate(행정기관 = factor(행정기관, levels = c("경기도", "서울특별시", "부산광역시", "경상남도", "인천광역시", 
                                        "경상북도", "대구광역시", "충청남도", "전라남도", "전라북도", 
                                        "충청북도", "강원도", "대전광역시", "광주광역시", "울산광역시", 
                                        "제주특별자치도", "세종특별자치시"))) %>% 
  filter(연령대 != "00~19")


# library(testthat)
# 
# test_that("single number", {
#   expect_equal(
#     object = mois_tbl %>% summarise(sum(인구수)) %>% pull,
#     expected =  51662290 # 51,662,290
#   )
# })

mois_tbl %>% 
  write_csv("data/mois_tbl.csv")

2

2.1 남녀

library(gt)
library(gtExtras)

gender_gt <- mois_tbl %>% 
  group_by(성별) %>% 
  summarise(인구수 = sum(인구수)) %>% 
  mutate(비율 = 인구수 / sum(인구수)) %>% 
  mutate(인구수비율 = glue::glue("{scales::comma(인구수)}<br>{scales::percent(round(비율, 2))}")) %>% 
  select(성별, 인구수, 비율) %>% 
  arrange(desc(인구수)) %>% 
  mutate(누적비율 = cumsum(비율)) %>% 
  gt() %>% 
   # fmt_markdown(columns = 인구수비율 )  %>% 
    gt_theme_538() %>%
    tab_header(
      title = md("**&#x2600; 제20대 대통령 선거 &#x2600;**"),
      subtitle = md("*시도별 인구수*")
    ) %>%
    opt_align_table_header(align = "center") %>%
    tab_options(
      table.width = "600px",
      heading.background.color = "#1E61B0", # R logo 파란색
      heading.title.font.size = "20px",
      column_labels.background.color = "#F7F7F7", # R logo 회색
      column_labels.font.weight = "bold",
      stub.background.color = "#ffffff",
      stub.font.weight = "bold"
    ) %>% 
    tab_source_note(
      source_note = md("데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월")
    ) %>% 
    cols_align(
      align = "center",
      columns = c(성별)
    ) %>% 
    fmt_number(
      columns = 인구수,
      decimals = 0
    ) %>% 
    fmt_percent(
      columns = c(비율, 누적비율),
      decimals = 1
    )  

gender_gt
☀ 제20대 대통령 선거 ☀
시도별 인구수
성별 인구수 비율 누적비율
여자 21,770,334 50.5% 50.5%
남자 21,377,682 49.5% 100.0%
데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월

2.2 연령대

age_gt <- mois_tbl %>% 
  group_by(연령대) %>% 
  summarise(인구수 = sum(인구수)) %>% 
  mutate(비율 = 인구수 / sum(인구수)) %>% 
  mutate(인구수비율 = glue::glue("{scales::comma(인구수)}<br>{scales::percent(round(비율, 2))}")) %>% 
  select(연령대, 인구수비율) %>% 
  gt() %>% 
   fmt_markdown(columns = 인구수비율 )  %>% 
    gt_theme_538() %>%
    tab_header(
      title = md("**&#x2600; 제20대 대통령 선거 &#x2600;**"),
      subtitle = md("*연령별 인구수*")
    ) %>%
    opt_align_table_header(align = "center") %>%
    tab_options(
      table.width = "600px",
      heading.background.color = "#1E61B0", # R logo 파란색
      heading.title.font.size = "20px",
      column_labels.background.color = "#F7F7F7", # R logo 회색
      column_labels.font.weight = "bold",
      stub.background.color = "#ffffff",
      stub.font.weight = "bold"
    ) %>% 
    tab_source_note(
      source_note = md("데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월")
    )

age_gt
☀ 제20대 대통령 선거 ☀
연령별 인구수
연령대 인구수비율
20~30

13,431,836
31.0%

40~50

16,792,282
39.0%

60+

12,923,898
30.0%

데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월

2.3 시도

sido_gt <- mois_tbl %>% 
  group_by(행정기관) %>% 
  summarise(인구수 = sum(인구수)) %>% 
  mutate(비율 = 인구수 / sum(인구수)) %>% 
  mutate(인구수비율 = glue::glue("{scales::comma(인구수)}<br>{scales::percent(round(비율, 2))}")) %>% 
  select(행정기관, 인구수, 비율) %>% 
  arrange(desc(인구수)) %>% 
  mutate(누적비율 = cumsum(비율)) %>% 
  gt() %>% 
   # fmt_markdown(columns = 인구수비율 )  %>% 
    gt_theme_538() %>%
    tab_header(
      title = md("**&#x2600; 제20대 대통령 선거 &#x2600;**"),
      subtitle = md("*시도별 인구수*")
    ) %>%
    opt_align_table_header(align = "center") %>%
    tab_options(
      table.width = "600px",
      heading.background.color = "#1E61B0", # R logo 파란색
      heading.title.font.size = "20px",
      column_labels.background.color = "#F7F7F7", # R logo 회색
      column_labels.font.weight = "bold",
      stub.background.color = "#ffffff",
      stub.font.weight = "bold"
    ) %>% 
    tab_source_note(
      source_note = md("데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월")
    ) %>% 
    cols_align(
      align = "center",
      columns = c(행정기관)
    ) %>% 
    fmt_number(
      columns = 인구수,
      decimals = 0
    ) %>% 
    fmt_percent(
      columns = c(비율, 누적비율),
      decimals = 1
    )  

sido_gt
☀ 제20대 대통령 선거 ☀
시도별 인구수
행정기관 인구수 비율 누적비율
경기도 11,118,744 25.8% 25.8%
서울특별시 8,182,185 19.0% 44.7%
부산광역시 2,867,948 6.6% 51.4%
경상남도 2,744,722 6.4% 57.7%
인천광역시 2,454,588 5.7% 63.4%
경상북도 2,225,275 5.2% 68.6%
대구광역시 2,001,956 4.6% 73.2%
충청남도 1,753,637 4.1% 77.3%
전라남도 1,545,964 3.6% 80.9%
전라북도 1,497,999 3.5% 84.3%
충청북도 1,332,287 3.1% 87.4%
강원도 1,301,659 3.0% 90.4%
대전광역시 1,202,943 2.8% 93.2%
광주광역시 1,174,850 2.7% 96.0%
울산광역시 919,453 2.1% 98.1%
제주특별자치도 547,711 1.3% 99.4%
세종특별자치시 276,095 0.6% 100.0%
데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월

3 교차표

3.1 시도 X 남녀

sido_gender_gt <- mois_tbl %>% 
  group_by(행정기관, 성별) %>% 
  summarise(인구수 = sum(인구수)) %>% 
  mutate(비율 = 인구수 / sum(인구수)) %>%
  mutate(인구수비율 = glue::glue("{scales::comma(인구수)}<br>{scales::percent(round(비율, 2))}")) %>% 
  select(-인구수, -비율) %>% 
  pivot_wider(names_from = 성별, values_from = 인구수비율) %>% 
  ungroup() %>% 
  gt() %>% 
   fmt_markdown(columns = c(남자, 여자) )  %>%
    gt_theme_espn() %>%
    tab_header(
      title = md("**&#x2600; 제20대 대통령 선거 &#x2600;**"),
      subtitle = md("*시도별 남녀 인구수*")
    ) %>%
    opt_align_table_header(align = "center") %>%
    tab_options(
      table.width = "600px",
      heading.background.color = "#1E61B0", # R logo 파란색
      heading.title.font.size = "20px",
      column_labels.background.color = "#F7F7F7", # R logo 회색
      column_labels.font.weight = "bold",
      stub.background.color = "#ffffff",
      stub.font.weight = "bold"
    ) %>% 
    tab_source_note(
      source_note = md("데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월")
    ) %>% 
    cols_align(
      align = "center",
      columns = c(행정기관)
    )  

sido_gender_gt
☀ 제20대 대통령 선거 ☀
시도별 남녀 인구수
행정기관 남자 여자
경기도

5,573,522
50%

5,545,222
50%

서울특별시

3,938,078
48.0%

4,244,107
52.0%

부산광역시

1,391,261
49.0%

1,476,687
51.0%

경상남도

1,373,102
50%

1,371,620
50%

인천광역시

1,222,665
50%

1,231,923
50%

경상북도

1,114,591
50%

1,110,684
50%

대구광역시

977,319
49.0%

1,024,637
51.0%

충청남도

894,622
51.0%

859,015
49.0%

전라남도

774,608
50%

771,356
50%

전라북도

739,826
49.0%

758,173
51.0%

충청북도

673,645
51.0%

658,642
49.0%

강원도

652,214
50%

649,445
50%

대전광역시

596,109
50%

606,834
50%

광주광역시

575,511
49.0%

599,339
51.0%

울산광역시

471,105
51.0%

448,348
49.0%

제주특별자치도

272,346
50%

275,365
50%

세종특별자치시

137,158
50%

138,937
50%

데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월

3.2 시도 X 연령대

sido_age_gt <- mois_tbl %>% 
  group_by(행정기관, 연령대) %>% 
  summarise(인구수 = sum(인구수)) %>% 
  mutate(비율 = 인구수 / sum(인구수)) %>%
  mutate(인구수비율 = glue::glue("{scales::comma(인구수)}<br>{scales::percent(round(비율, 2))}")) %>% 
  select(-인구수, -비율) %>% 
  pivot_wider(names_from = 연령대, values_from = 인구수비율) %>% 
  ungroup() %>% 
  gt() %>% 
   fmt_markdown(columns = c(`20~30`, `40~50`, `60+`) )  %>%
    gt_theme_espn() %>%
    tab_header(
      title = md("**&#x2600; 제20대 대통령 선거 &#x2600;**"),
      subtitle = md("*시도별 연령별 인구수*")
    ) %>%
    opt_align_table_header(align = "center") %>%
    tab_options(
      table.width = "600px",
      heading.background.color = "#1E61B0", # R logo 파란색
      heading.title.font.size = "20px",
      column_labels.background.color = "#F7F7F7", # R logo 회색
      column_labels.font.weight = "bold",
      stub.background.color = "#ffffff",
      stub.font.weight = "bold"
    ) %>% 
    tab_source_note(
      source_note = md("데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월")
    ) %>% 
    cols_align(
      align = "center",
      columns = c(행정기관)
    )  

sido_age_gt
☀ 제20대 대통령 선거 ☀
시도별 연령별 인구수
행정기관 20~30 40~50 60+
경기도

3,704,923
33.0%

4,558,558
41.0%

2,855,263
26.0%

서울특별시

2,873,770
35.0%

2,993,544
37.0%

2,314,871
28.0%

부산광역시

826,544
29.0%

1,058,617
37.0%

982,787
34.0%

경상남도

749,248
27.0%

1,106,448
40.0%

889,026
32.0%

인천광역시

794,083
32.0%

990,423
40.0%

670,082
27.0%

경상북도

558,636
25.0%

831,492
37.0%

835,147
38.0%

대구광역시

598,427
30%

793,145
40%

610,384
30%

충청남도

499,506
28.0%

664,931
38.0%

589,200
34.0%

전라남도

373,979
24.0%

564,851
37.0%

607,134
39.0%

전라북도

391,042
26%

559,053
37%

547,904
37%

충청북도

386,590
29.0%

507,474
38.0%

438,223
33.0%

강원도

338,742
26%

483,875
37%

479,042
37%

대전광역시

400,640
33.0%

471,593
39.0%

330,710
27.0%

광주광역시

389,570
33.0%

471,375
40.0%

313,905
27.0%

울산광역시

284,257
31.0%

390,936
43.0%

244,260
27.0%

제주특별자치도

162,441
30.0%

225,970
41.0%

159,300
29.0%

세종특별자치시

99,438
36.0%

119,997
43.0%

56,660
21.0%

데이터: 행정안전부 시도별 연령별 인구현황, 2021년 10월
# sido_age_gt %>% 
#   gtsave(filename = "sido_age_gt.png", path = "fig")

4 시각화

4.1 시도별 유권자

extrafont::loadfonts()

mois_tbl %>% 
  group_by(행정기관) %>% 
  summarise(인구수 = sum(인구수)) %>% 
  ungroup() %>% 
  ggplot(aes(x = fct_reorder(행정기관, 인구수), y = 인구수)) +
    geom_col(width =0.1, color = "#2E7DBF") +
    geom_point(size = 3, color = "#0F1F48") +
    coord_flip() +
    scale_y_continuous(labels = scales::comma, limits = c(0, 15000000)) +
    labs(x = "", y = "",
         title = "제20대 대통령 선거",
         subtitle = "시도별 유권자") +
    theme_light(base_family = "NanumBarunpen")

4.2 시도별 X 연령별 유권자

mois_tbl %>% 
  group_by(행정기관, 연령대) %>% 
  summarise(인구수 = sum(인구수)) %>% 
  ungroup() %>% 
  mutate(연령대 = factor(연령대, levels = c("20~30", "40~50", "60+")) %>% fct_rev) %>% 
  ggplot(aes(x = fct_reorder(행정기관, 인구수), y = 인구수, fill = 연령대)) +
    geom_col(width =0.3, position = "fill") +
    coord_flip() +
    scale_y_continuous(labels = scales::percent) +
    labs(x = "", y = "",
         title = "제20대 대통령 선거",
         subtitle = "시도별 유권자") +
    theme_light(base_family = "NanumBarunpen") +
    theme(legend.position = "top") +
    guides(fill = guide_legend(reverse = TRUE))

 

데이터 과학자 이광춘 저작

kwangchun.lee.7@gmail.com