누적막대그래프 그리기
개요
이번 글에서는 누적막대그래프를 그리는 방법과, 데이터 시각화 과정에서 흔히 발생하는 문제를 해결하는 팁을 소개합니다. 2023년 시도별 시, 군, 구의 주민등록인구 비중 데이터를 활용합니다. 이 데이터를 활용해 누적막대그래프를 그리고, 텍스트 겹침 문제를 해결하는 방법과 x축 텍스트를 축약하는 방법에 대해서도 알려드리겠습니다.
데이터 준비하기
비율 계산 글에서 만든 2023년 시도별 시, 군, 구가 차지하는 주민등록인구 비중을 활용해서 누적막대그래프를 그려보겠습니다. 데이터를 정리하고, 각 시도와 구분(시/군/구)별 주민등록인구 비율을 계산합니다.
# 패키지 로드
library(tidyverse)
library(readxl)
library(ggplot2)
library(showtext)
library(scales)
# 글꼴 설정
font_add("kopub", "C:/Users/.../AppData/Local/Microsoft/Windows/Fonts/KoPub Dotum Medium.ttf")
showtext_auto()
showtext_opts(dpi=300)
theme.size = 12
text.size = theme.size / .pt
# 데이터 불러오기
data <- read_xlsx("데이터/주민등록인구_시도_시군구_2023.xlsx", skip=1)
# 데이터 정리하기
data2 <- data %>%
rename(시도 = `행정구역별(1)`,
시군구 = `행정구역별(2)`,
주민등록인구수 = `계 (명)`) %>%
filter(시도 != "전국",
(시도 == "세종특별자치시")|(시군구 != "소계")) %>%
mutate(시군구 = case_when(시도 == "세종특별자치시" ~ "세종특별자치시",
T ~ 시군구),
시도 = factor(시도, levels = unique(data$`행정구역별(1)`)[-1]),
구분 = case_when(grepl("시$", 시군구) ~ "시",
grepl("군$", 시군구) ~ "군",
grepl("구$", 시군구) ~ "구"),
구분 = factor(구분, levels = c("시", "군", "구")))
# 비율 계산하기
sum <- data2 %>%
group_by(시도, 구분) %>%
summarise(주민등록인구수 = sum(주민등록인구수, na.rm = TRUE)) %>%
group_by(시도) %>%
mutate(비율 = 주민등록인구수 / sum(주민등록인구수, na.rm = TRUE))
head(sum)
## # A tibble: 6 × 4
## # Groups: 시도 [4]
## 시도 구분 주민등록인구수 비율
## <fct> <fct> <dbl> <dbl>
## 1 서울특별시 구 9386034 1
## 2 부산광역시 군 178729 0.0543
## 3 부산광역시 구 3114633 0.946
## 4 대구광역시 군 285072 0.120
## 5 대구광역시 구 2089888 0.880
## 6 인천광역시 군 89382 0.0298
누적막대그래프 그리기
이제 정리된 데이터를 사용해 누적막대그래프를 그립니다. geom_col 함수에서 position = "stack"으로 설정해 누적막대그래프를 그릴 수 있습니다. geom_text 함수로 비율을 표기해 줍니다. 이때, 텍스트가 막대 중간에 위치하도록 하려면, position = position_stack(vjust = 0.5)로 설정해야 합니다.
ggplot(data = sum,
aes(x = 시도, y = 비율, group = 시도, fill = 구분,
label = percent(비율, accuracy = .1, suffix = ""))) +
geom_col(position = "stack", color = "white", linewidth = 0.4,
width = 0.6) +
geom_text(family = "kopub",
position = position_stack(vjust = 0.5),
size = text.size) +
scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
scale_fill_brewer(palette = "Pastel2") +
theme_minimal(base_size = theme.size, base_family = "kopub") +
theme(
axis.line.y = element_blank(),
axis.line.x = element_line(color = "#959595", linewidth = 1.5),
axis.title = element_blank(),
axis.text.y = element_blank(),
legend.title = element_blank(),
legend.position = "bottom",
legend.box.background = element_rect(color = "#959595", linewidth = 1),
legend.key.size = unit(theme.size, "pt"),
panel.grid = element_blank()
)

x축 텍스트 축약하고 텍스트 겹침 문제 해결하기
x축의 시도명이 길어서 겹치고, x축과 텍스트가 겹치는 문제가 발생했습니다. 시도명을 축약하고 geom_text_repel 함수을 사용해 텍스트 중첩 문제를 해결하겠습니다. case_when으로 조건을 걸어 지역명을 축약하면 character로 값이 반환되기 때문에, factor로 다시 순서를 지정해줘야 합니다.
# 패키지 로드
library(ggrepel)
# x축 시도명 축약
sum2 <- sum %>%
mutate(시도 = case_when(grepl("(특별시)|(광역시)|(자치시)|(특별자치도)$", 시도) ~ substr(시도, 1, 2),
시도 == "경기도" ~ "경기",
T ~ paste0(substr(시도, 1, 1), substr(시도, 3, 3))
),
시도 = factor(시도, levels = c("서울", "부산", "대구", "인천", "광주", "대전", "울산",
"세종", "경기", "강원", "충북", "충남", "전북", "전남", "경북", "경남", "제주"))
)
# 누적막대그래프 그리기
ggplot(data = sum2,
aes(x = 시도, y = 비율, group = 시도, fill = 구분,
label = percent(비율, accuracy = .1, suffix = ""))) +
geom_col(position = "stack", color = "white", linewidth = 0.4,
width = 0.6) +
geom_text_repel(family = "kopub",
position = position_stack(vjust = 0.5),
size = text.size,
force = 0.001,
force_pull = 1000) +
scale_y_continuous(labels = percent_format(accuracy = 1),
expand = expansion(mult = c(0, 0.1))) +
scale_fill_brewer(palette = "Pastel2") +
theme_minimal(base_size = theme.size, base_family = "kopub") +
theme(
axis.line.y = element_blank(),
axis.line.x = element_line(color = "#959595", linewidth = 2),
axis.title = element_blank(),
axis.text.y = element_blank(),
legend.title = element_blank(),
legend.position = "bottom",
legend.box.background = element_rect(color = "#959595", linewidth = 1),
legend.key.size = unit(theme.size, "pt"),
panel.grid = element_blank()
)
