(ggplot2) 지도 여러개를 하나로 묶어 그리기

저번 포스팅에서 외식비 관련해서 지도 그래프를 하나만 그렸었었다. 그런데 외식비 통계에 보면 냉면, 비빔밥, 삽겹살 등 종류가 다양하기 때문에 종류별로 지도를 하나로 묶어 표현하는게 좋을거 같았다. shp 지도파일만 있으면 ggplot을 이용해서 여러 그래프를 한판에 그릴 수 있다.

데이터 준비

한국소비자원 외식비 가격정보 사이트에 가서 데이터를 다운받는다. 그리고 불필요한 데이터를 지워 정리한다.

excel_data = read_excel('~/data.xls') |> 
  select(1:5, 삼겹살 = 7, 8:10) |> 
  arrange(지역)

# A tibble: 16 × 9                                                                                 
  번호  지역  냉면  비빔밥 김치찌개백반 삼겹살 자장면 삼계탕 칼국수
  <chr> <chr> <chr> <chr>  <chr>        <chr>  <chr>  <chr>  <chr> 
1 8     강원  9667  9722   8167         15321  6722   15667  8444  
2 9     경기  10017 9128   7962         17507  6776   16414  8862  
3 10    경남  10192 8500   8231         17944  6077   16077  7231  
4 11    경북  9538  9154   8038         15796  5923   15000  7923  
5 2     광주  9400  9900   8000         14844  6800   16400  8200  
# ℹ 11 more rows
# ℹ Use `print(n = ...)` to see more rows

외식비 엑셀데이터에 지도데이터의 시도 코드를 매핑해서 추가한다.

all_data = excel_data |> 
  bind_cols(sido3 |> st_drop_geometry() |> 
              arrange(CTP_KOR_NM) |> 
              select(1))

# A tibble: 16 × 10
  번호  지역  냉면  비빔밥 김치찌개백반 삼겹살 자장면 삼계탕 칼국수 CTPRVN_CD
  <chr> <chr> <chr> <chr>  <chr>        <chr>  <chr>  <chr>  <chr>  <chr>    
1 8     강원  9667  9722   8167         15321  6722   15667  8444   51       
2 9     경기  10017 9128   7962         17507  6776   16414  8862   41       
3 10    경남  10192 8500   8231         17944  6077   16077  7231   48       
4 11    경북  9538  9154   8038         15796  5923   15000  7923   47       
5 2     광주  9400  9900   8000         14844  6800   16400  8200   29       
# ℹ 11 more rows
# ℹ Use `print(n = ...)` to see more rows   

CTPRVN_CD 필드를 둘다 가지고 있도록 미리 셋팅을 해놨기 때문에, left_join 을 이용하면 지도 데이터에 외식비 데이터를 쉽게 조인할 수 있다. 그런데, 이런 레이아웃으로는 효율적으로 여러개의 지도를 그리기 어렵다.

sido66 = sido3 |> 
  left_join(all_data, by = join_by(CTPRVN_CD)) 

