# !conda install -c conda-forge plotly
11wk-2: Choropleth (plotly)
plotly
1. 강의영상
2. Imports
import numpy as np
import pandas as pd
#---#
import plotly.express as px
import json
import requests
3. 에너지사용량 시각화
A. 데이터 불러오기
= json.loads(requests.get('https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-provinces-2018-geo.json').text)
global_dict = json.loads(requests.get('https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-municipalities-2018-geo.json').text)
local_dict #--#
= 'https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/{}.csv'
url = ['Seoul', 'Busan', 'Daegu', 'Incheon',
prov 'Gwangju', 'Daejeon', 'Ulsan', 'Sejongsi',
'Gyeonggi-do', 'Gangwon-do', 'Chungcheongbuk-do',
'Chungcheongnam-do', 'Jeollabuk-do', 'Jeollanam-do',
'Gyeongsangbuk-do', 'Gyeongsangnam-do', 'Jeju-do']
= pd.concat([pd.read_csv(url.format(p+y)).assign(년도=y, 시도=p) for p in prov for y in ['2018', '2019', '2020', '2021']]).reset_index(drop=True)\
df = lambda df: df.년도.astype(int))\
.assign(년도 '년도','시도','지역']).applymap(lambda x: int(str(x).replace(',','')))\
.set_index([
.reset_index() df.head()
/tmp/ipykernel_1184324/712135142.py:12: FutureWarning:
DataFrame.applymap has been deprecated. Use DataFrame.map instead.
년도 | 시도 | 지역 | 건물동수 | 연면적 | 에너지사용량(TOE)/전기 | 에너지사용량(TOE)/도시가스 | 에너지사용량(TOE)/지역난방 | |
---|---|---|---|---|---|---|---|---|
0 | 2018 | Seoul | 종로구 | 17929 | 9141777 | 64818 | 82015 | 111 |
1 | 2018 | Seoul | 중구 | 10598 | 10056233 | 81672 | 75260 | 563 |
2 | 2018 | Seoul | 용산구 | 17201 | 10639652 | 52659 | 85220 | 12043 |
3 | 2018 | Seoul | 성동구 | 14180 | 11631770 | 60559 | 107416 | 0 |
4 | 2018 | Seoul | 광진구 | 21520 | 12054796 | 70609 | 130308 | 0 |
B. 데이터정리
(1)
global_dict
내의 영어이름과 df
의 영어이름이 일치하는지 확인
set(df.시도) == {l['properties']['name_eng'] for l in global_dict['features']}
True
(2)
global_dict
내의 영어이름과 한글이름을 이용해 변환을 위한 dictionary 생성
'properties']['name_eng']:l['properties']['name'] for l in global_dict['features']} {l[
{'Seoul': '서울특별시',
'Busan': '부산광역시',
'Daegu': '대구광역시',
'Incheon': '인천광역시',
'Gwangju': '광주광역시',
'Daejeon': '대전광역시',
'Ulsan': '울산광역시',
'Sejongsi': '세종특별자치시',
'Gyeonggi-do': '경기도',
'Gangwon-do': '강원도',
'Chungcheongbuk-do': '충청북도',
'Chungcheongnam-do': '충청남도',
'Jeollabuk-do': '전라북도',
'Jeollanam-do': '전라남도',
'Gyeongsangbuk-do': '경상북도',
'Gyeongsangnam-do': '경상남도',
'Jeju-do': '제주특별자치도'}
(3)
df
에 변환을 수행하여 영어지명을 한글지명으로 변환
df.assign(= lambda df: df.시도.map({l['properties']['name_eng']:l['properties']['name'] for l in global_dict['features']})
시도 )
년도 | 시도 | 지역 | 건물동수 | 연면적 | 에너지사용량(TOE)/전기 | 에너지사용량(TOE)/도시가스 | 에너지사용량(TOE)/지역난방 | |
---|---|---|---|---|---|---|---|---|
0 | 2018 | 서울특별시 | 종로구 | 17929 | 9141777 | 64818 | 82015 | 111 |
1 | 2018 | 서울특별시 | 중구 | 10598 | 10056233 | 81672 | 75260 | 563 |
2 | 2018 | 서울특별시 | 용산구 | 17201 | 10639652 | 52659 | 85220 | 12043 |
3 | 2018 | 서울특별시 | 성동구 | 14180 | 11631770 | 60559 | 107416 | 0 |
4 | 2018 | 서울특별시 | 광진구 | 21520 | 12054796 | 70609 | 130308 | 0 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
995 | 2019 | 제주특별자치도 | 서귀포시 | 34729 | 7233931 | 34641 | 1306 | 0 |
996 | 2020 | 제주특별자치도 | 제주시 | 66504 | 19819923 | 99212 | 22179 | 0 |
997 | 2020 | 제주특별자치도 | 서귀포시 | 34880 | 7330040 | 35510 | 1639 | 0 |
998 | 2021 | 제주특별자치도 | 제주시 | 67053 | 20275738 | 103217 | 25689 | 0 |
999 | 2021 | 제주특별자치도 | 서귀포시 | 35230 | 7512206 | 37884 | 2641 | 0 |
1000 rows × 8 columns
(4)
local_dict
와 global_dict
의 지명정보를 정리하여 데이터프레임으로 만듦
# 예비학습
pd.DataFrame('X':100,'y':0},
[{'X':101,'y':1}]
{ )
X | y | |
---|---|---|
0 | 100 | 0 |
1 | 101 | 1 |
#
= pd.DataFrame([l['properties'] for l in global_dict['features']])
df_global df_global
name | base_year | name_eng | code | |
---|---|---|---|---|
0 | 서울특별시 | 2018 | Seoul | 11 |
1 | 부산광역시 | 2018 | Busan | 21 |
2 | 대구광역시 | 2018 | Daegu | 22 |
3 | 인천광역시 | 2018 | Incheon | 23 |
4 | 광주광역시 | 2018 | Gwangju | 24 |
5 | 대전광역시 | 2018 | Daejeon | 25 |
6 | 울산광역시 | 2018 | Ulsan | 26 |
7 | 세종특별자치시 | 2018 | Sejongsi | 29 |
8 | 경기도 | 2018 | Gyeonggi-do | 31 |
9 | 강원도 | 2018 | Gangwon-do | 32 |
10 | 충청북도 | 2018 | Chungcheongbuk-do | 33 |
11 | 충청남도 | 2018 | Chungcheongnam-do | 34 |
12 | 전라북도 | 2018 | Jeollabuk-do | 35 |
13 | 전라남도 | 2018 | Jeollanam-do | 36 |
14 | 경상북도 | 2018 | Gyeongsangbuk-do | 37 |
15 | 경상남도 | 2018 | Gyeongsangnam-do | 38 |
16 | 제주특별자치도 | 2018 | Jeju-do | 39 |
= pd.DataFrame([l['properties'] for l in local_dict['features']])
df_local df_local
name | base_year | name_eng | code | |
---|---|---|---|---|
0 | 종로구 | 2018 | Jongno-gu | 11010 |
1 | 중구 | 2018 | Jung-gu | 11020 |
2 | 용산구 | 2018 | Yongsan-gu | 11030 |
3 | 성동구 | 2018 | Seongdong-gu | 11040 |
4 | 광진구 | 2018 | Gwangjin-gu | 11050 |
... | ... | ... | ... | ... |
245 | 함양군 | 2018 | Hamyang-gun | 38380 |
246 | 거창군 | 2018 | Geochang-gun | 38390 |
247 | 합천군 | 2018 | Hapcheon-gun | 38400 |
248 | 제주시 | 2018 | Jeju-si | 39010 |
249 | 서귀포시 | 2018 | Seogwipo-si | 39020 |
250 rows × 4 columns
(5)
df_local
에서 “전주시완산구”와 같이 정리된 지명들을 “완산구”로 변환
'name')\
df_local.set_index(
.rename('시')[-1] for name in df_local['name'] if ('시' in name) and ('구' in name) and (len(name)>3)}
{name:name.split( ).reset_index()
name | base_year | name_eng | code | |
---|---|---|---|---|
0 | 종로구 | 2018 | Jongno-gu | 11010 |
1 | 중구 | 2018 | Jung-gu | 11020 |
2 | 용산구 | 2018 | Yongsan-gu | 11030 |
3 | 성동구 | 2018 | Seongdong-gu | 11040 |
4 | 광진구 | 2018 | Gwangjin-gu | 11050 |
... | ... | ... | ... | ... |
245 | 함양군 | 2018 | Hamyang-gun | 38380 |
246 | 거창군 | 2018 | Geochang-gun | 38390 |
247 | 합천군 | 2018 | Hapcheon-gun | 38400 |
248 | 제주시 | 2018 | Jeju-si | 39010 |
249 | 서귀포시 | 2018 | Seogwipo-si | 39020 |
250 rows × 4 columns
(6)
df_local
과 df_global
의 정보를 정리하여 merge, 합쳐진 정보를 df_json
에 저장
= df_local.set_index('name')\
df_json
.rename('시')[-1] for name in df_local['name'] if ('시' in name) and ('구' in name) and (len(name)>3)}
{name:name.split(\
).reset_index()'base_year','name_eng'],axis=1)\
.drop(['name':'name_local','code':'code_local'},axis=1)\
.rename({= lambda df: df['code_local'].str[:2])\
.assign(code 'base_year','name_eng'],axis=1))
.merge(df_global.drop([ df_json
name_local | code_local | code | name | |
---|---|---|---|---|
0 | 종로구 | 11010 | 11 | 서울특별시 |
1 | 중구 | 11020 | 11 | 서울특별시 |
2 | 용산구 | 11030 | 11 | 서울특별시 |
3 | 성동구 | 11040 | 11 | 서울특별시 |
4 | 광진구 | 11050 | 11 | 서울특별시 |
... | ... | ... | ... | ... |
245 | 함양군 | 38380 | 38 | 경상남도 |
246 | 거창군 | 38390 | 38 | 경상남도 |
247 | 합천군 | 38400 | 38 | 경상남도 |
248 | 제주시 | 39010 | 39 | 제주특별자치도 |
249 | 서귀포시 | 39020 | 39 | 제주특별자치도 |
250 rows × 4 columns
(7)
df_json과 df의 정보를 merge하기 위하여 ’서울특별시-종로구’와 같은 형식으로 공통열을 각각 생성. 생성된 공통열의 원소가 일치하는지 비교
= df.assign(
df_left = lambda df: df.시도.map({l['properties']['name_eng']:l['properties']['name'] for l in global_dict['features']})
시도 = lambda df: df.시도 + '-' + df.지역)
).assign(on df_left
년도 | 시도 | 지역 | 건물동수 | 연면적 | 에너지사용량(TOE)/전기 | 에너지사용량(TOE)/도시가스 | 에너지사용량(TOE)/지역난방 | on | |
---|---|---|---|---|---|---|---|---|---|
0 | 2018 | 서울특별시 | 종로구 | 17929 | 9141777 | 64818 | 82015 | 111 | 서울특별시-종로구 |
1 | 2018 | 서울특별시 | 중구 | 10598 | 10056233 | 81672 | 75260 | 563 | 서울특별시-중구 |
2 | 2018 | 서울특별시 | 용산구 | 17201 | 10639652 | 52659 | 85220 | 12043 | 서울특별시-용산구 |
3 | 2018 | 서울특별시 | 성동구 | 14180 | 11631770 | 60559 | 107416 | 0 | 서울특별시-성동구 |
4 | 2018 | 서울특별시 | 광진구 | 21520 | 12054796 | 70609 | 130308 | 0 | 서울특별시-광진구 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
995 | 2019 | 제주특별자치도 | 서귀포시 | 34729 | 7233931 | 34641 | 1306 | 0 | 제주특별자치도-서귀포시 |
996 | 2020 | 제주특별자치도 | 제주시 | 66504 | 19819923 | 99212 | 22179 | 0 | 제주특별자치도-제주시 |
997 | 2020 | 제주특별자치도 | 서귀포시 | 34880 | 7330040 | 35510 | 1639 | 0 | 제주특별자치도-서귀포시 |
998 | 2021 | 제주특별자치도 | 제주시 | 67053 | 20275738 | 103217 | 25689 | 0 | 제주특별자치도-제주시 |
999 | 2021 | 제주특별자치도 | 서귀포시 | 35230 | 7512206 | 37884 | 2641 | 0 | 제주특별자치도-서귀포시 |
1000 rows × 9 columns
= df_json.assign(on = lambda df: df.name + '-' + df.name_local)\
df_right 'name_local','name'],axis=1)
.drop([ df_right
code_local | code | on | |
---|---|---|---|
0 | 11010 | 11 | 서울특별시-종로구 |
1 | 11020 | 11 | 서울특별시-중구 |
2 | 11030 | 11 | 서울특별시-용산구 |
3 | 11040 | 11 | 서울특별시-성동구 |
4 | 11050 | 11 | 서울특별시-광진구 |
... | ... | ... | ... |
245 | 38380 | 38 | 경상남도-함양군 |
246 | 38390 | 38 | 경상남도-거창군 |
247 | 38400 | 38 | 경상남도-합천군 |
248 | 39010 | 39 | 제주특별자치도-제주시 |
249 | 39020 | 39 | 제주특별자치도-서귀포시 |
250 rows × 3 columns
set(df_left.on) == set(df_right.on)
False
set(df_right.on) - set(df_left.on)
{'인천광역시-남구'}
set(df_left.on) - set(df_right.on)
{'인천광역시-미추홀구'}
(8)
아래의 기사를 살펴보고 지역명을 적절히 변환
'on')\
df_right.set_index('인천광역시-남구':'인천광역시-미추홀구'})\
.rename({ .reset_index()
on | code_local | code | |
---|---|---|---|
0 | 서울특별시-종로구 | 11010 | 11 |
1 | 서울특별시-중구 | 11020 | 11 |
2 | 서울특별시-용산구 | 11030 | 11 |
3 | 서울특별시-성동구 | 11040 | 11 |
4 | 서울특별시-광진구 | 11050 | 11 |
... | ... | ... | ... |
245 | 경상남도-함양군 | 38380 | 38 |
246 | 경상남도-거창군 | 38390 | 38 |
247 | 경상남도-합천군 | 38400 | 38 |
248 | 제주특별자치도-제주시 | 39010 | 39 |
249 | 제주특별자치도-서귀포시 | 39020 | 39 |
250 rows × 3 columns
(9)
데이터프레임을 결합
= df_left.merge(
df2 'on')\
df_right.set_index('인천광역시-남구':'인천광역시-미추홀구'})\
.rename({
.reset_index()'on'],axis=1) ).drop([
C. 시각화 (2018년도 전기에너지 사용량)
df2
년도 | 시도 | 지역 | 건물동수 | 연면적 | 에너지사용량(TOE)/전기 | 에너지사용량(TOE)/도시가스 | 에너지사용량(TOE)/지역난방 | code_local | code | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 2018 | 서울특별시 | 종로구 | 17929 | 9141777 | 64818 | 82015 | 111 | 11010 | 11 |
1 | 2019 | 서울특별시 | 종로구 | 17851 | 9204140 | 63492 | 76653 | 799 | 11010 | 11 |
2 | 2020 | 서울특별시 | 종로구 | 17638 | 9148895 | 60123 | 71263 | 912 | 11010 | 11 |
3 | 2021 | 서울특별시 | 종로구 | 22845 | 18551145 | 125179 | 117061 | 0 | 11010 | 11 |
4 | 2018 | 서울특별시 | 중구 | 10598 | 10056233 | 81672 | 75260 | 563 | 11020 | 11 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
995 | 2021 | 제주특별자치도 | 제주시 | 67053 | 20275738 | 103217 | 25689 | 0 | 39010 | 39 |
996 | 2018 | 제주특별자치도 | 서귀포시 | 34154 | 6914685 | 34470 | 1597 | 0 | 39020 | 39 |
997 | 2019 | 제주특별자치도 | 서귀포시 | 34729 | 7233931 | 34641 | 1306 | 0 | 39020 | 39 |
998 | 2020 | 제주특별자치도 | 서귀포시 | 34880 | 7330040 | 35510 | 1639 | 0 | 39020 | 39 |
999 | 2021 | 제주특별자치도 | 서귀포시 | 35230 | 7512206 | 37884 | 2641 | 0 | 39020 | 39 |
1000 rows × 10 columns
px.choropleth_mapbox(= local_dict,
geojson = 'properties.code',
featureidkey = df2.query('년도 == 2018'),
data_frame = 'code_local',
locations = '에너지사용량(TOE)/전기',
color = ['시도','지역'],
hover_data #---#
="carto-positron",
mapbox_style={"lat": 36, "lon": 127.5},
center=6,
zoom=800,
height=800
width )