Covid-19(新型コロナウイルス)の最新データをPandasのpivot(pivot_table), groupby, shift等を使って処理してグラフにしようと思います。ピボットとグループ化の違いや、データフレームのコピー処理についても触れているので、Python、特に、Pandasの初心者にとってかなり役立つ内容になっているのではないかと自負しています。
import requests as req
import pandas as pd
country = 'japan'
data = req.get(f"https://api.covid19api.com/live/country/{country}").json()
df = pd.DataFrame.from_dict(data)
df['Date'] = pd.to_datetime(df['Date'])
df['Date'] = df["Date"].dt.strftime('%Y/%m/%d')
df.head(2)
都道府県がバラバラなので、都道府県毎に並べ替えます。
df1 = df.sort_values(by = ['Province',"Date"])
df1.head(2)
必要なコラムだけ残します。
df2 = df1[["Date","Province", "Confirmed","Deaths"]]
df2.head(2)
このままだと、死者数・感染者数は累計なので、日毎の死者数・感染者数に変換する必要が有ります。この時に以下のようにシフトを用います。
df2['Daily Deaths']=(df2['Deaths'] -df2['Deaths'].shift(1)).fillna(0).astype(int)
df2.reset_index(drop=True, inplace=True)
df2.head(2)
上の長ったらしい警告は、dataframeのcopyに関する警告で、以下のようにして簡単に消すことができます。
df2 = df2.copy()
df2['Daily Deaths']=(df2['Deaths'] -df2['Deaths'].shift(1)).fillna(0).astype(int)
df2.reset_index(drop=True, inplace=True)
df2.head(2)
from matplotlib.pyplot import *
from matplotlib.font_manager import FontProperties
from matplotlib import rcParams
style.use('ggplot')
fp = FontProperties(fname='/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf', size=54)
rcParams['font.family'] = fp.get_name()
rcParams["font.size"] = "20"
fig, ax = subplots(figsize=(25,14))
df2.loc[df2['Province'] == 'Kanagawa'][['Date', 'Daily Deaths']][50:70].set_index('Date').plot(ax=ax,kind='barh',color='g',alpha=0.5, width= .8, align = 'center')
xticks(np.arange(0,16,2));
for i in ax.patches:
ax.text(i.get_width()+.1,i.get_y()+.5,\
'{:,}人'.format(int(round((i.get_width()), 2))), fontsize=20, color='k')
ax.legend(["神奈川県",""],loc='best', prop={'size': 26});
fig.tight_layout()
ax.invert_yaxis();
pvt = df.pivot_table(index=['Date'], columns=[ 'Province'], values = 'Deaths')
pvt.tail(5)
東京の9月24日の死者数は15人なので、このデータは正確ではない可能性があります。そしてデータをよく見ると、9月23日分のデータがありません。とりあえず確認してみます。
df[df['Date']=="2021/09/23"]
9月23日のデータが何故か存在しません。とりあえず厚労省のサイトからダウンロードしたデータと比べてみます。
df3 = pd.read_csv("https://covid19.mhlw.go.jp/public/opendata/deaths_cumulative_daily.csv")
df3['Date'] = pd.to_datetime(df3['Date'])
df3['Date'] = df3["Date"].dt.strftime('%Y/%m/%d')
df3[df3['Prefecture']=='Tokyo'].tail(5)
9月23日のデータがあります。こっちが当然のことながら正確なデータです。このデータにピボットテーブルを適用します。
pvt1 = df3.pivot_table(index=['Date'], columns=[ 'Prefecture'], values = 'Deaths(Cumulative)')
pvt1.tail(5)
死者数が累計なので、日毎に変換します。
pvt2 = pvt1.diff()
pvt2.tail(5)
7月1日からの首都圏+大阪府の死者数の推移を見てみます。
df4 = pvt2.loc['2021/07/01':'2021/09/27']
df4 = df4[['ALL', 'Chiba', 'Tokyo', 'Saitama', 'Kanagawa', 'Ibaraki', 'Osaka']]
df4.head(2)
from matplotlib.pyplot import *
from matplotlib.font_manager import FontProperties
from matplotlib import rcParams
import matplotlib.dates as dates
style.use('ggplot')
fp = FontProperties(fname='/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf', size=54)
rcParams['font.family'] = fp.get_name()
rcParams["font.size"] = "20"
fig, ax = subplots(figsize=(24,14))
df4.plot(ax = ax)
xticks(range(0,len(df4.index),2),df4.index[::2],rotation=90);
yticks(np.arange(0, 101, 5),
['{}人'.format(int(x)) for x in np.arange(0, 101, 5)]);
ylabel('死者数', fontsize=30,fontweight='heavy')
fig.tight_layout()
margins(x=0)
データが正しいのかどうかは別にして、ごちゃごちゃして見難いことだけは確かです。次にgroupbyを使って都道府県別死者数を多い順に並べます。
groupbyを使ったデータ操作¶
取り敢えず、今年に入ってからの死者数を算出します。基本としては、2020年12月31日の累積死者数から最新の累積死者数を引くことで、2021年の総死者数を求めることができます。
df5 = df3[df3['Date'] >'2020/12/30']
df5.set_index('Date', inplace=True)
df5.head(2)
グループ分けした各都道府県の最終行(2021/09/27)の数値から先頭行(2020/12/31)の数値を引けば2021年の死者数を求めることができるのですが、簡単な方法として、最大値(2021/09/27)から最小値(2020/12/31)を引くことでも今年の死者数を求めることができます。
df6 = df5.groupby('Prefecture')['Deaths(Cumulative)'].agg(['max','min'])
df6['diff'] = df6['max']-df6['min']
df6.head(5)
np.ptpを使うともっと簡潔になります。
df6 = df5.groupby('Prefecture')['Deaths(Cumulative)'].agg(np.ptp)
df6.head(5)
死者数の多い都道府県トップ20を抽出してグラフにします。
style.use('ggplot')
rcParams["font.size"] = "18"
fp = FontProperties(fname='/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf', size=54)
rcParams['font.family'] = fp.get_name()
rcParams["font.size"] = "25"
fig, ax = subplots(figsize=(20,15))
df6.sort_values(ascending=False, inplace=False)[1::].head(20).plot(kind='barh',width=.8,color='b')
rc('xtick', labelsize=30)
rc('ytick', labelsize=30)
xticks(np.arange(0,2601,200))
ax.legend(["2021年死者数"],loc='upper right', prop={'size': 26})
for i in ax.patches:
ax.text(i.get_width()+12.5,i.get_y()+.28,\
'{:,}人'.format(int(round((i.get_width()), 2))), fontsize=20, color='k');
#ax.invert_yaxis();
何となく正確なデータのような気もします。何れにしても、2021年の死者数に関しては、大阪・東京が断トツ多いのは納得がいくからです。結論として、Pandasのgroupbyとpivot tableを使い分けることで、目的のデータ処理が簡単にできることが分かったかと思います。