Quand la pandémie va-t-elle décliner? Évaluation en Python avec Pandas

image
Bonjour à tous.

J'ai vu plusieurs tableaux de bord sur COVID-19, mais je n'ai pas encore trouvé l'essentiel - la prévision du moment de la récession de l'épidémie. Par conséquent, j'ai écrit un petit script Python. Il prend les données des tableaux de l'OMS sur Github, les présente par pays et trace des lignes de tendance. Et il fait des prédictions en fonction de celles-ci - quand dans chaque pays du TOP 20 par le nombre de cas de COVID-19, une baisse des infections peut être attendue. J'avoue, je ne suis pas un expert dans le domaine de l'épidémiologie. Les calculs ci-dessous sont basés sur des données accessibles au public et sur le bon sens. Tous les détails sont sous coupe.

Mise à jour du 04/10/2020 - le tableau principal et les graphiques ont été mis à jour.

Mise à jour 16/04/2020 - Utilisateurh3cksla fait une mise à jour des graphiques et les a affichés sur son site Web .

Mise à jour du 29/04/2020 - Il est maintenant devenu évident que les courbes du nombre quotidien d'infections sont très différentes selon les pays. Et cela semble dépendre fortement de l'étendue de l'infection. Une courbe en forme de cloche idéale sur le modèle de la Corée du Sud est probablement une exception à la règle. Une forme plus courante consiste en une forte augmentation jusqu'à un pic et une diminution ultérieure qui est plus étendue au fil du temps. Par conséquent, en réalité, la baisse de l'incidence durera plus longtemps que celle prévue par ce modèle début avril.

UFO Care Minute


COVID-19 — , SARS-CoV-2 (2019-nCoV). — , /, .



, .

, , .

: |


Au début, afin d'évaluer le moment de l'épidémie, j'ai utilisé des tableaux de bord accessibles au public. L'un des premiers était le site Web du Center for Systems Science and Engineering de l'Université Johns Hopkins.

image

Il est assez joli avec sa simplicité inquiétante. Jusqu'à récemment, il ne permettait pas de construire la dynamique de l'augmentation des infections par jours, mais il a été corrigé. Et c'est curieux car cela vous permet de voir la propagation d'une pandémie sur une carte du monde. Compte tenu de la gamme noire et rouge du tableau de bord, il n'est pas recommandé de le regarder pendant longtemps. Je pense que cela peut être lourd de l'apparition de crises d'anxiété, se transformant en diverses formes de panique.

Afin de garder le doigt sur le pouls, j'ai aimé la page sur COVID-19 plussur la ressource Worldometer. Il contient des informations sur les pays sous forme de tableau:

image

Lorsque vous cliquez sur le pays souhaité, nous tombons dans des informations plus détaillées à son sujet, y compris les courbes quotidiennes et cumulatives pour les infectés / récupérés / morts.

Il existe d'autres tableaux de bord. Par exemple, pour des informations détaillées sur notre pays, vous pouvez simplement taper «COVID-19» dans la ligne de recherche Yandex et vous trouverez ceci:

image

Eh bien, peut-être que cela s'arrêtera. Ni sur ces trois tableaux de bord, ni sur plusieurs autres, j'ai pu trouver des informations avec des prévisions quand la pandémie va enfin décliner.

Un peu sur les graphiques


Actuellement, l'infection ne fait que gagner du terrain et les plus intéressants sont les graphiques avec une augmentation quotidienne du nombre de cas. Voici un exemple pour les États-Unis:

image

