공공데이터포탈 Open API 활용
국내 시.도별 코로나19 확진자 발생 현황 데이터 시각화
공공데이터포털은 국내 각 기관이 다루는 데이터를 통합하여 사용하기 편리하게 제공하는 포털사이트이다. 코로나19(COVID-19)와 관련한 데이터도 제공한다.
이번 블로그에서는 국내 코로나19 현황 데이터를 사용하여 일별 국내 총 확진자 추이, 국내 시.도별 확진자 추이 정보를 시각화 라이브러리, Plotly를 사용하여 차트로 표현해 본다.
배워 볼 것
- 공공데이터포털 OpenAPI 사용방법
- 데이터 전처리
- 데이터 시각화 ( Bar, Pie, Map(공간정보) )
개발 환경
- Python 3.9
- Pandas 1.2.0
- plotly 4.14.3
- requests 2.25.1
- beautifulsoup4 4.9.3
- pytz 2020.5
공공데이터포털 ( 바로가기 )
- 회원 가입 후 인증키 발급 확인
- 다음의 서비스를 활용신청 한다.
! 주의사항
- 활용신청 일로부터 2~3시간이 지나면 사용된다고 설명되어 있으나 많게는 24시간이 지나야 서비스를 활용할 수 있다.
라이브러리 임포트
import json # 공간정보 geojson파일 로드
import requests # 공공데이터포탈 API 호출
from bs4 import BeautifulSoup as bs #
from datetime import datetime, date
# data
import pandas as pd
#visualization
import plotly.graph_objects as go
import plotly.express as px
import warnings # Supress warnings
warnings.filterwarnings('ignore')
공공데이터포털 샘플 결과 확인
- 활용신청 24시간 후 정상 인증 되었는지 확인하기 위해 해당 서비스를 테스트 해본다.
결과
함수 생성
# 현재시간을 YYYYMMDD 형태로 리턴
def get_now(date_format='%Y%m%d'):
import pytz
return datetime.now(tz=pytz.timezone('Asia/Seoul')).strftime(date_format)
def get_korea_covid_data(start_dt=None, end_dt=None, num_of_rows=100, page_no=1, data_type='html.parser'):
if start_dt is None:
start_dt = get_now()
if end_dt is None:
end_dt = get_now()
# print(f'page_no {page_no}, num_of_rows {num_of_rows}, start_dt {start_dt}, end_dt {end_dt}')
end_point = "http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19SidoInfStateJson"
params = {
"ServiceKey": requests.utils.unquote("Insert your secret key", encoding='utf-8'), # 1
"numOfRows": num_of_rows,
"pageNo": page_no,
"startCreateDt": start_dt,
"endCreateDt": end_dt
}
resp = requests.get(end_point, params=params) #2
data = None
if resp.status_code == 200:
data = bs(resp.text, data_type) #3
return data
#4
def convert_list(item):
_rs = []
if item:
for data in list(item):
_rs.append(data.text)
return _rs
#1 `get_korea_covid_data` 함수의 `ServiceKey`에 공공데이터포털에서 발급된 인증키를 넣는다.
#2 `requests` 라이브러리를 사용하여 http 요청처리 한다.
#3 http응답메세지가 정상인 경우 `beautilfulsoup`로 데이터를 읽어 객체화 한다.
#4 `convert_list` 함수는 DataFrame으로 변환하기 위해 각 `item`데이터의 컬럼을 리스트로 저장한다.
데이터 조회
data = get_korea_covid_data(start_dt='20210101', end_dt='20210315', page_no=current_page_no, data_type='lxml', num_of_rows=num_of_rows)
위에서 얻은 데이터를 `convert_list`함수 처리한 결과를 보면 다음과 같다.
items = data.find_all('items')[0]
_template = []
for item in list(items):
print(convert_list(item))
_template.append(convert_list(item))
결과
..
['2021-01-30 9:31:34.34', '36', '931', '울산', '蔚山', 'Ulsan', '2', '861', '34', '1', '1', '81.17', '7330', '2021년 01월 30일 00시', 'NULL']
['2021-01-30 9:31:34.34', '13', '1097', '대전', '大田', 'Daejeon', '2', '934', '150', '1', '1', '74.42', '7329', '2021년 01월 30일 00시', 'NULL']
['2021-01-30 9:31:34.34', '17', '1766', '광주', '光州', 'Gwangju', '33', '1369', '380', '32', '1', '121.23', '7328', '2021년 01월 30일 00시', 'NULL']
['2021-01-30 9:31:34.34', '48', '3792', '인천', '仁川', 'Incheon', '15', '3412', '332', '15', '0', '128.28', '7327', '2021년 01월 30일 00시', 'NULL']
['2021-01-30 9:31:34.34', '207', '8298', '대구', '大邱', 'Daegu', '8', '7961', '130', '8', '0', '340.57', '7326', '2021년 01월 30일 00시', 'NULL']
['2021-01-30 9:31:34.34', '93', '2737', '부산', '釜山', 'Busan', '20', '2256', '388', '18', '2', '80.22', '7325', '2021년 01월 30일 00시', 'NULL']
['2021-01-30 9:31:34.34', '323', '24061', '서울', '首尔', 'Seoul', '154', '19594', '4142', '150', '2', '247.2', '7324', '2021년 01월 30일 00시', '2021-02-08 16:04:34.34']
['2021-01-30 9:31:34.34', '1414', '77848', '합계', '合计', 'Total', '456', '67121', '9313', '421', '35', '150.15', '7323', '2021년 01월 30일 00시', '2021-02-08 16:06:05.05']
['2021-01-29 9:36:10.10', '3', '2698', '검역', '隔離區', 'Lazaretto', '9', '1979', '716', '0', '9', '-', '7322', '2021년 01월 29일 00시', 'NULL']
['2021-01-29 9:36:10.10', '0', '519', '제주', '济州', 'Jeju', '1', '495', '24', '1', '0', '77.38', '7321', '2021년 01월 29일 00시', 'NULL']
..
DataFrame으로 변환하기 위해 적합한 형태로 만들어졌다. 여기에 컬럼 정보를 매핑해야 하므로 컬럼을 정의한다.
columns = [
'createdt',
'deathcnt',
'defcnt',
'gubun',
'gubuncn',
'gubunen',
'incdec',
'isolclearcnt',
'isolingcnt',
'localocccnt',
'overflowcnt',
'qurrate',
'seq',
'stdday',
'updatedt',
]
DataFrame 생성
df = pd.DataFrame(_template, columns=columns)
df.head(5)
# OUTPUT
createdt deathcnt defcnt gubun gubuncn gubunen incdec isolclearcnt isolingcnt localocccnt overflowcnt qurrate seq stdday updatedt
0 2021-03-15 09:55:40.206 4 3056 검역 隔離區 Lazaretto 7 2596 456 0 7 - 8253 2021년 03월 15일 00시 null
1 2021-03-15 09:55:40.206 1 609 제주 济州 Jeju 3 575 33 3 0 90.79 8252 2021년 03월 15일 00시 null
2 2021-03-15 09:55:40.206 14 2438 경남 庆南 Gyeongsangnam-do 31 2185 239 31 0 72.53 8251 2021년 03월 15일 00시 null
3 2021-03-15 09:55:40.206 72 3373 경북 庆北 Gyeongsangbuk-do 3 3191 110 3 0 126.69 8250 2021년 03월 15일 00시 null
4 2021-03-15 09:55:40.206 8 904 전남 全南 Jeollanam-do 1 827 69 0 1 48.48 8249 2021년 03월 15일 00시 null
데이터 전처리
- 중복 행 확인 ( 결과: 중복 없음 )
# 전체 행 중복 확인
df[_df.duplicated()]
# seq 컬럼 중복 확인
df[_df.duplicated(['seq'])]
- 데이터 형변환
int_cols = ['deathcnt', 'defcnt', 'incdec', 'isolclearcnt', 'isolingcnt', 'localocccnt', 'overflowcnt']
for col in int_cols:
df[col] = df[col].astype(int) #1
df['qurrate'] = df['qurrate'].astype(float) #2
df['createdt'] = df['createdt'].apply(lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S.%f')) #3
df['dt'] = df['stdday'].apply(lambda x: datetime.strptime(x, '%Y년 %m월 %d일 %H시')) #4
#1 `int_cols`에 정의된 컬럼을 int형으로 변환한다.
#2 비율 컬럼인 `qurrate`을 float형으로 변환한다.
#3 생성일시 `createdt`를 datetime형으로 변환한다.
#4 기준일시 `stdday`를 신규 컬럼 `dt`에 datetime형으로 변환하며 넣는다.
- 불필요 데이터 삭제
df.drop(columns=['gubuncn', 'gubunen', 'seq', 'stdday', 'updatedt'], inplace=True)
df.head(5)
# OUTPUT
createdt deathcnt defcnt gubun incdec isolclearcnt isolingcnt localocccnt overflowcnt qurrate dt
0 2021-03-15 09:55:40.206 4 3056 검역 7 2596 456 0 7 0.00 2021-03-15
1 2021-03-15 09:55:40.206 1 609 제주 3 575 33 3 0 90.79 2021-03-15
2 2021-03-15 09:55:40.206 14 2438 경남 31 2185 239 31 0 72.53 2021-03-15
3 2021-03-15 09:55:40.206 72 3373 경북 3 3191 110 3 0 126.69 2021-03-15
4 2021-03-15 09:55:40.206 8 904 전남 1 827 69 0 1 48.48 2021-03-15
df.dtypes
# OUTPUT
createdt datetime64[ns]
deathcnt int32
defcnt int32
gubun object
incdec int32
isolclearcnt int32
isolingcnt int32
localocccnt int32
overflowcnt int32
qurrate float64
dt datetime64[ns]
dtype: object
데이터 시각화
1. 서울 지역 확진자 추이
df_seoul = df[df.gubun == '서울']
fig = go.Figure([
go.Bar(x=df_seoul.dt, y=df_seoul.overflowcnt, name='해외유입', text=df_seoul.overflowcnt, marker_color='red'),
go.Bar(x=df_seoul.dt, y=df_seoul.localocccnt, name='국내발생', text=df_seoul.localocccnt, marker_color='blue'),
])
fig.update_layout(width=1000, barmode='stack')
fig.show()
- `stack` 으로 해외유입, 국내발생 Bar 차트 출력
2. 시.도 별 확진자 발생 추이 비교 - Line chart
charts = []
for region in list(sorted_df.gubun.unique()):
_df = df[df.gubun == region]
charts.append(go.Scatter(x=_df.dt, y=_df.localocccnt, name=_df.iloc[0].gubun))
fig = go.Figure(charts)
fig.update_layout(
title=dict(
text='시.도',
x=0.5
)
)
fig.update_layout(width=1300, height=600)
fig.show()
(Plotly 라이브러리에선 Line 차트를 Scatter 함수로 표현한다.)
3. 현재일 기준 전체 시.도 확진자 발생 현황 - Bar chart
- 확진자 가 많은 지역순으로 차트를 출력한다.
- 현재일은 현재 데이터 중 최신일을 기준으로 한다.
- 확진자 수 기준 정렬을 하기 위해 `sort_values` 를 사용한다.
- 데이터 행 중 `검역`, `합계` 는 제외 한다.
sorted_df = df[df.dt == df.dt.max()][(df.gubun != '검역') & (df.gubun != '합계')].sort_values('localocccnt', ascending=False)
sorted_df.head(5)
# OUTPUT
createdt deathcnt defcnt gubun incdec isolclearcnt isolingcnt localocccnt overflowcnt qurrate dt
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
max_date = sorted_df.dt.iloc[0]
fig = px.bar(
sorted_df,
x=sorted_df.gubun,
y=sorted_df.localocccnt,
title='시.도',
text='localocccnt',
color='localocccnt',
color_continuous_scale=px.colors.sequential.Redor,
labels={'localocccnt':'확진자 수'}
)
fig.update_layout(
title=dict(
text=f"<b>국내 시.도 확진자 발생 현황</b><br>집계일자: {max_date}",
x=0.5,
),
xaxis_title='시.도',
yaxis_title='확진자 수',
bargap=0.3,
)
fig.show()
4. 현재일 기준 전체 시.도 확진자 발생 현황 - Pie chart
fig = px.pie(
sorted_df,
values='localocccnt',
names='gubun',
title=f"<b>국내 시.도 확진자 발생 현황</b><br>집계일자: {max_date}",
)
fig.update_traces(textposition='inside', textinfo='percent+label')
fig.show()
2편에 이어서 Plotly의 choropleth_mapbox 차트를 사용하여 공간정보 시각화에 대해 알아본다.
2021.03.18 - [데이터 시각화] - 국내 시.도별 코로나 19 확진 정보 - Plotly 공간정보 ( 단계구분도 - choropleth map )
'데이터 시각화' 카테고리의 다른 글
Notebook에서 matplotlib 한글 폰트 깨짐 문제 해결 (0) | 2022.11.02 |
---|---|
Python 데이터 시각화 - 상관관계(correlation) 분석, Heatmap (with Matplotlib, seaborn ) (0) | 2021.03.30 |
Python 데이터 시각화, 국내 시.도별 코로나 19 확진 정보 - 공간정보 ( 단계구분도 - plotly choropleth map ) (0) | 2021.03.18 |