import numpy as np
import pandas as pd
import plotly.express as px
import plotly.io as pio
import json
import requests
import pickle
supp-5: 기말고사 풀이
= "plotly"
pd.options.plotting.backend = "plotly_white" pio.templates.default
1. NYCTaxi 자료 분석 (dashboard) – 100점
아래는 NYCTaxi자료에서 기본적인 전처리를 수행한 데이터프레임이다.
= pd.read_csv("https://raw.githubusercontent.com/guebin/DV2023/main/posts/NYCTaxi.csv").assign(
df = lambda df: np.log(df.trip_duration),
log_trip_duration = lambda df: df.pickup_datetime.apply(pd.to_datetime),
pickup_datetime = lambda df: df.dropoff_datetime.apply(pd.to_datetime),
dropoff_datetime = lambda df: np.sqrt((df.pickup_latitude-df.dropoff_latitude)**2 + (df.pickup_longitude-df.dropoff_longitude)**2),
dist #---#
= lambda df: df.vendor_id.map({1:'A',2:'B'})
vendor_id
).assign(= lambda df: df.dist / df.trip_duration,
speed = lambda df: df.pickup_datetime.dt.hour,
pickup_hour = lambda df: df.dropoff_datetime.dt.hour,
dropoff_hour = lambda df: df.pickup_datetime.dt.dayofweek
dayofweek
)= df[::100].reset_index(drop=True) df_small
주어진 자료를 이용하여 (1)-(3)에 해당하는 시각화를 대시보드로 구현하고, 홈페이지를 남겨라. 답안 예시는 아래와 같다.
(답안예시)
주의사항
- 제출시간 이후에 대시보드 생성을 시도할 경우 부정행위로 간주하여 기말고사 전체를 0점처리함. (git에 기록남아있음)
- 대시보드 구현이 되어있지 않은 경우 0점 처리함
xaxis
,yaxis
,lengend
등을 조정하는 것이 문제의도임. (구현되어있지 않으면 0점처리함)
힌트
아래와 같은 양식으로 qmd를 만들면 그림이 중복되어 출력되는 문제1가 발생하지 않음.
1 현재 quarto dashboard가 불완전(정식버전이 아니라 prereleased version임)하여 생기는 버그인듯합니다
NYCTaxi.qmd
---
title: "NYCTaxi"
author: "최규빈"
format: dashboard
execute:
enabled: true
cache: false
freeze: false
---
```{python}
#| output: false
# 여기에 온갖코드를 넣음.
# 1-(1),(2),(3) 에 대응하는 plotly figure를 아래와 같은 이름으로 저장
# fig1 = ...
# fig2 = ...
# fig3 = ...
```
# 기말고사1-(1),(2)
```{python}
#| title: 요일,시간에 따른 평균속력 시각화
fig1.show()
```
```{python}
#| title: 요일,시간에 따른 평균이동거리 시각화
fig2.show()
```
# 기말고사1-(3)
```{python}
#| title: 속력별 경로 시각화
fig3.show()
```
(1)
요일,시간에 따른 평균속력 시각화 – 25점
자료 df
에서 시간에 따른 평균속력을 구하고 이를 대시보드에 시각화하라.
README
- 요일은
{0:'월',1:'화',2:'수',3:'목',4:'금',5:'토',6:'일'}
의 규칙에 따라 변환할 것
(2)
요일,시간에 따른 평균이동거리 시각화 – 25점
자료 df
에서 시간에 따른 평균이동거리를 구하고 이를 대시보드에 시각화하라.
README
- 요일은
{0:'월',1:'화',2:'수',3:'목',4:'금',5:'토',6:'일'}
의 규칙에 따라 변환할 것
(3)
속력별 경로시각화 – 50점
자료 df_small
에서 속력을 quatile에 따라 4개의 구간으로 나누고, 구간별 이동경로를 대시보드에 시각화하라.
README
- Zoom = 11 로 설정할것. Figure의 width, height는 설정하지 말것
- 기타 설정값에 대해서는 궁금한것이 있다면 질문할 것
힌트:
힌트1: 아래의 코드를 관찰하세요.
= pd.Series([1,1,2,2,3,3,4,4])
speed print(pd.qcut(speed,4))
print(pd.qcut(speed,4,labels=['매우느림','조금느림','조금빠름','매우빠름']))
0 (0.999, 1.75]
1 (0.999, 1.75]
2 (1.75, 2.5]
3 (1.75, 2.5]
4 (2.5, 3.25]
5 (2.5, 3.25]
6 (3.25, 4.0]
7 (3.25, 4.0]
dtype: category
Categories (4, interval[float64, right]): [(0.999, 1.75] < (1.75, 2.5] < (2.5, 3.25] < (3.25, 4.0]]
0 매우느림
1 매우느림
2 조금느림
3 조금느림
4 조금빠름
5 조금빠름
6 매우빠름
7 매우빠름
dtype: category
Categories (4, object): ['매우느림' < '조금느림' < '조금빠름' < '매우빠름']
힌트2: 1-(3)에 해당하는 그림을 fig3로 저장한후 아래의 코드를 관찰하세요
for i in range(150):
print(fig3.data[i].mode)
이를 이용하여 legend를 수정하는 방법을 생각해보세요.
(풀이)
2. 에너지사용량 (지리정보시각화) – 50점
아래는 대한민국의 행정구역을 나타내는 json
파일과 2018-2021 기간동안의 에너지사용량이 저장된 url들이다.
# Json
https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-provinces-2018-geo.json
https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-municipalities-2018-geo.json
# DataFrame
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2018.csv
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2019.csv
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2020.csv
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2021.csv
...
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Busan2018.csv
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Busan2019.csv
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Busan2020.csv
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Busan2021.csv
주어진 자료를 활용하여 아래의 물음에 답하라.
주의사항
- ‘전주시완산구’,’완산구’와 같은 지역명은 ’전주시-완산구’와 같은 양식으로 정리하라.
- 인천광역시 남구는 새로운 이름인 미추홀구로 변경하라.
힌트
문제가 되는 지역명을 정리하면 아래와 같다.
= pd.Series(['인천광역시-미추홀구',
s '경기도-고양시-덕양구','경기도-고양시-일산동구','경기도-고양시-일산서구',
'경기도-성남시-분당구','경기도-성남시-수정구','경기도-성남시-중원구',
'경기도-수원시-권선구','경기도-수원시-영통구', '경기도-수원시-장안구', '경기도-수원시-팔달구',
'경기도-안산시-단원구', '경기도-안산시-상록구',
'경기도-안양시-동안구', '경기도-안양시-만안구',
'경기도-용인시-기흥구', '경기도-용인시-수지구', '경기도-용인시-처인구',
'경상남도-창원시-마산합포구', '경상남도-창원시-마산회원구', '경상남도-창원시-성산구', '경상남도-창원시-의창구', '경상남도-창원시-진해구',
'경상북도-포항시-남구', '경상북도-포항시-북구',
'전라북도-전주시-덕진구', '전라북도-전주시-완산구',
'충청남도-천안시-동남구', '충청남도-천안시-서북구',
'충청북도-청주시-상당구', '충청북도-청주시-서원구', '충청북도-청주시-청원구', '충청북도-청주시-흥덕구'])
(사전풀이)
= {f'{prov}-{district}':f'{prov}-{city}-{district}' for prov,city,district in s[1:].str.split('-')}
dct1 = {f'{prov}-{city}{district}':f'{prov}-{city}-{district}' for prov,city,district in s[1:].str.split('-')}
dct2 '인천광역시-남구'] = '인천광역시-미추홀구' dct2[
print(dct1)
{'경기도-덕양구': '경기도-고양시-덕양구', '경기도-일산동구': '경기도-고양시-일산동구', '경기도-일산서구': '경기도-고양시-일산서구', '경기도-분당구': '경기도-성남시-분당구', '경기도-수정구': '경기도-성남시-수정구', '경기도-중원구': '경기도-성남시-중원구', '경기도-권선구': '경기도-수원시-권선구', '경기도-영통구': '경기도-수원시-영통구', '경기도-장안구': '경기도-수원시-장안구', '경기도-팔달구': '경기도-수원시-팔달구', '경기도-단원구': '경기도-안산시-단원구', '경기도-상록구': '경기도-안산시-상록구', '경기도-동안구': '경기도-안양시-동안구', '경기도-만안구': '경기도-안양시-만안구', '경기도-기흥구': '경기도-용인시-기흥구', '경기도-수지구': '경기도-용인시-수지구', '경기도-처인구': '경기도-용인시-처인구', '경상남도-마산합포구': '경상남도-창원시-마산합포구', '경상남도-마산회원구': '경상남도-창원시-마산회원구', '경상남도-성산구': '경상남도-창원시-성산구', '경상남도-의창구': '경상남도-창원시-의창구', '경상남도-진해구': '경상남도-창원시-진해구', '경상북도-남구': '경상북도-포항시-남구', '경상북도-북구': '경상북도-포항시-북구', '전라북도-덕진구': '전라북도-전주시-덕진구', '전라북도-완산구': '전라북도-전주시-완산구', '충청남도-동남구': '충청남도-천안시-동남구', '충청남도-서북구': '충청남도-천안시-서북구', '충청북도-상당구': '충청북도-청주시-상당구', '충청북도-서원구': '충청북도-청주시-서원구', '충청북도-청원구': '충청북도-청주시-청원구', '충청북도-흥덕구': '충청북도-청주시-흥덕구'}
print(dct2)
{'경기도-고양시덕양구': '경기도-고양시-덕양구', '경기도-고양시일산동구': '경기도-고양시-일산동구', '경기도-고양시일산서구': '경기도-고양시-일산서구', '경기도-성남시분당구': '경기도-성남시-분당구', '경기도-성남시수정구': '경기도-성남시-수정구', '경기도-성남시중원구': '경기도-성남시-중원구', '경기도-수원시권선구': '경기도-수원시-권선구', '경기도-수원시영통구': '경기도-수원시-영통구', '경기도-수원시장안구': '경기도-수원시-장안구', '경기도-수원시팔달구': '경기도-수원시-팔달구', '경기도-안산시단원구': '경기도-안산시-단원구', '경기도-안산시상록구': '경기도-안산시-상록구', '경기도-안양시동안구': '경기도-안양시-동안구', '경기도-안양시만안구': '경기도-안양시-만안구', '경기도-용인시기흥구': '경기도-용인시-기흥구', '경기도-용인시수지구': '경기도-용인시-수지구', '경기도-용인시처인구': '경기도-용인시-처인구', '경상남도-창원시마산합포구': '경상남도-창원시-마산합포구', '경상남도-창원시마산회원구': '경상남도-창원시-마산회원구', '경상남도-창원시성산구': '경상남도-창원시-성산구', '경상남도-창원시의창구': '경상남도-창원시-의창구', '경상남도-창원시진해구': '경상남도-창원시-진해구', '경상북도-포항시남구': '경상북도-포항시-남구', '경상북도-포항시북구': '경상북도-포항시-북구', '전라북도-전주시덕진구': '전라북도-전주시-덕진구', '전라북도-전주시완산구': '전라북도-전주시-완산구', '충청남도-천안시동남구': '충청남도-천안시-동남구', '충청남도-천안시서북구': '충청남도-천안시-서북구', '충청북도-청주시상당구': '충청북도-청주시-상당구', '충청북도-청주시서원구': '충청북도-청주시-서원구', '충청북도-청주시청원구': '충청북도-청주시-청원구', '충청북도-청주시흥덕구': '충청북도-청주시-흥덕구', '인천광역시-남구': '인천광역시-미추홀구'}
= 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 = pd.DataFrame([l['properties'] for l in global_dict['features']]).drop(['base_year','name_eng'],axis=1)
df_global = pd.DataFrame([l['properties'] for l in local_dict['features']]).drop(['base_year','name_eng'],axis=1)\
df_local 'name':'name_local','code':'code_local'},axis=1)\
.rename({= lambda df: df.code_local.str[:2])
.assign(code = pd.merge(df_local,df_global)\
df_json = lambda df: df['name'] + '-' + df['name_local'])\
.assign(on 'on').rename(dct2).reset_index()\
.set_index('name_local','name'],axis=1)
.drop([ df_json
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
= '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()= lambda df: df.시도.map({l['properties']['name_eng']:l['properties']['name'] for l in global_dict['features']}))\
.assign(시도 = lambda df: df.시도 + '-' + df.지역)\
.assign(on 'on').rename(dct1).reset_index()\
.set_index('지역','시도'],axis=1)
.drop(['df',df.head())
display(= pd.merge(df_json,df).assign(
df2 = lambda df: df['on'].str.split('-').str[0],
시도 = lambda df: df['on'].str.split('-').str[1:].str.join('-')
지역 'on',axis=1).set_index(['시도','지역']).reset_index()
).drop('df2[200:210]',df2[200:210])
display('df2[370:380]',df2[370:380]) display(
/tmp/ipykernel_1181513/17364776.py:9: FutureWarning:
DataFrame.applymap has been deprecated. Use DataFrame.map instead.
'df'
on | 년도 | 건물동수 | 연면적 | 에너지사용량(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 |
'df2[200:210]'
시도 | 지역 | code_local | code | 년도 | 건물동수 | 연면적 | 에너지사용량(TOE)/전기 | 에너지사용량(TOE)/도시가스 | 에너지사용량(TOE)/지역난방 | |
---|---|---|---|---|---|---|---|---|---|---|
200 | 인천광역시 | 동구 | 23020 | 23 | 2018 | 5414 | 2094127 | 10459 | 18695 | 0 |
201 | 인천광역시 | 동구 | 23020 | 23 | 2019 | 5157 | 2084655 | 10098 | 17304 | 0 |
202 | 인천광역시 | 동구 | 23020 | 23 | 2020 | 5096 | 2080453 | 10053 | 17113 | 0 |
203 | 인천광역시 | 동구 | 23020 | 23 | 2021 | 5015 | 2186129 | 10336 | 17704 | 0 |
204 | 인천광역시 | 미추홀구 | 23030 | 23 | 2018 | 28305 | 14479403 | 67154 | 125211 | 2282 |
205 | 인천광역시 | 미추홀구 | 23030 | 23 | 2019 | 27416 | 14515405 | 64888 | 115779 | 3161 |
206 | 인천광역시 | 미추홀구 | 23030 | 23 | 2020 | 26646 | 14476580 | 63312 | 114153 | 2314 |
207 | 인천광역시 | 미추홀구 | 23030 | 23 | 2021 | 25676 | 14830633 | 65328 | 116521 | 5337 |
208 | 인천광역시 | 연수구 | 23040 | 23 | 2018 | 5982 | 15321162 | 70421 | 55642 | 47047 |
209 | 인천광역시 | 연수구 | 23040 | 23 | 2019 | 6019 | 16083104 | 71719 | 53937 | 48334 |
'df2[370:380]'
시도 | 지역 | code_local | code | 년도 | 건물동수 | 연면적 | 에너지사용량(TOE)/전기 | 에너지사용량(TOE)/도시가스 | 에너지사용량(TOE)/지역난방 | |
---|---|---|---|---|---|---|---|---|---|---|
370 | 경기도 | 고양시-일산동구 | 31103 | 31 | 2020 | 11496 | 11754447 | 52447 | 33546 | 50595 |
371 | 경기도 | 고양시-일산동구 | 31103 | 31 | 2021 | 11530 | 11882593 | 53565 | 33855 | 51300 |
372 | 경기도 | 고양시-일산서구 | 31104 | 31 | 2018 | 6766 | 11412557 | 49505 | 28483 | 59278 |
373 | 경기도 | 고양시-일산서구 | 31104 | 31 | 2019 | 6823 | 11722973 | 49429 | 27013 | 56862 |
374 | 경기도 | 고양시-일산서구 | 31104 | 31 | 2020 | 6839 | 11738763 | 49913 | 27720 | 58026 |
375 | 경기도 | 고양시-일산서구 | 31104 | 31 | 2021 | 6861 | 11779013 | 51686 | 27710 | 58009 |
376 | 경기도 | 과천시 | 31110 | 31 | 2018 | 2290 | 2089132 | 11629 | 10145 | 8707 |
377 | 경기도 | 과천시 | 31110 | 31 | 2019 | 2311 | 2099538 | 10883 | 9811 | 8387 |
378 | 경기도 | 과천시 | 31110 | 31 | 2020 | 2264 | 2040791 | 9089 | 9695 | 8631 |
379 | 경기도 | 과천시 | 31110 | 31 | 2021 | 2339 | 2615206 | 9984 | 9818 | 10977 |
(1)
에너지사용량차이(전기-도시가스) 시각화 – 25점
에너지사용량(TOE)/전기
와 에너지사용량(TOE)/도시가스
의 차이를 계산하여 에너지사용량차이(전기-도시가스)
라는 새로운 열로 추가하라. 수도권지역에 한정하여 에너지사용량차이(전기-도시가스)
를 시각화하라. 시각화를 위해 plotly
의 choropleth_mapbox
를 사용하고, 다음 요구사항을 충족시켜라.
- 색상은
에너지사용량차이(전기-도시가스)
값에 따라 달라져야 하며, 색상 범위(range_color
)는 해당 열의 최소값과 최대값으로 설정하라. - 애니메이션 프레임은
년도
를 기준으로 하라. - 호버 데이터는
시도
와지역
을 포함해야 한다. - 투명도는 0.5로 설정하라.
- 지도 스타일은 ’carto-positron’을 사용하며, 중심 좌표는 위도 37.5642135, 경도 127.0016985로 설정하라.
- 지도의 줌 레벨은 7.5로, 높이는 800, 너비는 750으로 설정하라.
- 스크롤 줌 기능은 비활성화하라.
주의사항
- 수도권지역은 서울,경기,인천을 의미한다.
- 호버데이터시 ‘고양시-덕양구’와 같은 양식이 아니라 ’고양시덕양구’ 혹은 ’덕양구’와 같은 방식으로 호버될 경우 0점 처리함. (인천광역시의 미추홀구 역시 구지명(인천광역시 남구)로 호버될경우 0점처리함)
(풀이)
= local_dict.copy()
metro_dict 'features'] = [l for l in metro_dict['features'] if (l['properties']['code'][:2] == '31' or l['properties']['code'][:2] == '23' or l['properties']['code'][:2] == '11')]
metro_dict[#---#
= df2.assign(diff = lambda df : df['에너지사용량(TOE)/전기']- df['에너지사용량(TOE)/도시가스'])\
tidydata 'diff':'에너지사용량차이(전기-도시가스)'},axis=1)\
.rename({'시도 in ["서울특별시","경기도","인천광역시"]')
.query(
tidydata#---#
= tidydata['에너지사용량차이(전기-도시가스)'].min(), tidydata['에너지사용량차이(전기-도시가스)'].max()
range_color = px.choropleth_mapbox(
fig = metro_dict,
geojson = 'properties.code',
featureidkey = tidydata,
data_frame = 'code_local',
locations = '에너지사용량차이(전기-도시가스)',
color = '년도',
animation_frame= ['시도','지역'],
hover_data = 0.5,
opacity #---#
="carto-positron",
mapbox_style= range_color,
range_color={"lat": 37.5642135, "lon": 127.0016985},
center=7.5,
zoom=800,
height=750
width
)={'scrollZoom':False}) fig.show(config