#概要
以前もブログでも使用したKaggleの国内陽性者数統計値を使用して、今回はbar_chart_raceで表示してみる。
- 実施時期: 2021年7月
- OS: Ubuntu20.04LTS
- Python: Python3.8.10
##モチベーション
去年来、ご存知のとおりCovid-19がニュースのトップに挙がることも多く、最近はbar chart raceも報道に利用され始めた。
Covid-19蔓延の前から下記のYouTubeで「へぇ〜、こんなのがあるんだ〜」くらいに楽しんでいたが、流石に日本の報道も使用するようになるとオイラもやらないわけにはいかない。で、ちょっと調べてみた。
他のViz系YouTubeなどでは、"...chasing bars..."などと言っていたので、そこら辺のキーワードでググると下記からGithubにつながった。
恥ずかしながら知らなかったが'bar_chart_race'(以下、BCR)というパケージで、すでに用意されている。
仕組みは、隣り合うタイムスタンプのデータを任意の点数で内挿し、それらをmatplotlibのアニメーション機能で連続描画させるというアイデアがベースとなっている。他にもいろんな工夫がされているので仕組みの詳細が知りたければよく読むこと。
また制約は他にもあるようなのでオフィシャルを参照のこと。例えばこのタイムスタンプは等間隔でなければならないなど。結果は下図のようなグラフ描画が動画ファイルで出力される。
また、Matplotlib版が紹介されているがplotly版もある。
この内挿するコードは他のアニメ表示に活用できそう。
##都道府県別Covid-19陽性者数の描画
###仕様のデータ形式へ加工
扱うことができるデータ型はPandasのDataFrame型である。それは下表のようなオフィシャルが使用している時間indexとデータcolumnを持つ必要がある(と思われる)。
今回使用するデータをそのままDataFrameに読み込むと下表のような構成となる。
Date Prefecture Positive ... Fatal Hosp_require Hosp_severe
20581 2021-07-16 Kumamoto 6516 ... 119.0 31.0 2.0
20582 2021-07-16 Oita 3520 ... 64.0 25.0 0.0
20583 2021-07-16 Miyazaki 3118 ... 27.0 14.0 0.0
20584 2021-07-16 Kagoshima 3763 ... 39.0 52.0 0.0
20585 2021-07-16 Okinawa 21661 ... 215.0 609.0 34.0
BCRに渡すためにやらないといけないのはザックリ、
- Positiveの値はその時点までの累積値なので前日のPositive値と差分の計算
- Dateをindexに、都道府県名をデータcolmunにしたDataFrameを作成し流し込む
- 人口当たりの陽性者数も別途描画したいので、都道府県ごとの人口ファイルを用意し割り算する。
今回はほぼこのPandasの操作が主となる。
###BCRをインストール
pipでインストールする。
pip install --upgrade pip
pip install bar_chart_race
試してないがconda環境でもインストール可能
forgeを使ってなければ適宜変更すること。
###csvファイルの読み込み
Kaggleのデータファイルと、自分で作成した人口ファイル(2015年国勢調査)を読み込ませる。
人口当たりの描画が不要なら後者は必要ない。またKaggleのデータは2020/5/4以前に欠損が多く、EDAが目的ではないのでエディタでこの日以前の行はすべて削除している。
import pandas as pd
import numpy as np
import csv
import bar_chart_race as bcr
# Kaggleのデータ
df_org = pd.read_csv('covid_jpn_prefecture.csv')
print(df_org.tail())
# 2015年の都道府県別人口
dict_population = {}
with open('pref_popu.csv', newline='') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
dict_population[row['Prefecture']] = row['Population']
###DataFrameの準備
元csvデータをBCR用に変更するため、DataFrame(df_1とdf_2)を用意する。
# 県名の取得
df_pref = df_org[df_org['Date'] == '2021-05-06'] # 何日でもOK
lst_pref = list(df_pref.iloc[:,1])
# csvファイル中の全年月日の取得
lst_date = list(df_org.iloc[::47,0])
df_1 = pd.DataFrame(columns=lst_pref, index=lst_date) # 陽性者数/日 用
df_1.index.name = 'date' # オフィシャルに合わせてindex名に'date'を追加する。
df_2 = pd.DataFrame(columns=lst_pref, index=lst_date) # 陽性者数/100万人/日 用
df_2.index.name = 'date'
###データを再計算
累積値から日毎陽性者数に再計算し、またその結果から人口100万人あたりの陽性者数ももとめる。
# 日毎の陽性者数
for i in range(len(lst_pref)):
df_wk1 = df_org[df_org['Prefecture'] == lst_pref[i]]
lstPos1 = list(df_wk1['Positive'])
lstPos2 = lstPos1[:-1]
lstPos2.insert(0, 0)
lstPos = [x-y for x,y in zip(lstPos1, lstPos2)]
df_1[lst_pref[i]] = lstPos
# 人口100万人あたりの陽性者数
for i in range(len(lst_pref)):
wkpopu = int(dict_population[lst_pref[i]])
lstPos1 = df_1[lst_pref[i]]
lstPos = [round(x / wkpopu * 1000000) for x in lstPos1]
df_2[lst_pref[i]] = lstPos
# 先頭行(NaN)の削除
df_1 = df_1.drop('2020-05-05', axis=0)
df_2 = df_2.drop('2020-05-05', axis=0)
###描画
bar_chart_race()にdf_1 or df_2を渡すのだが、データ数が多いと動画作成にかなり時間がかかったので、範囲を指定してdf_showに入れ替えている。
# bar_chart_raceで描画
df_show = df_1.loc['2021-05-01':, :] # 任意の開始日を指定
bcr.bar_chart_race(df=df_show, n_bars=6,
fixed_max=True, steps_per_period=12,
period_summary_func=lambda v, r: {'x': .95, 'y': .08,
's': f'Total infects: {v.nlargest(47).sum():,.0f}',
'ha': 'right', 'size': 8, 'family': 'Courier New'},
filename=None)
bar_chart_race()の引数はオフィシャルを参照しないといけないが簡単に…
描画するバー数はn_bars、内挿する点数はsteps_per_periodで指定できる。
この点数を多くするとバーの移動動作はなめらかになり、ゆっくり移動するように見えた。
横軸スケールを全期間中の最大値に固定するか否かをfixed_maxで指定する。
また、v.nlargest(47).sum()はその日の47都道府県の合計を計算し描画している。
出力する動画ファイル名はfilenameで指定するが、環境がColaboのようなJupyter notebookタイプであれば、filename=Noneとなる。つまりファイルの出力はされず、セル下に動画が表示される。
普通のpython環境であればfilename="hoge.mp4"でファイル出力してくれる。
フォーマットはmp4なのでライブラリが必要かも。
sudo apt install ffmpeg
あと、本題と直接関係ないけどmp4ファイルを各種フォーマットに変換していただける素敵なサイトを見つけた。
このサイトで、mp4をgifに変換してこのブログに貼っている。
動画だけでなく静止画や音声ファイル、ドキュメントファイルなどもコンバートしてくれる。
用途に応じてフリーウェアをいちいちインストールしなくて済むので重宝しそう。
以上