COVID-19のデータを可視化してみた【Python】

2020/04/16

【Python】 作ってみた

t f B! P L

こんなプロットができた



こちらのページで見れます

COVID-19 Python plot

null
Githubのレポジトリにて公開しています。


きっかけ

昨今、COVID-19(コロナウイルス)の文字を見ない日はありません。
それほど現在の世の中にはCOVID-19に関する情報、データが出回っています。


COVID-19に関する論文もたくさん出ていますし、データもオープンになっているものがたくさんあります。
プログラミングを学んでいる身として、データ分析・可視化の練習になるのでは、と思い今回いろいろなプロットを作ってみました。

また、Python会という団体での新歓イベントの一環として、時流に乗ってこのようなデータを扱ってみたらなじみやすいのでは、と思ったのも一つです。

全体のコード

github gistを使用して、Jupyter notebookを埋め込んであります。
詳細はこちらのコードをご確認ください。

ハンズオンを行いました


ハンズオンで使用したスライドをのせておきます。

使用したデータ

今回可視化に用いたデータはこちらのレポジトリにあるデータです。

他にも、WHOや厚労省がオープンにしているデータもありましたが、いかんせんWeb上のテキストとして表示されているだけで、ファイルとして扱いにくかったので、今回は使いませんでした。
その点こちらのレポジトリでは、毎日データが更新されており、データもcsv形式でまとめられていて、非常に扱いやすかったです。

ちなみに、こちらのレポジトリのデータは、
Coronavirus COVID-19 (2019-nCoV)
というCOVID-19に関する様々な数字がチェックできるサイトにて使用されていると思われます。
また、論文も公表されています。
An interactive web-based dashboard to track COVID-19 in real time - The Lancet Infectious Diseases

使用したライブラリ

pandas

csvファイルの扱いに長けています。定番ですね。

pycountry


国名を国名コードに変換できるライブラリです。

可視化

  • matplotlib
  • pandas_bokeh
  • Folium
使い方などは後ほど。

