iMind Developers Blog

iMind開発者ブログ

Pythonで新型コロナの感染者数推移を確認する(全国データ)

概要

COVID-19の国内の感染状況について、自分で手を動かしてデータを確認する為のサンプルコードです。

都道府県版はこちら

利用するデータ

下記に掲載されているCSVファイルを利用します。

https://github.com/kaz-ogiwara/covid19

自分で厚労省のデータを毎日整形していたらかなりの手間がかかるので、こうした整形済みのデータを作ってくださっている方がいるのはたいへんありがたいです。

dsata/summary.csvに全国の感染者数等の情報が入っています。

データの読込み

summary.csvを読み込んで利用しやすい形式に整形します。

Jupyter Notebook上で動かす想定で記述しています。

import datetime
import pandas as pd
import matplotlib
from matplotlib import pylab as plt

%matplotlib inline

# フォント設定(環境によってここに指定する値は異なります)
from matplotlib import rcParams
rcParams['font.family'] = 'sans-serif'
rcParams['font.sans-serif'] = ['MS PGothic', 'Osaka', 'Hiragino Sans', 'TakaoGothic']

# summaryの取得
summary = pd.read_csv('https://raw.githubusercontent.com/kaz-ogiwara/covid19/master/data/summary.csv')

# 日付をdatetimeに変換
def to_datetime(row):
    return datetime.datetime(row['年'], row['月'], row['日'])
summary['日付'] = summary[['年', '月', '日']].apply(to_datetime, axis=1)

# 今回使わないカラムを落としておきます
summary.drop(['年', '月', '日', 'URL'], axis=1, inplace=True)

# 日付をindexに指定します
summary.set_index('日付', inplace=True)

当該データは累計しか入っていないので前日との差分から当日データを生成します。

summary['当日陽性者'] = summary['PCR検査陽性者'].diff(periods=1)
summary['当日検査実施人数'] = summary['PCR検査実施人数'].diff(periods=1)
summary['当日死亡者'] = summary['死亡者'].diff(periods=1)
summary['当日重傷者'] = summary['人工呼吸器又は集中治療室に入院している者'].diff(periods=1)
summary['当日有症状者'] = summary['有症状者'].diff(periods=1)
summary['当日無症状者'] = summary['無症状者'].diff(periods=1)
summary['当日退院者'] = summary['退院者'].diff(periods=1)

日毎の新規感染者数推移

ある程度件数が増えてくる3/15あたりから当日陽性者の推移をplotしてみます。

# 3/15以降にデータを絞る
tmp_summary = summary[summary['日付'] >= '2020-03-15'].set_index('日付')

# plot
tmp_summary[['当日陽性者']].plot(figsize=(15,10))

f:id:mwsoft:20200502172859p:plain

ニュース等では棒グラフにしていることが多いですね。

日付表示等の整形も交えつつそれっぽいグラフを表示してみます。

import matplotlib.ticker as ticker
from matplotlib.dates import DateFormatter

# 日付表示を3日起きにしてみる
fig, ax = plt.subplots(figsize=(15,7))
ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
ax.xaxis.set_major_locator(ticker.LinearLocator(int(len(tmp_summary) / 3)))
ax.set_xlim([datetime.datetime(2020, 3, 15), datetime.datetime(2020, 5, 2)])

ax.bar(tmp_summary.index, tmp_summary['当日陽性者'])

f:id:mwsoft:20200502172405p:plain

だいぶそれっぽいグラフにできました。

移動平均

感染者数は日によってバラつきがあり日曜はあまり検査されないという特徴もある為、7日間で移動平均を取って数字を均すと良さそうです。

summary['当日陽性者(移動平均)'] = summary['当日陽性者'].rolling(7, center=True).mean()

# axの設定処理の共通化
def set_xticks(ax, df, start_date, end_date):
    df = df[(df.index >= start_date) & (df.index <= end_date)]
    ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
    ax.xaxis.set_major_locator(ticker.LinearLocator(int(len(df) / 3)))
    ax.set_xlim([start_date, end_date])
    return ax

fig, ax = plt.subplots(figsize=(15,7))
ax.bar(summary.index, summary['当日陽性者'], alpha=0.7)
ax.bar(summary.index, summary['当日陽性者(移動平均)'], alpha=0.5)
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 5, 2))

f:id:mwsoft:20200502205806p:plain

移動平均では4/11-13頃を頂点として徐々に減少していることがわかります。

検査数と感染者数

検査数と感染者数を並べてplotしてみます。

検査数については民間の結果は金曜に集計されるという情報もあるので、直近のデータは後々修正されるそうです。

移動平均を取ってアテになりそうな4/29までのデータをplotしてみます。

