overflow33の日記

python 機械学習 系の記事を書いて行きたい所存

COVID-19 感染から回復、死亡までの日数比較

概要と結論

日本では、COVID-19に罹ってから、回復、死亡するまでにどのくらい時間がかかっているのか、考えてみた。
各国の1週あたりの感染者数、回復者数、死亡者数の推移から、医療現場の状況を推察した。
分析では、感染者一人ひとりのデータを追うのは大変なので、日別の感染者数から、大体の目安を付けることにする。

・日本では、感染確認から回復確認までに約14日程度(10-20日)、感染確認から死亡確認まで26日程度(20-30日)
医療崩壊が起こると、1週あたりの回復者と死者の割合が 1:1 に近づき、回復者<死者となる場合がある

詳細

データ処理の内容

各日の確認数から 前4日、後3日の合計7日(=1週)の合計値を計算し、プロットした。
1日当たりだと、雑音が多く土日の影響も出てしまうため、移動合計を利用した。
縦軸の数値は、その日を基準にしたときの、1週あたりの人数を表している。

見やすさのため縦軸は、対数。
横軸は、2020/01/22を Day 0 とした。

日本の場合

f:id:overflow33:20200405205945p:plain

日本の場合、第一波と第二波が見られるのが特徴である。
第一波に注目する。
感染者数のピークが、約Day 10、収束が 約Day 17 である。
回復者数のピークは、約Day 25、収束が 約Day 34 である。
死者数のピークは、約Day 37、収束が 約Day 42 である。

ピークと収束の間隔から
感染確認から回復確認までの日数は、だいたい14-15日。
感染確認から死亡確認までの日数は、だいたい 25-27日。
となる。

第二波に関しては、まだピークも収束も迎えておらず、推定が難しい。

イタリア、スペイン、アメリカの場合

f:id:overflow33:20200405210022p:plainf:id:overflow33:20200405210042p:plainf:id:overflow33:20200405210057p:plain

控えめに言ってヤバい。
なにがヤバいって、1weekあたりの死者数と回復者数がほぼ同じ。
回復者と死者が同相で増えていく。
さらに、アメリカでは、回復者よりも死亡者の位相が早い(最近になって、追いついては来たが)。
病院に来て、医者が診たときには、重症もしくは瀕死となった方が多数いることが容易に想像できる。
完全に医療崩壊である。

オランダの場合

f:id:overflow33:20200405210457p:plain
これはもっとヤバい。
少し前まで感染確認後、平均10日程度で、かなりの割合で人が亡くなっている!
しかも、回復者は死亡者より少ない状況が続いている。
感染者数の増加ペースが鈍っていることだけが救いである。
(もはや検査すらできていない可能性もあるが、そうでないことを願う…。)

韓国、ドイツの場合

f:id:overflow33:20200405210012p:plainf:id:overflow33:20200405210113p:plain
感染者数>回復者数>死者数
となっており、医療が機能してる様子が伺える。
ドイツの感染者数は非常に多いが、落ち始めており、この山を越えれば大丈夫だろう。
あと1-2週が山に見える。
韓国は、非常に上手く制御されている。
感染者数も少なく、死者の増加も見られない。
各国が韓国を手本にする理由がよく分かる。

中国の場合

f:id:overflow33:20200405210203p:plain
死者が隠されていないか、心配になるグラフである。
感染拡大後、死者のピークが先に来ているが、回復者数も一気に増えていく。
感染者が重症化する前に医療が機能して、命を助けた結果であると信じたい。
この数字が本当なら、巨大な隔離施設を作った甲斐があったといえる。

データのソース

2019 Novel Coronavirus COVID-19 (2019-nCoV) Data Repository by Johns Hopkins CSSE
ArcGIS Dashboards
GitHub - CSSEGISandData/COVID-19: Novel Coronavirus (COVID-19) Cases, provided by JHU CSSE

解析コード

main.py

from os import path