on constate que le nombre de personnes infectées augmente chaque jour. Bien entendu, sur de tels graphiques, nous ne parlons pas de tous ceux qui ont été infectés (il n'est pas possible de déterminer leur nombre exact), mais uniquement des cas confirmés. Mais pour être bref, nous appellerons ci-après ces personnes infectées confirmées simplement «infectées».

À quoi ressemble une pandémie apprivoisée ? Un exemple ici est un graphique de la Corée du Sud:

image

On peut voir que l'accroissement quotidien des infectés a d'abord augmenté, puis atteint un pic, baissé et fixé à un certain niveau constant (environ 100 personnes par jour). Ce n'est pas une victoire complète sur le virus, mais un succès significatif. Cela a permis au pays de démarrer l'économie (même si, disent-ils, les Coréens ont réussi à ne pas la ralentir) et de retrouver une vie moins normale.

On peut supposer que la situation dans d'autres pays évoluera de la même manière. Autrement dit, la croissance au premier stade sera remplacée par la stabilisation, puis le nombre de personnes malades diminuera chaque jour. Le résultat est une courbe en forme de cloche. Cherchons les sommets des courbes des pays les plus «infectés».

Télécharger et traiter les données


Les données de distribution des coronavirus sont mises à jour régulièrement ici . Les tables source contiennent des courbes cumulatives, elles peuvent être récupérées et prétraitées avec un petit script.

Comme ça
import pandas as pd
import ipywidgets as widgets
from ipywidgets import interact
import matplotlib.pyplot as plt

pd.set_option('display.max_columns', None)

#  
url = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/'
confirmed = pd.read_csv(url + 'time_series_covid19_confirmed_global.csv', sep = ',')
deaths = pd.read_csv(url + 'time_series_covid19_deaths_global.csv', sep = ',')
recovered = pd.read_csv(url + 'time_series_covid19_recovered_global.csv', sep = ',')

#   -   'dd.mm.yy'
new_cols = list(confirmed.columns[:4]) + list(confirmed.columns[4:].map(lambda x: '{0:02d}.{1:02d}.{2:d}'.format(int(x.split(sep='/')[1]), int(x.split(sep='/')[0]), int(x.split(sep='/')[2]))))
confirmed.columns = new_cols
recovered.columns = new_cols
deaths.columns = new_cols

#     
confirmed_daily = confirmed.copy()
confirmed_daily.iloc[:,4:] = confirmed_daily.iloc[:,4:].diff(axis=1)
deaths_daily = deaths.copy()
deaths_daily.iloc[:,4:] = deaths_daily.iloc[:,4:].diff(axis=1)
recovered_daily = recovered.copy()
recovered_daily.iloc[:,4:] = recovered_daily.iloc[:,4:].diff(axis=1)

#       
smooth_conf_daily = confirmed_daily.copy()
smooth_conf_daily.iloc[:,4:] = smooth_conf_daily.iloc[:,4:].rolling(window=8, min_periods=2, center=True, axis=1).mean()
smooth_conf_daily.iloc[:,4:] = smooth_conf_daily.iloc[:,4:].round(1)

#   ,    
last_date = confirmed.columns[-1]

#  20       
confirmed_top = confirmed.iloc[:, [1, -1]].groupby('Country/Region').sum().sort_values(last_date, ascending = False).head(20)
countries = list(confirmed_top.index)

# ,      20 
conf_tot_ratio = confirmed_top.sum() / confirmed.iloc[:, 4:].sum()[-1]

#    
# countries.append('Russia')

#       ,   
l1 = 'Infected at ' + last_date + ' - ' + str(confirmed.iloc[:, 4:].sum()[-1])
l2 = 'Recovered at ' + last_date + ' - ' + str(recovered.iloc[:, 4:].sum()[-1])
l3 = 'Dead at ' + last_date + ' - ' + str(deaths.iloc[:, 4:].sum()[-1])

#      
fig, ax = plt.subplots(figsize = [16,6])
ax.plot(confirmed.iloc[:, 4:].sum(), '-', alpha = 0.6, color = 'orange', label = l1)
ax.plot(recovered.iloc[:, 4:].sum(), '-', alpha = 0.6, color = 'green', label = l2)
ax.plot(deaths.iloc[:, 4:].sum(), '-', alpha = 0.6, color = 'red', label = l3)

ax.legend(loc = 'upper left', prop=dict(size=12))
ax.xaxis.grid(which='minor')
ax.yaxis.grid()
ax.tick_params(axis = 'x', labelrotation = 90)
plt.title('COVID-19 in all countries. Top 20 countries consists {:.2%} of total confirmed infected cases.'.format(conf_tot_ratio[0]))
plt.show()


Ainsi, vous pouvez obtenir les mêmes courbes que sur les tableaux de bord:

image

il y a un petit plus dans le script qui n'est pas nécessaire pour afficher le graphique, mais qui sera nécessaire dans les étapes suivantes. Par exemple, j'ai immédiatement formé une liste de 20 pays avec le nombre maximum de personnes infectées aujourd'hui. Soit dit en passant - c'est 90% de tous les infectés.

Passez. Les chiffres de l'augmentation quotidienne des malades sont assez «bruyants». Par conséquent, il serait bon de lisser le graphique. Ajoutez une ligne de tendance aux graphiques quotidiens (j'ai utilisé une moyenne mobile centrée). Cela se révèle en quelque sorte comme ceci:

image

La tendance est représentée en noir. Il s'agit d'une courbe assez lisse avec un seul pic. Reste à trouver le début de l'épidémie de la maladie (flèche rouge) et le pic d'incidence (vert). Avec un pic, tout est simple - c'est le maximum sur les données lissées. De plus, si le maximum tombe à l'extrême droite du graphique, le pic n'a pas encore été dépassé. Si le maximum reste à gauche - très probablement, le pic est resté dans le passé.

Le début de la rafale peut être trouvé de différentes manières. Supposons par souci de simplicité que c'est le moment où le nombre de personnes infectées par jour commence à dépasser 1% du nombre maximum de personnes infectées par jour.