# 当日検査人数も移動平均を取ります
summary['当日検査実施人数(移動平均)'] = summary['当日検査実施人数'].rolling(7, center=True).mean()

fig, ax = plt.subplots(figsize=(15,7))
columns = ['当日陽性者', '当日陽性者(移動平均)', '当日検査実施人数', '当日検査実施人数(移動平均)']
for col in columns:
    ax.plot(summary.index, summary[col], alpha=0.5, linewidth=4)
ax.legend(columns)
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 4, 30))

f:id:mwsoft:20200502214729p:plain

緑が検査数、赤が検査数の移動平均。検査数は4月中旬までは増加傾向でその後は横ばいのようです。

両者のスケールに差があって見づらいので、Y軸をそれぞれ0〜MAXに指定してみます。

fig, ax = plt.subplots(figsize=(15,7))
ax2 = ax.twinx()
ax.bar(summary.index, summary['当日陽性者(移動平均)'], color='red', alpha=0.3)
ax.set_ylim(0, max(summary['当日陽性者(移動平均)'].fillna(0.)) * 1.2)
ax2.bar(summary.index, summary['当日検査実施人数(移動平均)'], color='blue', alpha=0.3)
ax2.set_ylim(0, max(summary['当日検査実施人数(移動平均)'].fillna(0.)) * 1.2)

ax.legend(['当日陽性者(移動平均)'], loc=2)
ax2.legend(['当日検査実施人数(移動平均)'], loc=1)
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 4, 30))

f:id:mwsoft:20200502223217p:plain

検査数(蒼)が高止まりしているのに対して徐々に陽性者数(赤)が落ちているのがわかります。

陽性率

当日陽性者 / 当日検査実施人数を陽性率としてグラフに追加します。

# 陽性率を移動平均から出します
summary['陽性率(移動平均)'] = summary['当日陽性者(移動平均)'] / summary['当日検査実施人数(移動平均)']

fig, ax = plt.subplots(figsize=(15,7))
ax2 = ax.twinx()
ax3 = ax.twinx()
ax.bar(summary.index, summary['当日陽性者(移動平均)'], color='red', alpha=0.3)
ax.set_ylim(0, max(summary['当日陽性者(移動平均)'].fillna(0.)) * 1.2)
ax2.bar(summary.index, summary['当日検査実施人数(移動平均)'], color='blue', alpha=0.3)
ax2.set_ylim(0, max(summary['当日検査実施人数(移動平均)'].fillna(0.)) * 1.2)

# 陽性率は0%〜25%までを範囲とし、tickerはoffで表示しておきます
ax3.plot(summary.index, summary['陽性率(移動平均)'], color='#333333')
ax3.axis('off')
ax3.set_ylim(0., 0.25)

ax.legend(['当日陽性者(移動平均)'], loc=2)
ax2.legend(['当日検査実施人数(移動平均)'], loc=1)
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 4, 30))

f:id:mwsoft:20200502223326p:plain

黒の線が陽性率の推移です。感染数が多かった時期をピークに徐々に減少していることがわかります。

陽性率の実際の数値を表示。

summary[(summary.index >= '2020-04-01') & (summary.index <= '2020-04-29')]['陽性率(移動平均)']
日付 陽性率(移動平均)
2020-04-01 0.100958
2020-04-02 0.111384
2020-04-03 0.126063
2020-04-04 0.105643
2020-04-05 0.098617
2020-04-06 0.105960
2020-04-07 0.132352
2020-04-08 0.138506
2020-04-09 0.149766
2020-04-10 0.156903
2020-04-11 0.151039
2020-04-12 0.161367
2020-04-13 0.137068
2020-04-14 0.127414
2020-04-15 0.129117
2020-04-16 0.126142
2020-04-17 0.109958
2020-04-18 0.119413
2020-04-19 0.109208
2020-04-20 0.109963
2020-04-21 0.105113
2020-04-22 0.095640
2020-04-23 0.088046
2020-04-24 0.088559
2020-04-25 0.077078
2020-04-26 0.080130
2020-04-27 0.085493
2020-04-28 0.067442
2020-04-29 0.058031

4/12をピークに徐々に減少しています。

重傷者数

カラム「人工呼吸器又は集中治療室に入院している者」を重傷者として扱かい、陽性者数と並べて表示してみます。

# 重傷者数の移動平均
summary['当日重傷者(移動平均)'] = summary['当日重傷者'].rolling(7, center=True).mean()

fig, ax = plt.subplots(figsize=(15,7))
ax2 = ax.twinx()
ax.bar(summary.index, summary['当日陽性者(移動平均)'], color='red', alpha=0.3)
ax.set_ylim(0, max(summary['当日陽性者(移動平均)'].fillna(0.)) * 1.2)
ax2.bar(summary.index, summary['当日重傷者(移動平均)'], color='blue', alpha=0.3)
ax2.set_ylim(0, max(summary['当日重傷者(移動平均)'].fillna(0.)) * 1.2)

