확진자가 5000명을 넘어선 가운데, 현 시각 기준으로 총 88명의 환자가 완치 판정을 받았다. 정부 당국과 전문가들 사이에선 앞으로 완치 사례가 더 많이 나올 것이란 기대감이 커지고 있다. 대부분의 확진 환자들이 신천지 신도들에게서 발생했다는 점, 그리고 대구와 경북이 아닌 지역에선 그나마 전염 속도가 빠르지 않다는 것이 앞으로를 조금이나마 낙관적으로 바라볼 수 있게 한다.
아래의 데이터는 지난 번에 분석한 코로나19 캐글 데이터의 업데이트 버전이다. 3월 4일까지의 데이터가 기록돼있지만 결측치가 없는 확진 날짜(confirmed_date) 피처와는 달리, 회복일(released_date)과 사망일(deceased_date) 피처에는 결측치가 굉장히 많다. 따라서, 컬럼 간의 관계를 살펴보는 건 조금 미루고 이번에는 일단 각 컬럼을 정제하고 시각화를 해봤다. (중간에 확진일과 회복일, 사망일 컬럼 간에 겹치는 날짜를 기준으로 추이(누적 아님)를 시각화했는데 겹치는 날이 너무 적어서 유의미하지는 않은 것 같습니다. 특히 최근 완치 판정을 받는 환자가 늘고 있는데, 그게 반영이 되어있지 않은 데이터임을 감안해주길 바랍니다)
중간 중간 iplot으로 그린 그래프가 제대로 보이지 않을 수 있습니다. 아래 파일에는 제대로 반영이 되어있으니 궁금하신 분들은 파일을 클릭해서 봐주세요!
import pandas as pd
import numpy as np
import matplotlib; matplotlib.rc('font', family='Malgun Gothic')
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno
import warnings; warnings.filterwarnings('ignore')
plt.style.use('ggplot')
- id: 확진자의 id (n번째 확진자)
- sex: 성별
- birth_year: 출생 연도
- country: 국적
- region: 주 활동 지역 (광역시/도 단위)
- group: 특정 집단 관련
- infection_reason: 감염 경로
- infection_order: 감염 차수 (n차 감염)
- infected_by: 해당 확진자의 감염원 id
- contact_number: 접촉자 수
- confirmed_date: 확진 일자
- released_date: 퇴원 일자 (격리 해제 일자)
- deceased_date: 사망 일자
- state: 상태
raw_df = pd.read_csv('/kaggle/input/coronavirusdataset/patient.csv')
print(raw_df.shape)
raw_df.head()
raw_df.info()
msno.bar(raw_df)
- 결측치가 매우 많음
raw_df.describe(include='all')
# 각 컬럼의 결측치 개수
raw_df.isnull().sum().to_frame().T
각 컬럼 살펴보기(cleaning)¶
sex¶
print(raw_df['sex'].unique())
plt.style.use('ggplot')
raw_df['sex'].value_counts().plot.bar()
raw_df.head()
bitrh_year¶
raw_df['birth_year'].value_counts()
sns.distplot(raw_df['birth_year'], hist=False, color='red')
# birth_year 대신 age 컬럼을 새로 만들겠다
# raw_df['birth_year'] = raw_df['birth_year'].astype(float).map(lambda x: x if x>0 else np.nan)
raw_df['age'] = pd.datetime.now().year - raw_df['birth_year']
sns.distplot(raw_df['age'], hist=False)
raw_df.drop(['birth_year'], axis=1, inplace=True)
raw_df.head(3)
country & region¶
raw_df.isnull().sum().to_frame().T
print(raw_df['country'].value_counts()); print('------------------------')
print(raw_df['region'].value_counts())
raw_df[['country', 'region']].head()
- region 컬럼의 데이터 정제
def region_clean(df):
if pd.isnull(df):
return np.nan
else:
return df.replace('Gyeongsangbuk-do', 'Gyeongbuk').\
replace('capital area', 'Capital').replace('Dague', 'Daegu').\
replace('Gangwon-do', 'Gangwon').replace('Jeollabuk-do', 'Jeonbuk').replace('Jeollanam-do', 'Jeonnam').\
replace('Jeju-do', 'Jeju').replace('Chungcheongbuk-do', 'Chungcheong').\
replace('Chungcheongnam-do', 'Chungcheong')
raw_df['region'] = raw_df['region'].apply(region_clean)
raw_df['region'].value_counts()
import chart_studio.plotly as py
import plotly.graph_objs as go
import cufflinks as cf
cf.go_offline(connected=True)
raw_df['region'].iplot(kind='hist', linecolor='blue')
group, infection_reason, infection_order, infected_by¶
raw_df.head()
print(raw_df['group'].value_counts()); print('--------------------')
print(raw_df['infection_reason'].value_counts()); print('------------------')
print(raw_df['infection_order'].value_counts()); print('---------------------')
print(raw_df['infected_by'].value_counts())
def infection_reason_clean(df):
if pd.isnull(df):
return np.nan
if 'Wuhan' in df:
return 'Wuhan'
if 'Daegu' in df:
return 'Daegu'
if 'Israel' in df:
return 'Abroad'
if 'Thailand' in df:
return 'Abroad'
if 'Singapore' in df:
return 'Abroad'
if 'Vietnam' in df:
return 'Abroad'
if 'Japan' in df:
return 'Abroad'
if 'China' in df:
return 'Abroad'
if 'Italy' in df:
return 'Italy'
if 'Cheongdo' in df:
return 'Cheongo Daenam hospital'
else:
return df
raw_df['infection_reason'] = raw_df['infection_reason'].apply(infection_reason_clean)
raw_df['infection_reason'].value_counts()
fig = plt.figure(figsize=(10, 4))
fig.add_subplot(1, 2, 1)
raw_df['infection_reason'].value_counts().plot.barh(color='green')
plt.title('infection reason')
fig.add_subplot(1, 2, 2)
raw_df['infection_order'].value_counts().plot.bar()
plt.title('infection order')
raw_df['infected_by'].value_counts().plot(kind='barh', figsize=(20,10))
contact_number, state¶
print(raw_df['contact_number'].value_counts())
raw_df['contact_number'].value_counts().iplot(kind='bar')
plt.figure(figsize=(10,8))
raw_df['contact_number'].value_counts().plot.kde()
sns.set(font_scale=1.2)
raw_df['state'].value_counts().plot.bar(figsize=(8,6))
plt.title('state')
table = pd.pivot_table(data=raw_df,
index='id',
values='contact_number',
aggfunc='sum')
table = table[table['contact_number'] > 0]
table
sns.relplot(y='contact_number', x='id', data=table.reset_index())
confirmed_date, released_date, deceased_date¶
msno.bar(raw_df[['confirmed_date', 'released_date', 'deceased_date']],
fontsize=30)
raw_df.info()
raw_df['confirmed_date'] = pd.to_datetime(raw_df['confirmed_date'])
raw_df['released_date'] = pd.to_datetime(raw_df['released_date'])
raw_df['deceased_date'] = pd.to_datetime(raw_df['deceased_date'])
raw_df.dtypes
sns.set(font_scale=1.4)
# plt.rc('font', family='Malgun Gothic')
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(18,10))
sns.distplot(raw_df['confirmed_date'].value_counts(), kde=False, ax=ax1)
sns.distplot(raw_df['confirmed_date'].value_counts(), bins=3, hist=False, ax=ax2)
ax1.set_title('confirmed histogram')
ax2.set_title('confirmed kde plot')
plt.tight_layout()
컬럼 순서 변경¶
# 편의상 컬럼 순서 변경!
raw_df.head()
raw_df.columns.tolist()
col = raw_df.columns[[0, 12, 13, 1, 2, 3, 9, 10, 11, 4, 5, 7, 8, 6]]
df = raw_df[col].copy()
print(df.shape)
df.head()
# df.to_csv('../corona/corona(kaggle)_update_data.csv')
추이¶
len(df.id.unique())
confirm = pd.pivot_table(data=df,
index='confirmed_date',
values='id',
aggfunc=len)
confirm.rename(columns={'id':'count'}, inplace=True)
confirm
confirm.iplot(kind='line',
xTitle='날짜',
yTitle='확진자수',
title='확진자 추이',
theme=cf.set_config_file(theme='solar'))
plt.figure(figsize=(10,8))
sns.pointplot(data=confirm.reset_index(), x=np.arange(len(confirm.reset_index()['confirmed_date'])), y='count')
plt.xlabel('2020.01.26~2020.03.04',
fontdict={'size':16})
release = df.groupby('released_date')['id'].count().to_frame()
decease = df.groupby('deceased_date')['id'].count().to_frame()
confirm_reset = confirm.reset_index()
release_reset = release.reset_index()
decease_reset = decease.reset_index()
data_merged = confirm_reset.merge(release_reset,
how='inner',
left_on='confirmed_date',
right_on='released_date', ).\
merge(decease_reset,
how='inner', left_on='released_date', right_on='deceased_date')
data_merged.drop(columns=['released_date', 'deceased_date'], axis=1, inplace=True)
data_merged.columns = ['date', 'confirmed', 'released', 'deceased']
data_merged
data_merged.set_index('date').iplot(kind='box')
data_merged.set_index('date').iplot(kind='barh')
cf.set_config_file(theme='ggplot')
data_merged.set_index('date').iplot(kind='surface')
data_merged.set_index('date').iplot(kind='spread')
확진자 예측¶
plt.rc('font', family='Malgun Gothic')
confirm.cumsum().plot(figsize=(9,6))
plt.title('confirmed Cumulative trend')
- ARIMA로 예측해보기¶
from statsmodels.tsa.arima_model import ARIMA
import statsmodels.api as sm
confirm_cumsum = confirm.cumsum()
# confirm_cumsum = confirm_cumsum['count'].astype(float).to_frame()
confirm_cumsum.tail()
model = ARIMA(confirm_cumsum['count'].values, order=(1, 2, 1))
fit_model = model.fit(trend='c', full_output=True, disp=True)
fit_model.summary()
# 계수들의 p-value 값이 유의수준 0.05보다 작음. AR=1, MA=1, 차분=2로 설정하는 것 유의미하다고 보여짐.
계수들의 p-value값이 0.05보다 작으므로 AR, MA, 차분값 유의미하다고 보여짐
plt.rc('font', family='Malgun Gothic')
fit_model.plot_predict()
plt.title('Forecast Result')
pd.DataFrame(fit_model.resid).plot()
plt.title('Error Variation of Actual and Predicted Values')
- 이 ARIMA 모델로 3월 5일부터 10일까지 누적 확진자 수 예측¶
forcast = fit_model.forecast(steps=6)
pred_y = forcast[0].tolist()
pred_y
pd.DataFrame(data=pred_y,
index=['3/5', '3/6', '3/7', '3/8', '3/9', '3/10'],
columns=['confirmed_count(cumsum)'])
3월 5일 오후 19시 31분 기준으로 실제 누적 확진자 수는 6088명
ARIMA 모델로 예측한 값(6240명)과 실제 값 사이에 오차가 조금은 있지만 유사함
fbprophet으로 예측¶
from fbprophet import Prophet
fb_data = confirm_cumsum.reset_index()
fb_data.columns = ['ds','y']
fb_data
prophet = Prophet(growth='linear',
seasonality_mode='multiplicative',
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=True,
changepoint_range=0.6, # 데이터의 60% 정도에서 changepoint
changepoint_prior_scale=0.1)
prophet.fit(fb_data)
# 4일 동안의 확진자 수(누적) 예측
future_data = prophet.make_future_dataframe(periods=4)
forecast_data = prophet.predict(future_data)
forecast_data[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(10)
from fbprophet.plot import add_changepoints_to_plot
fig = prophet.plot(forecast_data)
a = add_changepoints_to_plot(fig.gca(), prophet, forecast_data)
sns.set(font_scale=1.1)
fig1 = prophet.plot_components(forecast_data)
plt.tight_layout()
- 1번 그래프는 시계열 데이터의 전체적인 트렌드를 보여주고
- 2번 그래프는 weekly 트렌드를
- 3번 그래프는 yearly 트렌드를
- 마지막 4번 그래프는 daily 트렌드를 보여준다
'Legacy > [Legacy] Project' 카테고리의 다른 글
[토이프로젝트1] 코로나19 캐글 데이터 간단 탐색 및 전처리 (1차) (0) | 2020.03.01 |
---|