可視化の流れ



  • csvファイルの読み込み
  • データを整える
  • 可視化していく

  • 以下、コードの詳しい説明はしませんが、ポイントだけ記しておこうと思います。

    データを見てみる

    今回使ったデータは、レポジトリの中でも、
    COVID-19/csse_covid_19_data/csse_covid_19_time_series/
    にあるデータです。
    日付ごとの数値を記録してあります。
    種類は3つあり、
    • timeseriescovid19confirmedglobal.csv
    • timeseriescovid19deathsglobal.csv
    • timeseriescovid19recoveredglobal.csv
    で、それぞれ感染者確認数、死者数、治った数の記録でした。

    データの様子


    縦に日付、横に国名のファイルです。
    2020/1/22から始まり、現在まであります。

    データを整える

    さて、ここからPythonでデータを扱っていきます。
    上記の記録されたファイル、そのままではきれいに可視化できません。
    都合のよいようにカスタマイズしていきます。

    整えるポイント


  • いらないカラム(列)の削除
  • Country/Regionごとにまとめる
  • カラムとインデックスの反転
  • 国名→国名コードに変換する
  • 以下、dfはpandasのdataframeのことです。

    いらないカラム(列)の削除

    df.drop(columns=['Province/State', 'Lat', 'Long'])
    

    Country/Regionごとにまとめる

    df.groupby('Country/Region').mean()
    


    同じ国でも地域ごとに1行ずつデータがあったので、国ごとに1つにまとめます。

    カラムとインデックスの反転

    df.T
    
    「カラムに国名、インデックスに日付」がくるように全体を転置します。

    国名→国名コードに変換する

    たとえば、JapanをJPNに変換します。
    pycountry.countries.get(name='Japan').alpha_3
    

    以下で使う、コロプレス図は国名を3文字コードで認識するので、今のうちに変換しておきます。
    pycountryというライブラリを使います。
    • 国名→3文字コード
    • 3文字コード→2文字コード
    • 2文字コード→3文字コード
    など、いろいろな変換に対応しています。

    import pycountry
    # 空のリスト作成
    list_country_code = []
    list_country_ = []
    
    # 国名を3文字コードに変換していく
    for i in list(df_time_confirmed_sum.columns):
        try:
            list_country_code.append(pycountry.countries.get(name=i).alpha_3)
            list_country_.append(i)
        except:
            print(i)
    

    ライブラリの変換だけでは、うまく国名を認識してくれないものもあるので、
    それらはprintして打ち出し、手動で変換しました。

    国名コードはこちらを参考に。
    List of countries by regional classification - Meta

    可視化していく

    データがきれいになったので、可視化してみます。
    以下のデータはすべて、4/10現在までのデータになります。

    国ごとの、日付に沿った確認患者数の折れ線グラフ

    日本

    country = "JPN"
    df_time_confirmed_sum[country].plot()
    plt.title(country)
    #plt.savefig('fig/plot_japan_no_yaxis.pdf')
    

    USA

    country = "USA"
    df_time_confirmed_sum[country].plot()
    plt.title(country)
    #plt.savefig('fig/plot_usa_no_yaxis.pdf')
    

    アメリカが桁違いで多いですね…

    アメリカが感染者数の最大値だったので、y軸の範囲を設定しなおして、
    もう一度日本を見てみると…
    country = "JPN"
    df_time_confirmed_sum[country].plot()
    plt.title(country)
    plt.ylim([0, today_max_round])
    #plt.savefig('fig/plot_japan.pdf')
    

    いかにアメリカの数が多いかがわかります。

    感染確認数上位10の国を比較したグラフ

    dataframeから、上位10ヶ国を抜き出します。
    df_time_confirmed_sum.max().sort_values(ascending=False)[0:10]
    

    折れ線図

    df_time_confirmed_sum[list_top10_country].plot()
    plt.title("confirmed top10")
    #plt.savefig('fig/plot_top10_confirmed.pdf')
    

    アメリカ、スペイン、イタリア…と並んでいます。

    棒グラフ

    4/10におけるデータの棒グラフです。
    df_time_confirmed_sum[list_top10_country][-1:].plot.bar()
    plt.title("confirmed top10")
    #plt.savefig('fig/bar_top10_confirmed.pdf')
    

    インタラクティブな図を作成

    ここまでは日頃よく作っている可視化のグラフです。
    今回はインタラクティブな、カーソルでぐりぐり動かしてグラフ上の数値などを見ることのできる、プロット・地図を描いてみました。

    折れ線グラフ

    pandas-bokehというライブラリを使います。

    • USAとESP(スペイン)を比較
    import pandas_bokeh
    pandas_bokeh.output_notebook()
    df_time_confirmed_sum[["USA", "ESP"]].plot_bokeh.line()
    

    • 感染者数top10ヶ国の比較
    df_time_confirmed_sum[list_top10_country].plot_bokeh.line()
    

    • 棒グラフ
    df_time_confirmed_sum[list_top10_country][-1:].plot_bokeh.bar()
    

    コロプレス図を作成

    こちらを使用します。


    Foliumで描画するためには、
    • GeoJSON もしくは TopoJSON 形式のファイル
    • コロプレス図を色分けするための値を含む pandas の DataFrame
    の2つが必要です。

    世界地図のGeoJSONファイルは、

    こちらを使用しました。

    まずは普通の世界地図をplotしてみます。
    import folium
    m = folium.Map(location=[10, 35], zoom_start=1.5)
    
    geojson = "./world.geo.json/countries.geo.json"
    folium.GeoJson(geojson).add_to(m)
    
    m
    

    dataframeに、国名コードと値が並ぶように調整をしてplotしてみると、
    m = folium.Map(location=[10, 35], zoom_start=1.5)
    
    geojson = "./world.geo.json/countries.geo.json"
    folium.GeoJson(geojson).add_to(m)
    m.choropleth(geo_data=geojson, data=df_latest,
                 columns=['country', 'number'],
                 key_on='feature.id',
                 fill_color='YlGnBu', reset=True)
    
    m
    

    colormapの変更もできます。
    m = folium.Map(location=[10, 35], zoom_start=1.5)
    
    geojson = "./world.geo.json/countries.geo.json"
    folium.GeoJson(geojson).add_to(m)
    m.choropleth(geo_data=geojson, data=df_latest,
                 columns=['country', 'number'],
                 key_on='feature.id',
                 fill_color='YlOrRd', reset=True)
    
    m
    

    • マーカーを追加してみる
    Foliumには、マーカーの機能があります。
    地図上にピンを刺して、数値などを表示できます。

    今回はtop10の国について、首都の位置に感染者数のピンをおいてみます。
    m = folium.Map(location=[10, 35], zoom_start=1.5)
    
    geojson = "./world.geo.json/countries.geo.json"
    folium.GeoJson(geojson).add_to(m)
    m.choropleth(geo_data=geojson, data=df_latest,
                 columns=['country', 'number'],
                 key_on='feature.id',
                 fill_color='YlOrRd', reset=True)
    
    # マーカーの追加
    for i in list_top10_country:
        capital = df_capital[df_capital["code3"] == i]
        folium.Marker(
        [capital["Capital Latitude"], capital["Capital Longitude"]],
        popup=df_time_confirmed_sum[-1:][i][0]
        ).add_to(m)
    
    m
    

    1つのfolium.Mapオブジェクトに対して、forループでマーカーを追加できました。

    • 日付を指定してplotしてみる
    2/1時点のデータは、
    df_test = df_time_confirmed_sum.loc["2/1/20"].T.reset_index()
    df_test.columns = ["country", "number"]
    df_test.head()
    

    なので、
    m = folium.Map(location=[10, 35], zoom_start=1.5)
    
    geojson = "./world.geo.json/countries.geo.json"
    folium.GeoJson(geojson).add_to(m)
    m.choropleth(geo_data=geojson, data=df_test,
                 columns=['country', 'number'],
                 key_on='feature.id',
                 fill_color='YlOrRd', reset=True)
    
    m
    

    でplotできます。

    まとめ

    • 自分で各国の数字を眺めていると、ニュースなどの情報がよりわかりやすくなるのかも
    • 今回はただ数字のデータからその状況をグラフとして「見える化」したにすぎないので、 何かしらの意味が見いだせるような解析もしてみたいところ

    コードや説明で間違っているところがあれば、教えてもらえると幸いです。

    記事の感想をリアクションでお願いします!

    QooQ