(12주차) 11월24일, 11월29일
Choropleth Map
- 강의영상
- imports
- Choropleth Map 시작: 지역구그리기
- json 파일 뜯어보기
- 전주시에 해당하는 구만 시각화해보자.
- 덕진구 vs 완산구 시각화
- 대한민국 인구수
- plotly 맛보기
-
(1/3) Choropleth Map (1) :: 11월24일강의-1
-
(2/3) Choropleth Map (2) :: 11월24일강의-2
-
(3/3) Choropleth Map (3) :: 11월24일강의-3
-
(4/6) folium: 한국지도 그리기 :: 11월29일강의-1
-
(5/6) plotly: 한국지도 그리기 (1) :: 11월29일강의-2
-
(6/6) plotly: 한국지도 그리기 (2) :: 11월29일강의-3
import folium
import pandas as pd
import json
import requests
local_distriction_jsonurl='https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-municipalities-2018-geo.json'
global_distriction_jsonurl='https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-provinces-2018-geo.json'
local_dict = json.loads(requests.get(local_distriction_jsonurl).text)
global_dict = json.loads(requests.get(global_distriction_jsonurl).text)
- 잔뜩 복잡해보이는 파일이 일단 dict형태로 있음.
-
지역을 global scale로 시각화.
m = folium.Map([36,128],zoom_start=7,scrollWheelZoom=False)
folium.Choropleth(geo_data=global_dict).add_to(m)
#m
-
지역을 local scale로 시각화
m = folium.Map([36,128],zoom_start=7,scrollWheelZoom=False)
folium.Choropleth(geo_data=local_dict).add_to(m)
#m
-
local_dict 를 살펴보자.
local_dict?
- 4개의 원소를 가지는 딕셔너리
-
원소의 이름은 아래와 같이 확인가능
local_dict.keys()
- type, features, name, crs
-
type, name, crs 부터 살펴보자.
local_dict['type']
local_dict['name']
local_dict['crs']
-
위의 3개는 특별한것이 없어보임. 이제 features를 살펴보자.
_features = local_dict['features']
- 길이가 250인 list
- 250의 리스트는 각각의 구(혹은 군,시)를 의미하는것 같다.
_features[0] # 첫번째 구
- 첫번째 구는 다시 3개의 원소로 구성된 딕셔너리
_features[0]['type']
_features[0]['geometry']
_features[0]['properties']
-
_features[0]
는 다시 dict 타입이다. 길이가3, 키는 type, geometry, properties 이다. 이중에서 type은 별 정보가 없고 geometry에는 멀티폴리곤 지옴에 대한 좌표값이 정리되어있다. 그리고 properties에는 이름, 영문이름, 코드와 같은 정보들이 정리되어 있다.
_features[200]['properties']['name']
-
덕진과와 완산구를 찾아보자.
_lst =[_features[i]['properties']['name'] for i in range(250)]
_lst[:10]
-
전주시완산구, 전주시덕진구가 있다. (도시의 크기에 따라서 구/군/시 등으로 표기됨)
-
전주시완산구, 전주시덕진구가 _lst의 몇번째에 위치할까?
(예비학습) 리스트에서 특정 원소가 위치한 인덱스를 알아내는 방법 (where)
['a','b','c'].index('c')
예비학습끝
_lst.index('전주시덕진구')
_lst.index('전주시완산구')
_lst.index(['전주시완산구','전주시덕진구'])
_keywords=['전주시완산구','전주시덕진구']
[_lst.index(_keywords[i]) for i in range(2)] # 구식프로그래밍
_keywords=['전주시완산구','전주시덕진구']
[_lst.index(i) for i in _keywords] #신식(?)프로그래밍
_keywords=['전주시완산구','전주시덕진구']
[_lst.index(keyword) for keyword in _keywords] # 가독성향상
[_lst.index(keyword) for keyword in ['전주시완산구','전주시덕진구']] # compact
-
local_dict와 형식은 똑같은데 덕진구와 완산구만 포함한 local_dict2를 만들자.
(예비학습) VIEW vs COPY
dict1= {'a':1, 'b':2, 'c':3}
dict1
dict2=dict1
dict2
dict2['b']=5
dict2
dict1
- 또 이상한 일이 생김.
- 이러한 현상을 피하기 위해서는 dict2가 dict1의 카피임을 명시해야 한다. (현재는 dict2가 dict1의 바로가기 아이콘같은 존재)
dict1= {'a':1, 'b':2, 'c':3}
dict2=dict1.copy()
dict2['b']=5
print(dict1)
print(dict2)
- 카피임을 명시하니까 문제가 없어짐
(예비학습끝)
local_dict2 = local_dict.copy()
local_dict2.keys()
_keywords=['전주시완산구','전주시덕진구']
_jj = [_lst.index(keyword) for keyword in _keywords]
_jj
local_dict2['features'] = [local_dict['features'][j] for j in _jj]
m = folium.Map([36,128],zoom_start=7,scrollWheelZoom=False)
folium.Choropleth(geo_data=local_dict2).add_to(m)
#m
-
좀 더 크게 + 위치조정 (전주시를 중심으로)
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=11,scrollWheelZoom=False)
folium.Choropleth(geo_data=local_dict2).add_to(m)
#m
-
아래와 같은 그림에서 덕진구와 완산구의 어떠한 value를 비교하고 싶음 (카카오바이크 등)
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=11,scrollWheelZoom=False)
folium.Choropleth(geo_data=local_dict2).add_to(m)
#m
-
선실습, 후설명
df = pd.DataFrame({'key':['전주시덕진구','전주시완산구'],'value':[20,30]})
df
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=11,scrollWheelZoom=False)
choro = folium.Choropleth(
data=df,
geo_data=local_dict2,
columns=['key','value'],
key_on = 'feature.properties.name'
)
choro.add_to(m)
#m
-
folium.Choropleth
의 사용법
(이해를 위해 필요한 약간의 직관)
- 뭔가 2개의 데이터를 연결해야하는 구조이다. 하나는 df, 다른하나는 json에서 나온 dict (local_dict2) 이다.
- 데이터를 연결하기 위해서는 공유가능한 연결의 매개체가 필요하다. (cbind: row-index를 공유, rbind: colnames공유, merge: 양쪽 데이터프레임에서 같은 이름을 가진 특정 col이 있었음)
- 연결의 매개체는 df와 local_dict2에 각각 존재하는데, df에서는
key
라는 이름의 칼럼으로 저장했었고 local_dict2 에서는local_dict2['features'][?]['properties']['name']
에 있다.
local_dict2['features'][0]['properties']['name']
(사용법)
choro = folium.Choropleth(
data=df, ## data1
geo_data=local_dict2, ## data2 (이 시점에서 폴리곤에 대한 정보가 choro 인스턴스에 전달)
columns=['key','value'], ## data1에서 중요한것들을 나열. 항상 [key,value]의 조합으로 써야한다.
# 이때 ['key','value']는 [data2와의 매개체,지도에서 색의 단계를 표현하는 변수]로 해석가능
key_on = 'feature.properties.name' ## data2에서 중요한것: 즉 data1과의 연결매개체
)
folium.Choropleth
의 key_on
파라메터는 (1) 항상 feature로 시작하고 (2) 이후는에는 local_dict2[’features’][?]
의 하위에 data1과 매칭되는 path를 찾음
local_dict2['features'][0]['properties']['name']
-
당연히 영문이름을 매개체로 쓸 수 있다.
local_dict2['features'][0]['properties']['name_eng']
df = pd.DataFrame({'key':['전주시덕진구','Jeonjusiwansangu'],'value':[20,30]})
df
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=11,scrollWheelZoom=False)
choro = folium.Choropleth(
data=df,
geo_data=local_dict2,
columns=['key','value'],
key_on = 'feature.properties.name'
)
choro.add_to(m)
#m
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=11,scrollWheelZoom=False)
choro = folium.Choropleth(
data=df,
geo_data=local_dict2,
columns=['key','value'],
key_on = 'feature.properties.name_eng'
)
choro.add_to(m)
#m
-
한글이름, 영문이름 뿐만이 아니라 code를 key_on으로 쓸 수 있음.
local_dict2['features'][0]['properties']
local_dict2['features'][1]['properties']
df = pd.DataFrame({'key':['35012','35011'],'value':[20,30]})
df
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=11,scrollWheelZoom=False)
choro = folium.Choropleth(
data=df,
geo_data=local_dict2,
columns=['key','value'],
key_on = 'feature.properties.code'
)
choro.add_to(m)
#m
df=pd.read_csv('https://raw.githubusercontent.com/guebin/2021DV/master/_notebooks/2021-11-22-prov.csv')
df
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=6,scrollWheelZoom=False)
choro= folium.Choropleth(
data=df,
geo_data= global_dict,
columns=['행정구역(시군구)별','총인구수 (명)'],
key_on='feature.properties.name'
)
choro.add_to(m)
#m
df=pd.read_csv('https://raw.githubusercontent.com/guebin/2021DV/master/_notebooks/2021-11-22-muni.csv')
df
m = folium.Map([35.84195368311022, 127.1155556693179],zoom_start=7,scrollWheelZoom=False)
choro= folium.Choropleth(
data=df,
geo_data= local_dict,
columns=['행정구역(시군구)별','총인구수 (명)'],
key_on='feature.properties.name'
)
choro.add_to(m)
#m
import plotly.express as px
from IPython.display import HTML
df = px.data.election()
geojson = px.data.election_geojson()
fig = px.choropleth_mapbox(df, geojson=geojson, color="Bergeron",
locations="district", featureidkey="properties.district",
center={"lat": 45.5517, "lon": -73.7073},
mapbox_style="carto-positron", zoom=9)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
HTML(fig.to_html(include_plotlyjs='cdn',include_mathjax=False, config=dict({'scrollZoom':False})))
#fig.show(config=dict({'scrollZoom':False}))
-
공식홈페이지의 예제를 뜯어보자.
df.head()
- properties의 district 혹은 id를 key로 한다.
-
코드를 다시 관찰하자.
df = px.data.election() ### 일반적인 데이터 프레임
geojson = px.data.election_geojson() ### json파일
fig = px.choropleth_mapbox(df, ### 데이터프레임
geojson=geojson, ### json파일
color="Bergeron", ### df에서 코로플레스의 단계를 표시
locations="district", ### df에 존재하는 연결변수
featureidkey="properties.district", ### json에 존재하는 연결매개체
center={"lat": 45.5517, "lon": -73.7073},
mapbox_style="carto-positron",
zoom=9)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show(config=dict({'scrollZoom':False}))
-
우리의 상황으로 바꾸면
df=pd.read_csv('https://raw.githubusercontent.com/guebin/2021DV/master/_notebooks/2021-11-22-prov.csv')
df
geojson = global_dict ### json파일
fig = px.choropleth_mapbox(df, ### 데이터프레임
geojson=geojson, ### json파일
color="총인구수 (명)", ### df에서 코로플레스의 단계를 표시
locations="행정구역(시군구)별", ### df에 존재하는 연결변수
featureidkey="properties.name", ### json에 존재하는 연결매개체
center={"lat": 35.84195368311022, "lon": 127.1155556693179},
mapbox_style="carto-positron",
zoom=5)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
HTML(fig.to_html(include_plotlyjs='cdn',include_mathjax=False, config=dict({'scrollZoom':False})))