티스토리 뷰

목적

우편번호 별로 권역 코드가 매핑되어 있는 데이터를 지도 위에 표현하고자합니다.

권역코드 별로 색상을 다르게하여 권역을 구분하고 우편번호를 지도 위에서 확인하겠습니다.

 

1. folium을 이용한 시각화

데이터 준비

folium의 경우 utf-8로 인코딩된 csv파일을 이용할 경우 오류가 납니다. euc-kr로 인코딩을 변경해주세요. 

저는 mac 환경을 사용하고 있고 아래 명령어로 인코딩을 바꾸었습니다. 

iconv -c -f utf-8 -t euc-kr code-bas_id.csv > code-bas_id_euc-kr.csv

file -I code-bas_id.csv, file -I code-bas_id_euc-kr.csv 명령어로 인코딩이 잘 된 것을 확인하였습니다. 

code-bas_id_euc-kr.csv
0.01MB

 

 

 

colab에서 작업을 하였습니다.

  1. 지리 데이터 시각화(2)에서 만든 서울시 기초 구역도 geo json파일(TL_KODIS_BAS_W.json)
  2. 경기도 기초 구역도 geo json파일(TL_KODIS_BAS_W_GG.json)
  3. 위에서 euc-kr로 인코딩한 권역코드-우편번호 매핑 파일(code-bas_id_euc-kr.csv)을 불러옵니다.  

필요한 라이브러리를 import 합니다. 

import folium
import pandas as pd
import json

euc-kr로 인코딩된 권역코드-우편번호 매핑 csv 파일의 우편번호를 전처리하고 컬럼명을 변경합니다. ('우편번호*'->'BAS_ID', '권역코드*'->'AREA_CD')

df = pd.read_csv('sample_data/code-bas_id_euc-kr.csv', encoding = 'euc-kr')
df['우편번호*'] = df['우편번호*'].astype('str').str.zfill(5)  # 다섯자리 맞춰주기 위해. 
df.rename(columns={'우편번호*':'BAS_ID'}, inplace=True)
df.rename(columns={'권역코드*':'AREA_CD'}, inplace=True)
df.head()

서울 geo json 파일과 경기도 geo json 파일을 합쳐 geo_json이라는 변수를 만듭니다. 

# 서울 geo json 가져오기
with open('sample_data/TL_KODIS_BAS_W.json') as response:
  geo_json = json.load(response)

# 경기도 geo json 가져오기
with open('sample_data/TL_KODIS_BAS_W_GG.json') as response:
  geo_json2 = json.load(response)

# 서울 geo json과 경기도 geo json 합치기.
d = dict()
# geo_json.keys()  # dict_keys(['type', 'features'])
d['type'] = geo_json['type']
d['features'] = geo_json['features']
d['features'].extend(geo_json2['features'])
# len(d['features'])
geo_json = d

geo_json의 구조는 다음과 같습니다.

2. folium.Choropleth

만약 권역코드(AREA_CD) 가 연속형 변수일 경우 아래와 같이 쉽게 Choropleth 지도를 만들 수 있지만

권역 코드가 명목형 변수이기 때문에 folium.Choropleth를 이용할 수 없고 folium.GeoJson을 이용해야합니다.  

m = folium.Map(location=[37.541, 126.986], zoom_start=10)

# Choropleth 레이어를 만들고, 맵 m에 추가.
folium.Choropleth(
    geo_data=geo_json,
    data=df,
    columns=['BAS_ID', 'AREA_CD'],
    key_on='feature.properties.BAS_ID', # feature.properties.BAS_ID
    fill_color='YlGn',
).add_to(m)

m.save("index.html")

만약 명목형 변수 'AREA_CD'를 그냥 사용했을 경우 아래와 같은 오류가 납니다. 

TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

3. folium.GeoJson

geo_json의 properties에 특정 값 추가

(folium.GeoJson은 주어진 json파일로만 그래프를 그립니다. 그래서 df에만 있던 AREA_CD 정보를 geo_json에 추가해주어야합니다.)

geo_json 데이터의 ['properties']에 ['AREA_CD']라는 키를 추가해 해당 우편번호의 권역 코드 값을 채웁니다. 

df['BAS_ID']에 해당 우편번호가 없다면 AREA_CD엔 'None'값을 채웁니다. 

for i in range(len(geo_json['features'])):
  try:
    area_cd = df[df['BAS_ID'] == geo_json['features'][i]['properties']['BAS_ID']]['AREA_CD'].tolist()[0]
    geo_json['features'][i]['properties']['AREA_CD'] = area_cd
  except:
    geo_json['features'][i]['properties']['AREA_CD'] = 'None'

 

  AREA_CD 추가