# 출력결과
Simple feature collection with 16 features and 12 fields
Geometry type: GEOMETRY
Dimension:     XY
Bounding box:  xmin: 747235.2 ymin: 1468717 xmax: 1391920 ymax: 2065895
Projected CRS: Korea 2000 / Unified CS
# A tibble: 16 × 13
   CTPRVN_CD CTP_KOR_NM    CTP_ENG_NM                  geometry 번호  지역  냉면  비빔밥 김치찌개백반
   <chr>     <chr>         <chr>                 <GEOMETRY [m]> <chr> <chr> <chr> <chr>  <chr>       
 1 11        서울특별시    Seoul      POLYGON ((966793.7 194081     서울  11308 10577  8000        
 2 26        부산광역시    Busan      MULTIPOLYGON (((11371475     부산  10714 8814   7714        
 3 27        대구광역시    Daegu      POLYGON ((1087860 1760093     대구  10417 9367   7150        
 4 28        인천광역시    Incheon    MULTIPOLYGON (((905900.67     인천  10667 8833   7333        
 5 29        광주광역시    Gwangju    POLYGON ((932781.7 169612     광주  9400  9900   8000        
 6 30        대전광역시    Daejeon    POLYGON ((991177.8 183214     대전  10600 9800   9300        
 7 31        울산광역시    Ulsan      POLYGON ((1161481 1709706     울산  10000 9700   8000        
 8 41        경기도        Gyeonggi-POLYGON ((937517.7 189089     경기  10017 9128   7962        
 9 43        충청북도      Chungcheo… POLYGON ((1063153 19165115    충북  8929  9171   8357        
10 44        충청남도      Chungcheo… MULTIPOLYGON (((904505.914    충남  9500  9500   8500        
11 45        전라북도      Jeollabuk… POLYGON ((921372.9 1776013    전북  9200  11290  8600        
12 46        전라남도      Jellanam-MULTIPOLYGON (((916440.312    전남  8778  8500   7500        
13 47        경상북도      Gyeongsan… MULTIPOLYGON (((118725911    경북  9538  9154   8038        
14 48        경상남도      Gyeongsan… MULTIPOLYGON (((108016010    경남  10192 8500   8231        
15 50        제주특별자치… Jeju-do    POLYGON ((883486.3 1470316    제주  9000  9750   9125        
16 51        강원특별자치… Gangwon-do POLYGON ((1157807 1925328     강원  9667  9722   8167        
# ℹ 4 more variables: 삼겹살 <chr>, 자장면 <chr>, 삼계탕 <chr>, 칼국수 <chr>

횡으로 펼쳐져있던 외식비종류를 아래방향으로 내려야 한다. pivot_longer 을 이용하면 아래와 같이 데이터 형태가 변환된다.

sido77 = sido66 |> 
  pivot_longer(냉면:삼계탕) |> 
  mutate(value = as.numeric(value))

# 출력결과
Simple feature collection with 96 features and 8 fields
Geometry type: GEOMETRY
Dimension:     XY
Bounding box:  xmin: 747235.2 ymin: 1468717 xmax: 1391920 ymax: 2065895
Projected CRS: Korea 2000 / Unified CS
# A tibble: 96 × 9
   CTPRVN_CD CTP_KOR_NM CTP_ENG_NM                            geometry 번호  지역  칼국수 name  value
 * <chr>     <chr>      <chr>                           <GEOMETRY [m]> <chr> <chr> <chr>  <chr> <dbl>
 1 11        서울특별시 Seoul      POLYGON ((966793.7 1940892, 9671601     서울  8962   냉면  11308
 2 11        서울특별시 Seoul      POLYGON ((966793.7 1940892, 9671601     서울  8962   비빔… 10577
 3 11        서울특별시 Seoul      POLYGON ((966793.7 1940892, 9671601     서울  8962   김치…  8000
 4 11        서울특별시 Seoul      POLYGON ((966793.7 1940892, 9671601     서울  8962   삼겹… 19429
 5 11        서울특별시 Seoul      POLYGON ((966793.7 1940892, 9671601     서울  8962   자장…  7069
 6 11        서울특별시 Seoul      POLYGON ((966793.7 1940892, 9671601     서울  8962   삼계… 16846
 7 26        부산광역시 Busan      MULTIPOLYGON (((1137147 1677385, 15     부산  7200   냉면  10714
 8 26        부산광역시 Busan      MULTIPOLYGON (((1137147 1677385, 15     부산  7200   비빔…  8814
 9 26        부산광역시 Busan      MULTIPOLYGON (((1137147 1677385, 15     부산  7200   김치…  7714
10 26        부산광역시 Busan      MULTIPOLYGON (((1137147 1677385, 15     부산  7200   삼겹… 15830
# ℹ 86 more rows
# ℹ Use `print(n = ...)` to see more rows

facet_wrap 을 사용하여 여러지도를 하나의 plot 안에 그릴 수 있다. 그런데 이 데이터를 기준으로 ggplot2 를 그리면, 생각했던 것과는 다른 결과가 출력된다. 냉면 외식비의 최소,최대값을 기준으로 색이 채워져야 하는데, 전체 금액에서 냉면이 차지하는 범위만큼의 색만 사용되고 있었다.

ggplot(data = sido77) +
  geom_sf(aes(fill = value)) +
  scale_fill_viridis_c(option = "B", begin = 0.4, direction = -1) +
  facet_wrap(vars(name))

min-max 스케일로 0~1사이값으로 조정

이럴때는 최소값과 최대값을 가지고 0~1 값으로 리스케일 작업을 해야 한다.

sido88 = sido77 |> 
  group_by(name) |> 
  mutate(min_value = min(value),
         max_value = max(value)) |> 
  mutate(norm = (value - min_value) / (max_value - min_value))

Simple feature collection with 6 features and 11 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 935186.2 ymin: 1936868 xmax: 971969.6 ymax: 1966536
Projected CRS: Korea 2000 / Unified CS
# A tibble: 6 × 12
# Groups:   name [6]
  CTPRVN_CD CTP_KOR_NM CTP_ENG_NM                   geometry 번호  지역  칼국수 name  value min_value
  <chr>     <chr>      <chr>                   <POLYGON [m]> <chr> <chr> <chr>  <chr> <dbl>     <dbl>
1 11        서울특별시 Seoul      ((966793.7 1940892, 967161     서울  8962   냉면  11308      8778
2 11        서울특별시 Seoul      ((966793.7 1940892, 967161     서울  8962   비빔… 10577      8500
3 11        서울특별시 Seoul      ((966793.7 1940892, 967161     서울  8962   김치…  8000      7150
4 11        서울특별시 Seoul      ((966793.7 1940892, 967161     서울  8962   삼겹… 19429     14197
5 11        서울특별시 Seoul      ((966793.7 1940892, 967161     서울  8962   자장…  7069      5923
6 11        서울특별시 Seoul      ((966793.7 1940892, 967161     서울  8962   삼계… 16846     14714
# ℹ 2 more variables: max_value <dbl>, norm <dbl>

그러면 제대로 색이 더 대비되어 출력된다. 빨간색이 짙을수록 비싼 물가 지역이고, 엻은색일 수록 저렴한 지역으로 표현하였다.

ggplot(data = sido88) +
  geom_sf(aes(fill = norm)) +
  scale_fill_viridis_c(option = "B", begin = 0.4, direction = -1) +
  coord_sf(datum = st_crs(5179)) +
  facet_wrap(vars(name))


더 보면 좋을 글들