국내 코로나19 정보를 Plotly의 공간정보 표현 API인 Choropleth map API를 사용하여 공간정보를 표시해본다. 데이터는 이전 블로그에서 사용한 sorted_df 를 사용할 것이므로 미리 준비하도록 하자.
2021.03.15 - [데이터 시각화] - 국내 시.도별 코로나19 확진자 발생 현황 데이터 시각화( 공공데이터포탈 Open API )
다음으로 공간정보의 이해를 위해 아래내용들을 숙지하길 바란다.
1. 지리정보시스템, GIS ( Geographic Information System )
일반 지도와 같은 지형정보와 함께 지하시설물 등 관련 정보를 인공위성으로 수집, 컴퓨터로 작성해 검색, 분석할 수 있도록 한 복합적인 지리정보시스템이다. 국토계획 및 도시계획, 수자원관리, 통신 · 교통망 가설, 토지관리, 지하매설물 설치 등의 분야에서 필요성이 강조되고 있다.
GIS가 운용되는 분야는 구체적으로 기상항공 정보분석, 상 · 하수도망, 통신망, 전력망, 도시가스망, 도로 등 지상 · 지하 시설물 설치 및 관리, 공장부지, 농작물 재배지역, 산업단지선정 등이다.
출처: 네이버 지식백과
2. Shapefile
shapefile은 GIS를 표현하기 위한 지리정보를 담고 있는 데이터 포맷이다. shapefile 포맷은 지역 위치와 해당 지역의 속성 정보를 담고 있다. .shp .shx .dbf 파일 포맷으로 구성된다.
출처: 위키백과
3. geoJson
GeoJSON(지오제이슨)은 위치정보를 갖는 점을 기반으로 체계적으로 지형을 표현하기 위해 설계된 개방형 공개 표준 형식이다.
출처: 위키백과
Plotly의 Choropleth map은 geoJson 내용을 사용한다. geoJson은 shapefile로 만들어 지는데 국토교통부의 국가공간정보포털이나 민간 운영 블로그인 GIS DELELOPER 에서 다운로드 받을 수 있다.
shapefile을 geoJson으로 변경하기 위해서는 약간의 노력이 필요하며 본 블로그에서는 다루지 않는다. 구글에서 검색하면 많은 블로그에서 관련 내용을 다루고 있으니 참고바란다.
본 블로그에서 사용된 국내 시.도 geoJson파일
geoJson 파일 구조
{
"type":"FeatureCollection",
"bbox":[126.11294936746019,33.23173832249056,129.64960727081376,38.574130814660194],
"features":[
{
"type":"Feature",
"geometry":{
"type":"Polygon",
"coordinates":[
[[128.54880989370014,38.301950820518755],
[128.60765942875406,38.15215810015938],
[128.8786226699225,37.8294251483049],
[129.05505018456222,37.675224044111836],
.. 생략
,[128.54880989370014,38.301950820518755]]
]
},
"properties":{
"CTPRVN_CD":"42",
"CTP_ENG_NM":"Gangwon-do",
"CTP_KOR_NM":"강원도"
}
},
.. 이하 16개 시.도 Feature ..
]
}
- features에 각 시.도 데이터가 리스트로 담겨있다.
- plotly는 각 features 데이터에 id 키을 찾아 맵을 지정한다. id가 없으므로 이를 생성해야 하는데 sorted_df의 gubun 값과 위 geoJson의 "properties"에 정의된 "CTP_KOR_NM"을 매핑하여 id를 생성한다.
- featureidkey로 id 값을 갖는 key값을 지정할 수 있다.
데이터 로드 및 Map id 생성
with open('geojson/korea_geojson2.geojson', encoding='UTF-8') as f:
data = json.load(f)
for x in data['features']:
x['id'] = x['properties']['CTP_KOR_NM']
for idx, _ in enumerate(data['features']):
print(data['features'][idx]['id'])
# OUTPUT
강원도
경기도
경상남도
경상북도
광주광역시
대구광역시
대전광역시
부산광역시
서울특별시
세종특별자치시
울산광역시
인천광역시
전라남도
전라북도
제주특별자치도
충청남도
충청북도
- json으로 로드 후 id 키값에 CTP_KOR_NM 값을 넣었다.
- 잘 생성되었는지 결과값을 출력해본다.
sorted_df.gubun.unique()
# OUTPUT
array(['경기', '서울', '경남', '인천', '강원', '충북', '충남', '전북', '대구', '부산', '경북',
'제주', '세종', '대전', '울산', '광주', '전남'], dtype=object)
- sorted_df 의 gubun값은 geoJson의 id 값과 다르다. 매칭 되는 값을 매퍼로 만든 후 sorted_df에 geo_region 컬럼 값으로 데이터를 생성해보자.
mapper = [
('경기', '경기도'),
('서울', '서울특별시'),
('충북', '충청북도'),
('인천', '인천광역시'),
('충남', '충청남도'),
('광주', '광주광역시'),
('부산', '부산광역시'),
('강원', '강원도'),
('전남', '전라남도'),
('대구', '대구광역시'),
('전북', '전라북도'),
('울산', '울산광역시'),
('제주', '제주특별자치도'),
('경북', '경상북도'),
('세종', '세종특별자치시'),
('경남', '경상남도'),
('대전', '대전광역시'),
]
get_region = lambda gubun: [x[1] for x in mapper if x[0] == gubun][0]
sorted_df['geo_region'] = sorted_df.gubun.apply(get_region)
sorted_df
# OUTPUT
createdt deathcnt defcnt gubun incdec isolclearcnt isolingcnt localocccnt overflowcnt qurrate dt geo_region
9 2021-03-15 09:55:40.205 517 26157 경기 161 23355 2285 161 0 197.41 2021-03-15 경기도
17 2021-03-15 09:55:40.203 407 30061 서울 112 27526 2128 112 0 308.84 2021-03-15 서울특별시
2 2021-03-15 09:55:40.206 14 2438 경남 31 2185 239 31 0 72.53 2021-03-15 경상남도
14 2021-03-15 09:55:40.204 56 4726 인천 18 4423 247 18 0 159.87 2021-03-15 인천광역시
8 2021-03-15 09:55:40.205 42 2033 강원 10 1820 171 10 0 131.97 2021-03-15 강원도
7 2021-03-15 09:55:40.205 60 1978 충북 9 1711 207 9 0 123.67 2021-03-15 충청북도
6 2021-03-15 09:55:40.205 35 2541 충남 6 2400 106 5 1 119.72 2021-03-15 충청남도
5 2021-03-15 09:55:40.205 56 1276 전북 5 1142 78 4 1 70.21 2021-03-15 전라북도
15 2021-03-15 09:55:40.204 215 8726 대구 5 8429 82 4 1 358.14 2021-03-15 대구광역시
16 2021-03-15 09:55:40.203 114 3421 부산 4 3123 184 4 0 100.27 2021-03-15 부산광역시
3 2021-03-15 09:55:40.206 72 3373 경북 3 3191 110 3 0 126.69 2021-03-15 경상북도
1 2021-03-15 09:55:40.206 1 609 제주 3 575 33 3 0 90.79 2021-03-15 제주특별자치도
10 2021-03-15 09:55:40.205 1 246 세종 2 231 14 2 0 71.86 2021-03-15 세종특별자치시
12 2021-03-15 09:55:40.204 15 1205 대전 2 1175 15 2 0 81.74 2021-03-15 대전광역시
11 2021-03-15 09:55:40.204 37 1092 울산 2 970 85 1 1 95.20 2021-03-15 울산광역시
13 2021-03-15 09:55:40.204 21 2175 광주 1 2075 79 1 0 149.31 2021-03-15 광주광역시
4 2021-03-15 09:55:40.206 8 904 전남 1 827 69 0 1 48.48 2021-03-15 전라남도
- DataFrame에 geoJson에 정의된 id와 매핑되는 컬럼을 만들었다. 이제 plotly API를 호출해보자.
plotly choropleth_mapbox
fig = px.choropleth_mapbox(
sorted_df,
geojson=data,
locations='geo_region',
color='localocccnt',
color_continuous_scale=px.colors.sequential.Redor,
# featureidkey="properties.CTP_KOR_NM", # featureidkey를 사용하여 id 값을 갖는 키값 지정
mapbox_style="carto-positron",
zoom=5.5,
center = {"lat": 35.757981, "lon": 127.661132},
opacity=0.6,
labels={'localocccnt':'확진자 수'}
)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
- geojson 파라미터에 geoJson을 로드한 json파일을 넣는다.
- color 파라미터에 맵에서 표현할 DataFrame의 컬럼을 넣는다. localocccnt는 확진자 수 이다.
- mapbox_style 파라미터로 맵 형태를 다양하게 바꿀 수 있다.
결과
- 자세히 보면 부산을 비롯하여 몇 군데 색상표현이 누락된 곳이 있다. 이는 geoJson파일의 로케이션 정보에 오류로 인해 발생한 것이다.
mapbox_style