Le graphique est assez symétrique. C'est-à-dire que la durée entre le début de l'épidémie et un pic est approximativement égale à la durée du pic au déclin.Ainsi, si nous trouvons le nombre de jours du début au pic, alors environ le même nombre de jours devra attendre la fin de l'épidémie .

Nous allons mettre toute cette logique dans le script suivant.

Dans un tel
from datetime import timedelta, datetime

#     20  + .
confirmed_top = confirmed_top.rename(columns={str(last_date): 'total_confirmed'})
dates = [i for i in confirmed.columns[4:]]

for country in countries:
    
    #       ,      ,  
    confirmed_top.loc[country, 'total_confirmed'] = confirmed.loc[confirmed['Country/Region'] == country,:].sum()[4:][-1]
    confirmed_top.loc[country, 'last_day_conf'] = confirmed.loc[confirmed['Country/Region'] == country,:].sum()[-1] - confirmed.loc[confirmed['Country/Region'] == country,:].sum()[-2]
    confirmed_top.loc[country, 'mortality, %'] = round(deaths.loc[deaths['Country/Region'] == country,:].sum()[4:][-1]/confirmed.loc[confirmed['Country/Region'] == country,:].sum()[4:][-1]*100, 1)
    
    #       .
    smoothed_conf_max = round(smooth_conf_daily[smooth_conf_daily['Country/Region'] == country].iloc[:,4:].sum().max(), 2)
    peak_date = smooth_conf_daily[smooth_conf_daily['Country/Region'] == country].iloc[:,4:].sum().idxmax()
    peak_day = dates.index(peak_date)
    
    #      ,      1%    
    start_day = (smooth_conf_daily[smooth_conf_daily['Country/Region'] == country].iloc[:,4:].sum() < smoothed_conf_max/100).sum()
    start_date = dates[start_day-1]
    
    #     
    confirmed_top.loc[country, 'trend_max'] = smoothed_conf_max
    confirmed_top.loc[country, 'start_date'] = start_date
    confirmed_top.loc[country, 'peak_date'] = peak_date
    confirmed_top.loc[country, 'peak_passed'] = round(smooth_conf_daily.loc[smooth_conf_daily['Country/Region'] == country, last_date].sum(), 2) != smoothed_conf_max
    confirmed_top.loc[country, 'days_to_peak'] = peak_day - start_day
    
    #   ,    
    if confirmed_top.loc[country, 'peak_passed']:
         confirmed_top.loc[country, 'end_date'] = (datetime.strptime(confirmed_top.loc[country, 'peak_date']+'20', "%d.%m.%Y").date() + timedelta(confirmed_top.loc[country, 'days_to_peak'])).strftime('%d.%m.%Y')

#    
confirmed_top.loc['China', 'start_date'] = '22.01.20'
confirmed_top


À la sortie, nous obtenons un tel tableau pour les 20 premiers pays.

Mise à jour du 10.04.2020: au moment de la rédaction du présent rapport, la Russie n'était pas dans le top vingt, mais le 7 avril, elle figurait à la 20e place. Le 10 avril est parti le 18. Le tableau comprend également les dates des mesures de quarantaine dans différents pays.

image

Les colonnes suivantes sont dans le tableau:
total_confirmed - le nombre total de personnes infectées dans le pays;
last_day_conf - le nombre de personnes infectées le dernier jour;
mortalité,% - taux de mortalité (nombre de morts / nombre de personnes infectées);
trend_max - tendance maximale;
date_début - date de début de l'épidémie dans le pays;
peak_date - date de pointe;
peak_passed - indicateur de pic (si True - pic réussi);
days_to_peak - combien de jours se sont écoulés depuis le début jusqu'au pic;
end_date - date de fin de l'épidémie.

Mise à jour 10.04.2020: le pic d'incidence a été atteint dans 14 pays sur 20. De plus, en moyenne, du début des infections de masse au pic, il faut en moyenne 25 jours:

image

selon la logique ci-dessus, d'ici fin avril, la situation devra se normaliser dans presque tous les pays considérés . Tel qu'il sera, seul le temps nous le dira.

Mise à jour 10.04.2020: Vous pouvez voir que les graphiques des pays européens, contrairement au graphique de la Corée du Sud, connaissent une récession plus douce après le pic.

Il existe un autre script qui vous permet d'afficher des graphiques pour chacun des pays.

