시간에 따른 변화 시각화하기

개요

서울 자치구별 주민등록인구수 변화를 시각화하고 싶다고 가정해봅시다. 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()

1995년, 2023년 서울 구별 주민등록인구 수 변화를 나타낸 지도

지도 테마 다듬기

지도에 가독성을 더하기 위해 글꼴과 범례를 설정합니다. 먼저, font_add를 사용해 글꼴을 추가하고 showtext_auto로 활성화합니다. geom_sf_textvjust를 조정해 자치구 이름과 인구수 텍스트의 위치를 세밀히 조정합니다. 범례는 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")
  )

1995년, 2023년 서울 구별 주민등록인구 수 변화를 나타낸 지도 테마 수정