본문 바로가기

Information/Corona19(COVID19)

코로나 확진자 수/ 사망자 수에 대한 물방울 차트(Bubble Chart) 애니메이션

반응형

파이썬을 이용해서 다음과 같은 애니메이션을 만들고자 한다.  (아래는 애니메이션 실행되는 것을 캡처한 그림임)

 

 

물방울 차트(bubble chart)라고 불리는 것으로, 한스 로슬링(Hans Rosling) 교수님이 유행시킨 차트이다. 

 

x축은 신규 확진자 수에 대한 로그 스케일 값이고(주별 합계), y축은 주별 사망자 수 합계에 대한 로그 스케일 값이다. 로그 스케일로 변환하지 않으면 데이터의 간극이 너무 커서 제대로 표현되지 않는다. (적게 발생한 국가와 많이 발생한 국가 간 간극이 너무 큼)

 

물방울의 크기는 그 국가의 인구 수를 나타내고, 색깔은 어느 대륙에 있는지를 표시한다.

 

프로그램 작성 방법

  • 프로그램 언어로 파이썬을 사용하고, 주피터 노트북을 이용한다.
  • 코로나 데이터는 ECDC 사이트에서 다운로드한다. (자동으로 다운로드하게 프로그래밍)
  • 물방울 차트는 플로틀리(plotly)를 사용해서 그린다. 이 패키지가 안 깔려 있으면 설치한다.  pip install plotly

 

프로그램 작성

1. 코로나 데이터 파일(엑셀 파일)이 현재 폴더에 없으면 자동으로 ECDC 사이에서 다운로드한다. 

  • 엑셀 파일을 다운로드는 requests.get을 이용
  • SSL 인증서가 없어도 에러가 뜨지 않게 get 함수 내에서 verify=False로 처리
def download_covid_file(covid_file):
    covid_xls_url = 'https://www.ecdc.europa.eu/sites/default/files/documents/COVID-19-geographic-disbtribution-worldwide.xlsx'
    req = requests.get(covid_xls_url, allow_redirects=True, verify=False)
    open(covid_file, 'wb').write(req.content)

 

  • 다운로드하는 엑셀파일의 이름을 오늘 날자로 지정한다. 
  • 엑셀 파일이 이미 로컬 디스크에 있는지 조사해서, 만약 없다면 다운로드한다.
def get_corona_data():
    today_covid_file = date.today().strftime("%Y%m%d") + "_corona.xlsx"
    if not os.path.exists(today_covid_file):
        download_covid_file(today_covid_file)
    return pd.read_excel(today_covid_file)

 

2. 다운로드 받은 엑셀을 DataFrame으로 읽어내고, 이 df를 전처리

def get_data_by_day(df_world):
    df = df_world.drop(columns=['geoId','Cumulative_number_for_14_days_of_COVID-19_cases_per_100000'])
    
    #2019년 데이터 삭제
    df['dateRep'] = df['dateRep'].apply(lambda x: pd.to_datetime(x, format='%Y-%m-%d'))  
    df = df[df['dateRep'].dt.year != 2019]
    
    #인구수가 없는 나라의 경우(Cases_on_an_international_conveyance_Japan,Wallis_and_Futuna)
    #최솟값으로 대치
    min_pop = df['popData2019'].min()
    df['popData2019'].fillna(min_pop, inplace=True)
        
    # 정렬: 날자순
    df['dayofyear'] = df['dateRep'].dt.dayofyear
#     df['weekofyear'] = df['dateRep'].dt.weekofyear
    df['weekofyear'] = df['dateRep'].dt.isocalendar().week
    df = df.sort_values('dateRep', ascending=True).reset_index(drop=True)
    
    return df

 

3. 1주일 단위로 끊어서 확진자와 사망자 합계를 구한다. 

def sum_by_week(df_day):
    df = df_day.groupby(['countriesAndTerritories','year','weekofyear']).agg({'cases':'sum', 'deaths':'sum'}).reset_index()
    
    df_pop = df_day[['countriesAndTerritories', 'popData2019', 'continentExp','countryterritoryCode']]
    df_pop = df_pop.drop_duplicates('countriesAndTerritories').reset_index(drop=True)
    
    df = pd.merge(df,df_pop,how='inner', on='countriesAndTerritories')

 

지금까지 작성된 코드를 실행하면,

df_world = get_corona_data()
df_day= get_data_by_day(df_world)
df_week = sum_by_week(df_day)

df_week.head()

print(df_week['cases'].max(), df_week['deaths'].max())

 

4. 물방울 차트를 그린다. 

  • x축은 확진자 수, y축은 사망자 수를 나타낸다. 두 축 모두 로그 스케일로 표현(log_x=True, log_y=True)
  • x축 범위는 [1, x_max]  : 로그 스케일이기에 0부터 시작하면 에러가 뜬다. x_max는 만 단위에서 반올림이 되도록 처리.
  • y축 범위는 [1, y_max]: 역시 로그 스케일이기에 0부터 시작하면 안 되고, y_max는 백 단위에서 반올림
  • 애니메이션 시간단위는 1주 : animation_frame='weekofyear'
  • 애니메이션 할 때의 그룹 기준은 국가: animation_group='countriesAndTerritories'
  • 물방울 크기는 국가별 인구수에 비례하도록 하고, 최대 크기는 50: size='popData2009', size_max=50
  • 물방울 색상은 대륙별로 다르게 : color='continentExp'
  • 물방울에 마우스를 올렸을 때 나오는 값은 국가 이름: hover_name='countriesAndTerritories' 
import plotly.express as px

df = df_week
x_max = round(df_week['cases'].max() / 10000) * 10000
y_max = round(df_week['deaths'].max() / 100) * 100
px.scatter(data_frame=df, x='cases', y='deaths', 
           animation_frame='weekofyear', animation_group='countriesAndTerritories',           
           size='popData2019', size_max=50, color='continentExp', hover_name='countriesAndTerritories',
           log_x=True, log_y=True, 
           range_x=[1,x_max], range_y=[1,y_max],   
#            text='countryterritoryCode',
          )

 

위 코드를 모두 구현한 주피터 노트북 파일은, 

corona_bubble_chart.ipynb
5.14MB

 

위 ipynb파일을 다운로드한 후 실행시켜보면, 하단부에 애니메이션을 실행시킬 수 있는 창이 뜨고, 플레이 버튼을 누르면 애니메이션이 진행될 것이다.

 

 

 

-끝-

반응형