import matplotlib.pyplot as plt

import loadData
import myLoadModule as mLM

from matplotlib import rcParams
rcParams['font.family'] = 'IPAexGothic'
rcParams['font.size'] = 20

def main():
    folder = "data"
    deathfileName = "200404_death.csv"
    confirmedfileName = "200404_confirmed.csv"
    recoveredfileName = "200404_recovered.csv"

    saveFolder = "fig_200404"

    deathfilePath = path.join(folder, deathfileName)
    confirmedfilePath = path.join(folder, confirmedfileName)
    recoveredfilePath = path.join(folder, recoveredfileName)

    deathData = loadData.data()
    deathData.load(deathfilePath)

    confirmedData = loadData.data()
    confirmedData.load(confirmedfilePath)

    recoveredData = loadData.data()
    recoveredData.load(recoveredfilePath)

    countryList = confirmedData.countryList
    DataList = [deathData, confirmedData, recoveredData]
    labelList = ['死亡者数', '確認数', '回復数']
    my_plot(countryList, DataList, labelList, saveFolder)


def my_plot(contryList, DataList, labelList, saveFolder=None):

    dpi = 96
    width = 1920*0.8
    height = 1080*0.8

    for contry in contryList:
        for Data in DataList:
            fig = plt.figure(1, figsize=(width / 100, height / 100), dpi=dpi)
            ax = fig.add_subplot(1, 1, 1)

            plot_y = Data.getCountryDataPerDay7(contry)
            ax.semilogy(plot_y)
            # ax.plot(plot_y)
            ax.grid(True)
            ax.set_title(contry)
            ax.set_ylabel('[7日間移動合計 人数]')
            ax.set_xlabel('[日]')
        ax.legend(labelList)

        plt.tight_layout()

        if saveFolder is None:
            plt.show()
        else:
            mLM.checkFolder(saveFolder)
            saveFilePath = path.join(saveFolder, contry.replace('*', '') + '.png')
            fig.savefig(saveFilePath)

        plt.close(fig)

if __name__ == "__main__":
    main()

loadData.py

from os import path

import pandas as pd

class data:
    def __init__(self):
        self.filePath = ""
        self.df = pd.DataFrame([])
        self.countryDf = pd.DataFrame([])
        self.countryDfDay = pd.DataFrame([])
        self.countryDfDay7 = pd.DataFrame([])
        self.countryList = []

    def load(self, filePath):
        self.filePath = filePath
        self.df = pd.read_csv(filePath)

        # print(self.df)
        self.make_countryData()
        self.makePerDayData()

    # 国別のデータにする
    def make_countryData(self):
        col = "Country/Region"
        self.countryList = sorted(list(set(self.df[col].values)))

        for country in self.countryList:
            idx = (self.df[col] == country)
            oneCountry = self.df[idx].sum()
            self.countryDf = pd.concat([self.countryDf, oneCountry], axis=1)
        # 緯度経度や場所の情報を取り除く
        self.countryDf.drop(index=['Lat', 'Long', 'Province/State', 'Country/Region'], inplace=True)
        # 列名を国名に付け替える
        self.countryDf.columns = self.countryList
        # print(self.countryDf)

    def makePerDayData(self):
        self.countryDfDay = self.countryDf.diff()
        self.countryDfDay7 = self.countryDfDay.rolling(7, center=True).sum()
        print(self.countryDfDay7)

    def getCountryDataPerDay(self, country):
        if country in self.countryList:
            return self.countryDfDay[country].values
        else:
            return None

    def getCountryDataPerDay7(self, country):
        if country in self.countryList:
            return self.countryDfDay7[country].values
        else:
            return None

    def getCountryData(self, country):
        if country in self.countryList:
            return self.countryDf[country].values
        else:
            return None

myLoadModule.py

from os import path
from os import mkdir

def checkFolder(dir):
    if path.exists(dir):
        pass
    else:
        mkdir(dir)