Cette
@interact
def plot_by_country(country = countries):
    
    #   
    l1 = 'Infected at ' + last_date + ' - ' + str(confirmed.loc[confirmed['Country/Region'] == country,:].sum()[-1])
    l2 = 'Recovered at ' + last_date + ' - ' + str(recovered.loc[recovered['Country/Region'] == country,:].sum()[-1])
    l3 = 'Dead at ' + last_date + ' - ' + str(deaths.loc[deaths['Country/Region'] == country,:].sum()[-1])
    
    #        
    df = pd.DataFrame(confirmed_daily.loc[confirmed_daily['Country/Region'] == country,:].sum()[4:])
    df.columns = ['confirmed_daily']
    df['recovered_daily'] = recovered_daily.loc[recovered_daily['Country/Region'] == country,:].sum()[4:]
    df['deaths_daily'] = deaths_daily.loc[deaths_daily['Country/Region'] == country,:].sum()[4:]
    df['deaths_daily'] = deaths_daily.loc[deaths_daily['Country/Region'] == country,:].sum()[4:]
    df['smooth_conf_daily'] = smooth_conf_daily.loc[smooth_conf_daily['Country/Region'] == country,:].sum()[4:]
    
    #  
    fig, ax = plt.subplots(figsize = [16,6], nrows = 1)
    plt.title('COVID-19 dinamics daily in ' + country)
    
    #      ,   
    ax.bar(df.index, df.confirmed_daily, alpha = 0.5, color = 'orange', label = l1)
    ax.bar(df.index, df.recovered_daily, alpha = 0.6, color = 'green', label = l2)
    ax.bar(df.index, df.deaths_daily, alpha = 0.7, color = 'red', label = l3)
    ax.plot(df.index, df.smooth_conf_daily, alpha = 0.7, color = 'black')
    
    #     .
    start_date = confirmed_top[confirmed_top.index == country].start_date.iloc[0]
    start_point = smooth_conf_daily.loc[smooth_conf_daily['Country/Region'] == country, start_date].sum()
    ax.plot_date(start_date, start_point, 'o', alpha = 0.7, color = 'black')
    shift = confirmed_top.loc[confirmed_top.index == country, 'trend_max'].iloc[0]/40
    plt.text(start_date, start_point + shift, 'Start at ' + start_date, ha ='right', fontsize = 20)
    
    peak_date = confirmed_top[confirmed_top.index == country].peak_date.iloc[0]
    peak_point = smooth_conf_daily.loc[smooth_conf_daily['Country/Region'] == country, peak_date].sum()
    ax.plot_date(peak_date, peak_point, 'o', alpha = 0.7, color = 'black')
    plt.text(peak_date, peak_point + shift, 'Peak at ' + peak_date, ha ='right', fontsize = 20)
    
    ax.xaxis.grid(False)
    ax.yaxis.grid(False)
    ax.tick_params(axis = 'x', labelrotation = 90)
    ax.legend(loc = 'upper left', prop=dict(size=12))
    
    #         .
    max_pos = max(df['confirmed_daily'].max(), df['recovered_daily'].max())
    if confirmed_top[confirmed_top.index == country].peak_passed.iloc[0]:
        estimation = 'peak is passed - end of infection ' + confirmed_top[confirmed_top.index == country].end_date.iloc[0]
    else:
        estimation = 'peak is not passed - end of infection is not clear'
    plt.text(df.index[len(df.index)//2], 3*max_pos//4, country, ha ='center', fontsize = 50)
    plt.text(df.index[len(df.index)//2], 2*max_pos//3, estimation, ha ='center', fontsize = 20)
    
    #plt.savefig(country + '.png', bbox_inches='tight', dpi = 75)
    plt.show()


Voici la situation actuelle en Russie (mise à jour du 04/10/2020):

image

Malheureusement, le pic n'a pas encore été franchi, il est donc difficile de faire des prévisions concernant le moment de la baisse de l'incidence. Mais étant donné que l'épidémie dure 26 jours à l'heure actuelle, nous pouvons nous attendre à ce que pendant la semaine nous assistions à un pic et que l'incidence commence à diminuer. Je me risquerais à suggérer qu'au début du mois de mai la situation se normalise (j'ai toujours été optimiste, je dois dire).

Merci beaucoup d'avoir lu jusqu'au bout. Soyez en bonne santé et la force sera avec nous. Vous trouverez ci-dessous des graphiques pour le reste des vingt pays par ordre décroissant du nombre de personnes infectées. Si vous avez besoin de données plus récentes - vous pouvez exécuter les scripts donnés dans le texte, tout sera recalculé pour la date actuelle.

Mise à jour 10.04.2020 - les graphiques sont mis à jour.

Etats-Unis
image

Espagne
image

Italie
image

Allemagne
image

France
image

Chine
image

J'ai couru
image

Royaume-Uni
image

dinde
image

Suisse
image

Belgique
image

Pays-Bas
image

Canada
image

L'Autriche
image

le Portugal
image

Brésil
image

Corée du Sud
image

Israël
image

Suède
image

Norvège
image

All Articles