이 데이터를 이용해 서울과 경기도 전체 지도를 대상으로 AREA_CD가 None인 것과 None이 아닌 것에 대해 색상을 다르게 해서 지도를 만들어보겠습니다. 

참고: https://nbviewer.org/gist/talbertc-usgs/18f8901fc98f109f2b71156cf3ac81cd

center = [37.541, 126.986]
m = folium.Map(location=center, zoom_start=10)

def style_function(feature):
    AREA_CD = feature['properties']['AREA_CD']
    return {
        'fillOpacity': 0.5,
        'weight': 0.5,
        'fillColor': 'gray' if AREA_CD == 'None' \
            else 'orange'
    }

gjson = folium.GeoJson(geo_json, 
                       style_function=style_function
                      ).add_to(m)
m.save("index.html")

위 코드에서 중요한 부분은 gjson = folium.GeoJson() 부분입니다. 그 위에 있는 함수는 스타일을 지정해주는 함수에요. 

AREA_CD가 None인 부분은 회색, AREA_CD가 None이 아닌 부분은 주황색, 서울+경기가 아닌 부분은 색이 칠해지지 않은 지도를 생성하였습니다. 

다음은 geo_json에서 AREA_CD가 None인 데이터를 제외하고 지도를 생성해보겠습니다. 

new_geo_json = dict()
new_geo_json['type'] = geo_json['type']
new_geo_json['features'] = list()

for i in range(len(geo_json['features'])):
  if geo_json['features'][i]['properties']['AREA_CD'] == 'None':
    continue
  else:
    new_geo_json['features'].append(geo_json['features'][i])
 
 geo_json = new_geo_json

색상도 조금 다양하게 바꿔보았습니다. 

(색상을 조금 더 쉽게 설정하는 방법: 지리 데이터 시각화(6) 의 color 참고)

center = [37.541, 126.986]
m = folium.Map(location=center, zoom_start=10)

def style_function(feature):
    AREA_CD = feature['properties']['AREA_CD']
    return {
        'fillOpacity': 0.5,
        'fillColor': 'aliceblue' if AREA_CD[1:] == '00' \
            else 'cyan'if AREA_CD[1:] == '01'\
            else 'greenyellow'if AREA_CD[1:] == '02'\
            else 'dimgray'if AREA_CD == 'S03'\
            else 'forestgreen'if AREA_CD[1:] == '03'\
            else 'yellow'if AREA_CD == 'S04'\
            else 'deeppink'if AREA_CD[1:] == '04'\
            else 'deepskyblue'if AREA_CD[1:] == '05'\
            else 'black'if AREA_CD[1:] == '06'\
            else 'blanchedalmond'if AREA_CD[1:] == '07'\
            else 'blue'if AREA_CD[1:] == '08'\
            else 'blueviolet'if AREA_CD[1:] == '09'\
            else 'brown'if AREA_CD[1:] == '10'\
            else 'gray'

    }

gjson = folium.GeoJson(geo_json, 
                       style_function=style_function
                      ).add_to(m)
m.save("index.html")

 

4. folium.features.GeoJsonTooltip

지도위에 권역 별로 표시는 되었지만 해당 구역의 우편번호, 권역코드를 확인할 수 없는 불편함이 있었습니다. 

이를 해결하기 위해 tool tip 을 추가해 Interactive choropleth 를 그려보았습니다. (참고: https://vverde.github.io/blob/interactivechoropleth.html)

 

위에서 만든 코드에 m.save("index.html")줄을 제거하고 아래 내용을 추가해줍니다. 

style_function = lambda x: {'fillColor': '#ffffff', 
                            'color':'#000000', 
                            'fillOpacity': 0.1, 
                            'weight': 0.1}
highlight_function = lambda x: {'fillColor': '#000000', 
                                'color':'#000000', 
                                'fillOpacity': 0.50, 
                                'weight': 0.1}

NIL=folium.features.GeoJson(
        geo_json,
        style_function=style_function, 
        control=False,
        highlight_function=highlight_function, 
        tooltip=folium.features.GeoJsonTooltip(fields=['BAS_ID','AREA_CD'],
            aliases=['ZIP_CD','AREA_CD'],
            style=("background-color: white; color: #333333; font-family: arial; font-size: 12px; padding: 10px;"),
            sticky=True
        )
    )
m.add_child(NIL)
m.keep_in_front(NIL)
folium.LayerControl().add_to(m)
# m
m.save("index.html")

이렇게 마우스를 올리면 우편번호와 권역코드를 확인할 수 있는 툴팁이 추가 되었습니다. 

댓글
Total
Today
Yesterday
공지사항
최근에 올라온 글
글 보관함