시간에 따른 변화 시각화하기
개요
서울 자치구별 주민등록인구수 변화를 시각화하고 싶다고 가정해봅시다. 1995년과 2023년 데이터를 사용해 각 연도의 지도를 그리면, 자치구별로 인구수가 많은 지역을 색상으로 쉽게 확인할 수 있을 겁니다. 하지만 이렇게 하면 1995년 지도와 2023년 지도의 색상 범례가 서로 다르게 나타날 가능성이 높습니다. 이는 한 연도 안에서 지역 간 인구 비교는 가능하지만, 연도 간 인구 변화 추이를 직관적으로 파악하기 어렵게 만듭니다.
이 글에서는 두 연도의 지도를 하나의 공통된 색상 범례로 그려, 연도 간 변화를 쉽게 비교할 수 있는 방법을 소개합니다. 이를 통해 변화의 흐름을 더욱 명확하게 전달할 수 있을 것입니다.
데이터 준비
먼저, 서울시 자치구의 경계 데이터를 st_read로 불러오고, 1995년과 2023년 주민등록인구 데이터를 read_xlsx로 읽어옵니다.
서울시 자치구 경계 데이터는 V-World 디지털트윈국토의 행정구역시군구_경계에서 다운받았습니다. 주민등록인구 데이터는 통계청의 주민등록인구(시도/시/군/구)에서 다운받았습니다.
# 패키지 로드
library(tidyverse)
library(sf)
library(readxl)
# 서울시 자치구 shp 파일
sgg <- st_read("데이터/수도권 지도/행정구역 경계/LARD_ADM_SECT_SGG_11_202405.shp")
## Reading layer `LARD_ADM_SECT_SGG_11_202405' from data source
## `C:\...\데이터\수도권 지도\행정구역 경계\LARD_ADM_SECT_SGG_11_202405.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 25 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 179191.4 ymin: 536562.8 xmax: 216242.3 ymax: 566863.5
## Projected CRS: Korea_2000_Korea_Central_Belt_2010
# 서울시 자치구 주민등록인구수
pop <- read_xlsx("데이터/주민등록인구_서울.xlsx")
head(pop)
## # A tibble: 6 × 3
## 행정구역별 시점 `계 (명)`
## <chr> <chr> <dbl>
## 1 서울특별시 1995 10550871
## 2 서울특별시 2023 9386034
## 3 종로구 1995 203086
## 4 종로구 2023 139417
## 5 중구 1995 143138
## 6 중구 2023 121312
데이터 조인
주민등록인구 데이터와 shp 파일을 조인하기 위해 먼저 자치구 이름의 불필요한 공백을 제거합니다. 이후 shp 파일의 자치구 이름에서 ’서울특별시’라는 접두어를 제거하여 데이터의 이름 형식을 통일합니다. 마지막으로, left_join을 통해 두 데이터를 결합합니다. 이렇게 하면 지도 데이터에 인구수가 결합되어 시각화에 사용할 수 있는 데이터가 완성됩니다.
# 데이터 가공
pop <- pop %>%
mutate(행정구역별 = str_trim(행정구역별, "both"))
# 데이터 조인
joined_data <- sgg %>%
mutate(SGG_NM = str_replace(SGG_NM, "서울특별시 ", "")) %>%
left_join(pop, by=c("SGG_NM" = "행정구역별"))
# 데이터 확인
head(joined_data)
## Simple feature collection with 6 features and 6 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 195102.6 ymin: 545229.7 xmax: 202367.6 ymax: 559196.5
## Projected CRS: Korea_2000_Korea_Central_Belt_2010
## ADM_SECT_C SGG_NM SGG_OID COL_ADM_SE 시점 계 (명)
## 1 11110 종로구 11 11110 1995 203086
## 2 11110 종로구 11 11110 2023 139417
## 3 11140 중구 34 11140 1995 143138
## 4 11140 중구 34 11140 2023 121312
## 5 11170 용산구 1 11170 1995 254579
## 6 11170 용산구 1 11170 2023 213151
## geometry
## 1 MULTIPOLYGON (((197800 5590...
## 2 MULTIPOLYGON (((197800 5590...
## 3 MULTIPOLYGON (((202072.4 55...
## 4 MULTIPOLYGON (((202072.4 55...
## 5 MULTIPOLYGON (((197569.6 55...
## 6 MULTIPOLYGON (((197569.6 55...
지도 그리기
ggplot을 사용해 지도를 작성합니다. geom_sf는 공간 데이터를 지도에 표현하며, fill 매핑을 통해 각 자치구의 인구수를 색상으로 시각화합니다. facet_wrap(~시점)을 활용해 1995년과 2023년 지도를 두 개의 패널로 구분하여 배치합니다. 자치구 이름과 인구수를 지도 위에 텍스트로 표시하기 위해 geom_sf_text를 사용합니다.
ggplot(data = joined_data, aes(fill = `계 (명)`)) +
geom_sf(color="gray20") +
scale_fill_gradient(low = '#ffffff', high = '#ff6666') +
facet_wrap(~시점) +
geom_sf_text(aes(label = SGG_NM)) +
geom_sf_text(aes(label = `계 (명)`)) +
theme_void()

지도 테마 다듬기
지도에 가독성을 더하기 위해 글꼴과 범례를 설정합니다. 먼저, font_add를 사용해 글꼴을 추가하고 showtext_auto로 활성화합니다. geom_sf_text의 vjust를 조정해 자치구 이름과 인구수 텍스트의 위치를 세밀히 조정합니다. 범례는 scale_fill_gradient에서 타이틀, 콤마 표기를 통해 가독성을 높입니다. 마지막으로, theme_void를 사용해 배경을 없애고, theme으로 범례와 스트립 텍스트의 글꼴, 크기, 위치를 조정해 전체적인 스타일을 완성합니다.
# 패키지 로드
library(scales)
library(showtext)
# 글꼴 설정
font_add("kopub", "C:/.../AppData/Local/Microsoft/Windows/Fonts/KoPub Dotum Medium.ttf")
font_add("kopub_b", "C:/.../AppData/Local/Microsoft/Windows/Fonts/KoPub Dotum Bold.ttf")
showtext_auto()
showtext_opts(dpi=300)
theme.size = 10
geom.text.size = theme.size / .pt
# 지도 작성
ggplot(data = joined_data, aes(fill = `계 (명)`)) +
geom_sf(color="gray20") +
scale_fill_gradient(name = "인구 수(명)",
low = '#ffffff',
high = '#ff6666',
labels=comma) +
facet_wrap(~시점) +
geom_sf_text(aes(label = SGG_NM),
vjust = -0.5,
family = "kopub_b",
size = geom.text.size) +
geom_sf_text(aes(label = comma(`계 (명)`)),
vjust = 1.2,
family = "kopub",
size = geom.text.size) +
theme_void(base_family = "kopub", base_size = theme.size) +
theme(
strip.text = element_text(family = "kopub_b", size = theme.size * 1.5),
legend.position = "bottom",
legend.title = element_text(family = "kopub_b", vjust=1),
legend.text = element_text(size=theme.size),
legend.key.height = unit(theme.size, "pt"),
legend.key.width = unit(theme.size * 5, "pt")
)
