지방선거 예측 통계 보고서 (샘플)

저자

Auto-generated

공개

2025년 9월 12일

1 개요

이 문서는 샘플 데이터를 난수로 생성하여 지방선거 득표 예측 절차를 시연합니다.
실제 예측에서는 공식 자료(NEC 등)로 대체하세요.

1.1 재현 가능한 난수 시드

2 데이터 생성

  • 동별 유권자 수(2,000~6,000), 투표율(45%~75%)을 임의 생성
  • 각 동에서 정당별 선호 비중을 임의로 생성 후 정규화
코드
library(tidyverse)
library(gt)

# 후보/정당 정의
cands <- c("김민주" = "민주당", "이국힘" = "국민의힘", "정개혁" = "개혁신당")

n_precinct <- 25
base <- tibble(
= paste0("동", sprintf("%02d", 1:n_precinct)),
  유권자수 = sample(2000:6000, n_precinct, replace = TRUE),
  투표율 = runif(n_precinct, 0.45, 0.75)
)

# 동별 선호 비중(난수) -> 정규화
shares <-
  base |>
  mutate(
    민주_raw = rexp(n_precinct, rate = 1.2),
    국힘_raw = rexp(n_precinct, rate = 1.1),
    개혁_raw = rexp(n_precinct, rate = 1.6)
  ) |>
  pivot_longer(ends_with("_raw"), names_to = "키", values_to = "raw") |>
  mutate(정당 = recode(키,
    "민주_raw" = "민주당",
    "국힘_raw" = "국민의힘",
    "개혁_raw" = "개혁신당"
  )) |>
  group_by(동) |>
  mutate(비중 = raw / sum(raw)) |>
  ungroup() |>
  select(동, 정당, 비중)

# 득표수 계산
votes <-
  shares |>
  left_join(base, by = "동") |>
  mutate(총투표수 = round(유권자수 * 투표율),
         득표수 = round(총투표수 * 비중)) |>
  select(동, 정당, 득표수, 총투표수)

# 전체 요약(정당별 득표율)
summary_party <-
  votes |>
  group_by(정당) |>
  summarise(득표수 = sum(득표수), .groups = "drop") |>
  mutate(득표율 = 득표수 / sum(득표수)) |>
  arrange(desc(득표율))

summary_party
# A tibble: 3 × 3
  정당     득표수 득표율
  <chr>     <dbl>  <dbl>
1 국민의힘  24466  0.394
2 민주당    20143  0.325
3 개혁신당  17426  0.281

3 그래프: 정당별 예측 득표율(난수 시뮬레이션)

코드
library(scales)
library(ggplot2)

ggplot(summary_party, aes(x = reorder(정당, 득표율), y = 득표율)) +
  geom_col() +
  geom_text(aes(label = percent(득표율, accuracy = 0.1)), vjust = -0.5, size = 4) +
  scale_y_continuous(labels = percent, limits = c(0, max(summary_party$득표율) * 1.25)) +
  labs(title = "정당별 예측 득표율 (샘플 난수 데이터)",
       x = NULL, y = "득표율") +
  theme_minimal(base_size = 14)

해석(샘플): 난수 기반 시뮬레이션 결과로 특정 정당이 상대적으로 높은 득표율을 보일 수 있습니다. 이는 **데이터 생성 과정의 가정(선호 비중·투표율 분포)**에 좌우되며, 실제 분석에서는 표본 설계·가중치·검증을 통해 편향을 줄여야 합니다.

4 gt 표: 동별 1·2위 및 격차

아래 표는 동별 1위·2위 정당, 득표율 격차를 요약합니다.

코드
# 동별 득표율 계산
by_precinct <- votes |>
  group_by(동) |>
  mutate(득표율 = 득표수 / sum(득표수)) |>
  arrange(desc(득표율), .by_group = TRUE) |>
  mutate(ranking = row_number()) |>
  ungroup()

top2 <- by_precinct |>
  dplyr::filter(ranking <= 2) |>
  select(동, 정당, 득표율, 총투표수, ranking)

wide <- top2 |>
  pivot_wider(
    names_from = ranking,
    values_from = c(정당, 득표율),
    names_sep = "_"
  ) |>
  mutate(격차 = 득표율_1 - 득표율_2) |>
  arrange(desc(격차))

wide |>
  transmute(
    동,
    `1위 정당` = 정당_1,
    `1위 득표율` = 득표율_1,
    `2위 정당` = 정당_2,
    `2위 득표율` = 득표율_2,
    `격차(%)` = 격차,
    `총투표수` = 총투표수
  ) |>
  gt() |>
  tab_header(
    title = md("**동별 상위 득표 요약 (샘플 난수 데이터)**"),
    subtitle = md("1·2위 정당과 격차, 총투표수")
  ) |>
  fmt_percent(columns = c(`1위 득표율`, `2위 득표율`, `격차(%)`), decimals = 1) |>
  fmt_number(columns = `총투표수`, decimals = 0, use_seps = TRUE) |>
  cols_align(align = "center", columns = everything()) |>
  tab_options(table.font.size = px(14))
동별 상위 득표 요약 (샘플 난수 데이터)
1·2위 정당과 격차, 총투표수
1위 정당 1위 득표율 2위 정당 2위 득표율 격차(%) 총투표수
동10 국민의힘 73.0% 민주당 13.9% 59.1% 3,974
동25 민주당 70.0% 국민의힘 17.2% 52.9% 2,343
동09 국민의힘 71.1% 개혁신당 18.3% 52.8% 2,866
동14 국민의힘 68.6% 개혁신당 18.7% 49.9% 1,605
동01 국민의힘 67.2% 민주당 18.7% 48.6% 2,762
동18 국민의힘 71.8% 민주당 24.6% 47.2% 4,191
동19 개혁신당 68.3% 국민의힘 22.9% 45.4% 1,653
동24 개혁신당 60.2% 국민의힘 22.1% 38.1% 3,166
동03 민주당 58.5% 개혁신당 21.2% 37.3% 1,326
동21 민주당 66.4% 국민의힘 32.2% 34.2% 2,208
동16 국민의힘 58.4% 민주당 26.1% 32.3% 1,292
동11 개혁신당 57.3% 민주당 26.9% 30.4% 4,040
동07 국민의힘 57.1% 민주당 28.7% 28.4% 3,474
동22 개혁신당 58.0% 민주당 31.9% 26.2% 1,802
동17 민주당 55.9% 개혁신당 35.9% 20.0% 1,640
동05 개혁신당 50.2% 국민의힘 32.2% 18.0% 2,001
동04 개혁신당 50.0% 민주당 32.3% 17.8% 2,989
동20 민주당 41.9% 개혁신당 31.7% 10.1% 2,307
동08 민주당 40.8% 국민의힘 31.2% 9.7% 2,682
동23 개혁신당 39.8% 민주당 30.6% 9.1% 1,237
동13 민주당 51.6% 개혁신당 43.3% 8.3% 2,804
동12 개혁신당 43.4% 국민의힘 35.4% 8.1% 2,434
동02 민주당 52.7% 국민의힘 47.0% 5.7% 1,244
동06 국민의힘 47.2% 민주당 43.1% 4.2% 3,677
동15 국민의힘 47.3% 민주당 46.6% 0.7% 2,317

5 메모

  • 본 문서는 샘플 파이프라인으로, 실제 예측에는 *공식 데이터 소스, 가중치 설계, 모델링(예: 다층 로지스틱/베이지안), 검증(홀드아웃/부트스트랩)*을 추가하세요.
  • 그래프와 표는 set.seed()로 재현됩니다(시드 변경 시 결과가 달라짐).