ax.legend(['当日陽性者(移動平均)'], loc=2)
ax2.legend(['当日重傷者(移動平均)'], loc=1)
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 4, 29))

赤が感染者、青が重傷者です。

f:id:mwsoft:20200502223612p:plain

重傷者の発生数は日に10人程度なのである程度ブレはありますが、感染者数に対して数日遅れで増加する傾向が見て取れます。

死亡者数

陽性者と重傷者のグラフに死亡者数を黒の線で書き足します。

Y軸は重傷者数と同じものを使っています。

# 死亡者数の移動平均
summary['当日死亡者(移動平均)'] = summary['当日死亡者'].rolling(7, center=True).mean()

fig, ax = plt.subplots(figsize=(15,7))
ax2 = ax.twinx()
ax.bar(summary.index, summary['当日陽性者(移動平均)'], color='red', alpha=0.3)
ax.set_ylim(0, max(summary['当日陽性者(移動平均)'].fillna(0.)) * 1.2)
ax2.bar(summary.index, summary['当日重傷者(移動平均)'], color='blue', alpha=0.3)
ax2.set_ylim(0, max(summary['当日重傷者(移動平均)'].fillna(0.)) * 1.2)
ax2.plot(summary.index, summary['当日死亡者(移動平均)'], color='#333333')

ax.legend(['当日陽性者(移動平均)'], loc=2)
ax2.legend(['当日死亡者(移動平均)', '当日重傷者(移動平均)'], loc=1)
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 4, 29))

黒線が死亡者数の推移です。

f:id:mwsoft:20200502224541p:plain

重傷者数から遅れてピークが来るのは納得ができるところですが、ICUに行く前に死亡している人もいるせいか、当日重傷者と当日死亡者の数がかなり近くなっているのが少し気になりました。

summary[summary['日付'] >= '2020-04-01'][['当日重傷者', '当日死亡者']].sum()

    #=> 当日重傷者    269.0
    #=> 当日死亡者    250.0

医療や検査の体制が充実した場合にこの数字がどう変化するかは気になるところです。

感染者数と退院者数

感染者数と退院者数を比較して、感染から退院までにどの程度の遅れがあるか確認します。

# 退院者数の移動平均
summary['当日退院者(移動平均)'] = summary['当日退院者'].rolling(7, center=True).mean()

fig, ax = plt.subplots(figsize=(15,7))
ax2 = ax.twinx()
ax.bar(summary.index, summary['当日陽性者(移動平均)'], color='red', alpha=0.3)
ax.set_ylim(0, max(summary['当日陽性者(移動平均)'].fillna(0.)) * 1.2)
ax2.bar(summary.index, summary['当日退院者(移動平均)'], color='blue', alpha=0.3)
ax2.set_ylim(0, max(summary['当日退院者(移動平均)'].fillna(0.)) * 1.2)

ax.legend(['当日陽性者(移動平均)'], loc=2)
ax2.legend(['当日退院者(移動平均)'], loc=1)
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 5, 2))

f:id:mwsoft:20200502224623p:plain

ん? なんだこの最後の突起は。

4/25以降の数字を確認してみます。

summary[summary.index >= '2020-04-25'][['退院者', '当日退院者']]
日付 退院者 当日退院者
2020-04-25 1633 94.0
2020-04-26 1734 101.0
2020-04-27 1804 70.0
2020-04-28 1903 99.0
2020-04-29 1991 88.0
2020-04-30 2056 65.0
2020-05-01 2390 334.0

5/1に334人の退院者がカウントされているのが原因なようです。

都道府県別の数字によると東京都が153人。ゴールデンウィーク開始前日なので何かしら事情がありそうな数字ですが。

入院患者数

現在入院している患者数を仮に「入院治療を要する者 - 死亡者 - 退院者」としてざっくりとした数字を出してみます。

この計算では実際の値とは多少誤差が出るらしいので参考までに。

# 入院患者数
summary['入院者'] = summary['入院治療を要する者'] - summary['死亡者'] - summary['退院者']
fig, ax = plt.subplots(figsize=(15,7))
ax.bar(summary.index, summary['入院者'])
set_xticks(ax, summary, datetime.datetime(2020, 3, 15), datetime.datetime(2020, 5, 2))

f:id:mwsoft:20200502224116p:plain

4/15に新規感染者が頭打ちしたのに対して、退院数は5/1までは減少には転じていないことがわかります。

改定履歴

Author: Masato Watanabe, Date: 2020-05-03, 記事投稿