bitSpatial 패키지는 GitHub으로부터 다음과 같이 설치합니다.
# install.packages("devtools")
devtools::install_github("bit2r/bitSpatial")
지방자치 시대에 대한민국이 아닌 지역에 대한 경제, 사회, 문화 등의 이해는 중요한 사회과학적인 요소입니다. 그리고 지역에 대한 이해는 좀더 직관적인 시각화 방법이 유용합니다.
그러나 이러한 방법은 장미빛 청사진이 아닙니다. 데이터를 지역 기준으로 집계하고, 집계한 통계를 시각화하는 방법이 녹록치 않기 때문입니다.
현재 우리나라 행정구역 체계는 다음 그림과 같습니다. 행정 구역은 크게 1개의 특별시(서울), 6개의 광역시(부산, 대구, 인천, 광주, 대전, 울산), 6개의 도(경기, 충북, 충남, 전남, 경북, 경남), 1개의 특별자치시(세종), 3개의 특별자치도(강원, 전북, 제주)로 구성되어 있습니다.1
삽화소스: 국토지리정보원 청소년을 위한 국가지집
이를 기준으로 행정구역 체계를 일반화하기 위해서 광역시도 > 시군구 > 읍면동 레벨의 3단계 계층을 생각해볼 수 있습니다.
- 광역시도
- 특별시 + 특별자치시 + 특별자치도 + 광역시 + 도
- 시군구
- 시 + 군 + 구
- 읍면동
- 읍 + 면 + 동
그러나 광역시도 > 시군구 > 읍면동의 3단 계층 구조에서 다음의 예외 사항이 존재합니다.
- 세종특별 자치시의 구조
- 2단계 계층
- 광역시도 > 시군구 레벨 없음 > 읍면동
- 충청남도 천안시 동남구 신방동
- 4단계 계층
- 도 + 시 + 구 + 읍면동
- 인구가 팽창하는 도에 포함된 일부 시의 경우 (구) 레벨을 포함하기도 함
- 경기도 수원시, 경기도 성남시, 경기도 고양시 등
행정구역 체계는 변화합니다. 다음 그림은 광역시도 레벨의 행정구역 체계 변경 히스토리입니다.
삽화소스: 국토지리정보원 청소년을 위한 국가지집
지역에 대한 통계가 현황 분석을 목적으로 한다면 문제가 없겠지만, 시점별 통계의 변화를 살피는 추이분석을 수행할 경우에는 문제가 발생합니다. 행정구역 체계의 변경으로 인해서 과거의 통계를 현재의 행정구역에 매핑할 수 없는 경우가 있습니다.
그나마 광역시도와 시군구 레벨의 행정구역의 변경은 흔치 않지만, 읍면동 레벨(특히 행정동 기준)의 행정구역의 변경은 매우 잦게 발생합니다. 하나의 동이 여러 개의 동으로 분동되거나, 여러 동이 합동하여 하나의 구로 변합되거나 하는 사례가 많습니다.
광역시도 > 시군구 > 읍면동 레벨에서의 읍면동은 두 가지 기준이 있습니다.
- 법정동
- 법으로 정한 동의 의미로, 법으로 정한 행정구역의 단위
- 1914년 시행된 행정구역 통폐합때 정한 것으로, 현재까지 거의 변동이 없음
- 행정동
- 행정능률, 주민편의에 의해 설정한 행정구역의 단위
- 인구수 기반으로 나뉘어지며 주민센터에서 관리하는 구역
- 편의에 따라 분할/병합 등의 변경 및 폐지가 발생함
문제는 법정동:행정동의 관계는 M:N의 관계로, 통계 집계에 있어서 상호 매핑하기 어렵기 때문에 하나의 집계 기준을 가져가야 한다는 점입니다.
- 법정동은 변동이 적기 때문에 법정동의 집계기준으로 가져가면 추이분석
등의 장점이 있지만,
- 공공 데이터에서 법정동 기준으로 배포하는 통계가 적습니다.
- 행정동은 변동이 많기 때문에 행정동의 집계기준으로 가져가면 추이분석
등의 단점이 있지만,
- 공공 데이터에서 행정동 기준으로 배포하는 통계가 많습니다.
- 많은 파생 통계가 인구통계 기반으로 작성되기 때문에 인구통계는 중요한 통계입니다.
지번 주소에서 이제는 도로명 주소가 표준 주소 체계로 사용되고 있습니다. 그러나 우리가 집계해야할 지역기반의 Raw 데이터는 지번주소, 도로명주소가 혼용되어 있습니다. 과거의 통계를 무시하고 항상 현재 기준의 통계를 사용할 수 없으며, 경우에 따라서 현재 기준의 Raw 데이터가 과거 주소 체계로 배포되는 문제도 있습니다.
도로명 주소는 길게 이어진 도로를 기준으로 만들어진 체계입니다. 구역을 분할하는 기준이 아니어서, 어떤 도로는 여러 구역을 넘나들면 이어져 있기도합니다. 그래서 도로명 주소 정보로 행정동이나 법정동으로 집계하는 것은 그리 쉬운 작업은 아닙니다.
정부에서 제공하는 공공 데이터는 한심스럽게도, 이러한 지역 체계의 표준이 없습니다. 일례로 행정안전부에서 관리하는 행정동 코드와 통계청에서 관리하는 행정동 코드 체계가 다릅니다. 그로므로 여러 공공 데이터를 통합하여 데이터를 분석할 때에는 데이터 수요자가 이 문제를 해결해야 합니다.
지리정보는 국가분단이라는 안보의 특수성이 있기 때문에 중요하게 다뤄야하는 기밀일 수 있습니다. 그러나 행정구역 경계 수치지도의 경우에는 이와 무관한 정보로서 민간 및 학계의 연구를 위해서 안정적으로 공급되어야 합니다. 그러나 정부의나 공공 기관에서 배포하는 행정구역 경계 수치지도는 거의 제한적이고, 오류가 포함되어 있거나 행정구역 체계의 변화를 담지 못하고 있는 실정입니다. 변화하는 행정 구역체계에 따라 히스토리컬하게 배포하지 않습니다.
bitSpatial
은 통계지리정보(SGI; Statistical Geographic Information)를
개발을 지원하는 패키지로 광역시도 > 시군구 > 읍면동 레벨의 행정구역
경계 수치지도 기반으로 공공데이터를 집계하고, 이를 시각화하는 일련의
리소스를 제공합니다.
bitSpatial
는 앞에서 제기한 이슈를 완벽하게 해결한 완벽한 솔루션은
아닙니다. 그러나 차선의 솔루션은 될 수 있습니다. 현재와 근접한 행정동
기준의 행정구역 경계 수치지도를 준비하였고, 여러 통계를 집적했습니다.
그리고 앞으로도 여러 통계를 추가해 나가려 합니다.
- 광역시도 > 시군구 > 읍면동 레벨의 행정구역 경계 수치지도 제공
- 수치지도를 sf 객체로 가공하여 제공
- 매년 6월 기준으로 배포
- 17 광역시도
- 250 시군구
- 3,528 읍면동
- 수치지도를 sf 객체로 가공하여 제공
- 수치지도와 결합한 행정구역별로 집계된 통계
- 40개 통계
- 인구통계, 초중고 학교 통계, 병원/약국 통계
- 집계 통계는 계속 추가할 예정
- 수치지도와 조인할 수 있는 집계된 통계
- 성별/연령대별 인구수
- 수치지도와 조인할 수 있는 위치정보 데이터
- 초중등학교 위치 데이터
- 약국/병원 위치 데이터
- 상가 위치 데이터
- 위치 좌표 기반 연산
- 두 좌표의 거리 구하기
- 경위도 좌표계 위치정보의 좌표계 변환
- 지리기반 집계를 위한 메타
- 우편번호 행정동 매핑 데이터
- 위도/경도로 행정구역 코드와 이름 가져오기
- 주제도 시각화
- 최적 지도 이미지 사이즈 계산
- map 시각화용 ggplot2 테마
- 수치지도
- 통계청의 통계지리서비스에서 배포하는
지역경계 수치지도
- 행정동 기준
- 매년 주기적으로 배포하는 장점으로 선정
- 매년 6월 기준으로 배포
- 통계청의 통계지리서비스에서 배포하는
지역경계 수치지도
- 통계 및 집계 데이터
- 인구통계
- 행정안전부의 주민등록 인구 통계
- https://jumin.mois.go.kr/index.jsp
- 초중고 학교 위치 정보
- 공공데이터포털의 전국초중등학교위치표준데이터
- https://www.data.go.kr/data/15021148/standard.do?recommendDataYn=Y
- 병원 및 약국 위치 정보
- 공공데이터 포털의 건강보험심사평가원_전국 병의원 및 약국 현황
- https://www.data.go.kr/data/15051059/fileData.do
- 상가 위치 정보
- 공공데이터 포털의 소상공인시장진흥공단_상가(상권)정보
- https://www.data.go.kr/data/15083033/fileData.do
- 인구통계
sf 객체를 다룰 수 있는 R 사용자는 sf 객체로 제공하는 수치지도 정보와
데이터를 활용할 수 있겠으나, 익숙치 않는 사용자는 주제도 시각화 함수인
thematic_map()
함수를 사용하면 주제도를 그릴 수 있습니다.
이 함수의 원형은 다음과 같습니다.
thematic_map(
zoom = c("mega", "cty", "admi")[1],
subset = NULL,
stat = NULL,
polygon = TRUE,
point = FALSE,
label = NULL,
col_cnt = 9,
palette = "YlOrRd",
line_col = "darkgray",
fill = "lightblue",
point_col = "blue",
title = NULL,
subtitle = NULL,
legend_pos = c("none", "right", "left", "bottom", "top"),
base_family = "NanumSquare"
)
광역시도 > 시군구 > 읍면동의 3단계 줌 레벨을 지원합니다.
zoom
인수로 줌 레벨을 지원합니다. 기본값인 mega
가 광역시도 레벨이며,
cty
, admi
인수값으로 시군구, 읍면동 레벨의 주제도를 그릴 수
있습니다.
library(bitSpatial)
thematic_map(stat = "인구수",
title = "광역시도별 인구분포 현황",
legend_pos = "right")
thematic_map(zoom = "cty",
stat = "병원수",
title = "시군구별 병원수 현황",
palette = "Blues")
다음은 수도원의 병원수 분포를 시각화합니다.
thematic_map(zoom = "cty",
stat = "병원수",
subset = mega_nm %in% c("서울특별시", "경기도", "인천광역시"),
title = "시군구별 병원수 현황",
subtitle = "수도권 지역 (서울특별시, 경기도, 인천광역시)",
palette = "Blues")
다음은 서울 양천구의 행정동별 평균연령 현황입니다. 양천구의 기하학적 모습이 마치 강아지와 닯았습니다.
thematic_map(zoom = "admi",
subset = mega_nm == "서울특별시" & cty_nm %in% "양천구",
stat = "age_mean",
label = "name",
title = "서울 양천구 인구통계 주제도",
subtitle = "동별 평균 연령 현황",
palette = "Purples",
legend_pos = "right")
기하학적으로 통계를 표현하는 방법은 3가지로 각각의 인수로 표현이 가능합니다. 한 주제도에 세 가지 모두 표현이 가능하지만 가독성이 떨어져서 한 가지만 표현하는 것을 권장합니다.
- 다각형의 그라데이션 색상
polygon
- TRUE/FALSE로 지정
- 기본값은 TRUE
- 다각형 중심의 포인트 크기
point
- TRUE/FALSE로 지정
- 기본값은 FALSE
- 다각형 중심에 숫자 라벨 표현
label
- NULL은 미표현
- “name”
- 행정구역 이름
- “value”
- 통계 수치
- 기본값은 NULL
thematic_map(zoom = "admi",
subset = cty_nm %in% "노원구",
stat = "household",
polygon = FALSE,
point = TRUE, point_col = "Red",
label = "name",
line_col = "black", fill = "grey90",
title = "서울특별시 가구 분포",
subtitle = "노원구 동별 가구현황")
stats_info
에 패키지에서 제공하는 집계된 통계 현황이 있습니다. 통계
아이디와 이름이 있는데 모두 thematic_map()
함수에서 주제도를 그릴 때
통계 정보를 식별할 수 있는 stat
인수에 사용 가능합니다.
stats_info |>
gt::gt() |>
gt::as_raw_html()
stats_id | stats_nm | is_use |
---|---|---|
land_area | 면적 | TRUE |
population | 인구수 | TRUE |
household | 가구수 | TRUE |
pop_per_hosue | 가구당인구수 | TRUE |
pop_male | 남성인구수 | TRUE |
pop_female | 여성인구수 | TRUE |
male_per_female | 여성대비남성인구 | TRUE |
age_mean_male | 남성평균연령 | TRUE |
age_mean_female | 여성평균연령 | TRUE |
age_mean | 평균연령 | TRUE |
elemnt_schl_cnt | 초등학교수 | TRUE |
mdle_schl_cnt | 중학교수 | TRUE |
high_schl_cnt | 고등학교수 | TRUE |
pharmacy_cnt | 약국수 | TRUE |
total_hospital_cnt | 총의료기관수 | TRUE |
doctor_cnt | 총의사수 | TRUE |
hospital_cnt | 병원수 | TRUE |
pubhealth_center_cnt | 보건소수 | TRUE |
pubhealth_branch_cnt | 보건지소수 | TRUE |
pubhealth_clinic_cnt | 보건진료소수 | TRUE |
tertiary_hospital_cnt | 상급종합병원수 | TRUE |
nursing_hospital_cnt | 요양병원수 | TRUE |
clinic_cnt | 의원수 | TRUE |
mental_hospital_cnt | 정신병원수 | TRUE |
midwife_hospital_cnt | 조산원수 | TRUE |
general_hospital_cnt | 종합병원수 | TRUE |
dental_hospital_cnt | 치과병원수 | TRUE |
dental_clinic_cnt | 치과의원수 | TRUE |
kmedicine_hospital_cnt | 한방병원수 | TRUE |
kmedicine_clinic_cnt | 한의원수 | TRUE |
store_cnt_retail | 소매업체수 | TRUE |
store_cnt_acomodt | 숙박업체수 | TRUE |
store_cnt_food | 음식업체수 | TRUE |
store_cnt_estate | 부동산업체수 | TRUE |
store_cnt_tech | 기술업체수 | TRUE |
store_cnt_lease | 관리임대업체수 | TRUE |
store_cnt_edu | 교육서비스업체수 | TRUE |
store_cnt_medical | 보건의료업체수 | TRUE |
store_cnt_leisure | 스포츠여가업체수 | TRUE |
store_cnt_service | 개인서비스업체수 | TRUE |
현재는 다음과 같은 위치 데이터를 제공합니다.
- 초중고등학교 위치정보
- 약국 위치정보
- 병원 위치정보
- 상가 위치정보
위치 데이터인 학교위치, 약국위치, 병원위치를 주제도에 매시업하여 시각화가 가능합니다.
pos_school <- school |>
filter(mega_nm %in% "서울특별시") |>
filter(school_class %in% "초등학교") |>
st_as_sf(coords = c("lon", "lat"), crs = 4326)
ggplot() +
stat_density_2d(data = pos_school,
mapping = aes(x = purrr::map_dbl(geometry, ~.[1]),
y = purrr::map_dbl(geometry, ~.[2]),
fill = after_stat(density)),
geom = 'tile',
contour = FALSE,
alpha = 0.7) +
scale_fill_viridis_c(option = "viridis", direction = -1) +
geom_sf(data = cty |>
filter(mega_nm %in% "서울특별시"),
color = "grey30", fill = NA, linewidth = 0.8) +
geom_sf(data = pos_school, color = "blue", size = 0.5) +
xlim(126.75, 127.22) +
ylim(37.42, 37.71) +
labs(title = "서울특별시 초등학교 분포 현황",
subtitle = "출처: 공공데이터포털의 전국 초중등학교 위치 표준데이터") +
theme_custom_map()