.. _2020covidrst: ======================= Algo - simulation COVID ======================= .. only:: html **Links:** :download:`notebook <2020_covid.ipynb>`, :downloadlink:`html <2020_covid2html.html>`, :download:`python <2020_covid.py>`, :downloadlink:`slides <2020_covid.slides.html>`, :githublink:`GitHub|_doc/notebooks/td1a_home/2020_covid.ipynb|*` Ou comment utiliser les mathématiques pour comprendre la propagation de l’épidémie. .. code:: ipython3 from jyquickhelper import add_notebook_menu add_notebook_menu() .. contents:: :local: .. code:: ipython3 %matplotlib inline Enoncé ------ On récupère les données du COVID par région et par âge et premier graphe ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A cette adresse : `Données relatives à l’épidémie de COVID-19 en France : vue d’ensemble `__ .. code:: ipython3 from pandas import read_csv, to_datetime url = "https://www.data.gouv.fr/en/datasets/r/d3a98a30-893f-47f7-96c5-2f4bcaaa0d71" covid = read_csv(url, sep=",") covid['date'] = to_datetime(covid['date']) covid.tail() .. raw:: html
date total_cas_confirmes total_deces_hopital total_deces_ehpad total_cas_confirmes_ehpad total_cas_possibles_ehpad patients_reanimation patients_hospitalises total_patients_gueris nouveaux_patients_hospitalises nouveaux_patients_reanimation
802 2022-05-13 NaN 118431 28826.0 NaN NaN 1233.0 19272.0 653050.0 756.0 77.0
803 2022-05-14 NaN 118494 28837.0 NaN NaN 1213.0 18901.0 653734.0 387.0 42.0
804 2022-05-15 NaN 118508 28844.0 NaN NaN 1214.0 18935.0 653827.0 145.0 13.0
805 2022-05-16 NaN 118633 28845.0 NaN NaN 1199.0 18742.0 654775.0 900.0 94.0
806 2022-05-17 NaN 118723 28845.0 NaN NaN 1173.0 18290.0 655984.0 883.0 106.0
.. code:: ipython3 ax = covid.set_index("date").plot( title="Evolution des hospitalisations par jour", figsize=(14, 4)) ax.set_yscale("log"); .. image:: 2020_covid_6_0.png Il y a quelques valeurs manquantes même pour les séries aggrégées… Comme je n’ai pas le courage de corriger les valeurs unes à unes, je prends un autre fichier et quelques aberrations comme le nombre de décès qui décroît, ce qui est rigoureusement impossible. .. code:: ipython3 from pandas import concat, to_datetime def extract_data(kind='deaths', country='France'): url = ( "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/" "master/csse_covid_19_data/" "csse_covid_19_time_series/time_series_covid19_%s_global.csv" % kind) df = read_csv(url) eur = df[df['Country/Region'].isin([country]) & df['Province/State'].isna()] tf = eur.T.iloc[4:] tf.columns = [kind] return tf def extract_whole_data(kind=['deaths', 'confirmed', 'recovered'], country='France'): population = { 'France': 67e6, } total = population[country] dfs = [] for k in kind: df = extract_data(k, country) dfs.append(df) conc = concat(dfs, axis=1) conc['infected'] = conc['confirmed'] - (conc['deaths'] + conc['recovered']) conc['safe'] = total - conc.drop('confirmed', axis=1).sum(axis=1) conc.index = to_datetime(conc.index) return conc covid = extract_whole_data() covid.tail() .. raw:: html
deaths confirmed recovered infected safe
2022-11-29 155309 36762891 0 36607582 30237109.0
2022-11-30 155384 36829869 0 36674485 30170131.0
2022-12-01 155459 36897875 0 36742416 30102125.0
2022-12-02 155526 36955945 0 36800419 30044055.0
2022-12-03 155526 36955945 0 36800419 30044055.0
.. code:: ipython3 import matplotlib.pyplot as plt fig, ax = plt.subplots(1, 2, figsize=(14, 4)) covid[['confirmed', 'infected']].plot(title="Evolution de l'épidémie par jour", ax=ax[0]) covid[['deaths', 'recovered']].plot(title="Evolution de l'épidémie par jour", ax=ax[1]); .. image:: 2020_covid_9_0.png Même aberration, un nombre décès qui décroît… Il faudrait comprendre pourquoi pour savoir comment réparer les données. Ou on improvise. Pour chaque observation :math:`X_t < X_{t-1}`, on calcule le ratio :math:`\frac{X_{t}}{X_{t-1}}` et on multiplie toutes les observations :math:`i 0 and X[t] == 0: X[t] = X[t-1] continue if X[t] >= X[t-1] and X[t] < X[t-1] + 200000: continue ratio = X[t] / X[t-1] for i in range(0, t): X[i] *= ratio covid_modified = covid.copy() for c in tqdm(covid.columns): values = covid_modified[c].values correct_series(values) covid_modified[c] = values covid_modified.tail() .. parsed-literal:: 100%|██████████| 5/5 [00:00<00:00, 29.74it/s] .. raw:: html
deaths confirmed recovered infected safe
2022-11-29 155309 36762891 342253 36607582 30044055.0
2022-11-30 155384 36829869 342253 36674485 30044055.0
2022-12-01 155459 36897875 342253 36742416 30044055.0
2022-12-02 155526 36955945 342253 36800419 30044055.0
2022-12-03 155526 36955945 342253 36800419 30044055.0
.. code:: ipython3 fig, ax = plt.subplots(1, 2, figsize=(14, 4)) covid_modified[['confirmed', 'infected']].plot(title="Evolution de l'épidémie par jour", ax=ax[0]) covid_modified[['deaths', 'recovered']].plot(title="Evolution de l'épidémie par jour", ax=ax[1]); .. image:: 2020_covid_12_0.png C’est mieux. .. code:: ipython3 covid = covid_modified On lisse. .. code:: ipython3 lisse = covid.rolling(7).mean() fig, ax = plt.subplots(1, 2, figsize=(14, 4)) lisse[['confirmed', 'infected']].plot(title="Evolution de l'épidémie par jour", ax=ax[0]) lisse[['deaths', 'recovered']].plot(title="Evolution de l'épidémie par jour", ax=ax[1]); .. image:: 2020_covid_16_0.png La série des *recovered* ne compte vraisemblablement que ceux qui sont passés à l’hôpital. Il faudrait recouper avec d’autres données pour être sûr. Ce sera pour un autre jour. Modèle `SIRD `__ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pour en savoir plus `Modèles compartimentaux en épidémiologie `__. On classe la population en quatre catégories : - S : personnes non contaminées - I : nombre de personnes malades ou contaminantes - R : personnes guéries (recovered) - D : personnes décédées Les gens changent de catégorie en fonction de l’évolution de l’épidémie selon les équations qui suivent : - :math:`\frac{dS}{dt} = - \beta \frac{S I}{N}` - :math:`\frac{dI}{dt} = \frac{\beta S I}{N} - \mu I - \nu I` - :math:`\frac{dD}{dt} = \nu I` - :math:`\frac{dR}{dt} = \mu I` :math:`\beta` est lié au taux de transmission, :math:`\frac{1}{\mu}` est la durée moyenne jusqu’à guérison, :math:`\frac{1}{\nu}` est la durée moyenne jusqu’au décès. Q0 : une petite fonction pour dessiner ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cette fonction servira à représenter graphiquement les résultats. .. code:: ipython3 from datetime import datetime, timedelta def plot_simulation(sim, day0=datetime(2020, 1, 1), safe=True, ax=None, title=None, logy=False, two=False, true_data=None): """ On suppose que sim est une matrice (days, 4). :param sim: la simulation :param day0: le premier jour de la simulation (une observation par jour) :param safe: ajouter les personnes *safe* (non infectées), comme elles sont nombreuses, il vaut mieux aussi cocher *logy=True* pour que cela soit lisible :param ax: axes existant (utile pour superposer), None pour un créer un nouveau :param title: titre du graphe :param logy: échelle logarithmique sur l'axe des y :param two: faire deux graphes plutôt qu'un seul pour plus de visibilité :param true_data: vraies données à tracer également en plus de celle de la simulation :return: ax """ df = DataFrame(sim, columns=['S', 'I', 'R', 'D']) # On ajoute des dates. df["date"] = [day0 + timedelta(d) for d in range(0, df.shape[0])] df = df.set_index("date") if true_data is None: tdf = None else: tdf = DataFrame(true_data, columns=['Sobs', 'Iobs', 'Robs', 'Dobs']) tdf["date"] = [day0 + timedelta(d) for d in range(0, tdf.shape[0])] tdf = tdf.set_index("date") if two: if ax is None: fig, ax = plt.subplots(1, 2, figsize=(14, 4)) if safe: if tdf is not None: tdf.drop(['Dobs'], axis=1).plot(ax=ax[0], logy=logy, linewidth=8) df.drop('D', axis=1).plot(ax=ax[0], title=title, logy=logy, linewidth=4) else: if tdf is not None: tdf.drop(['Sobs', 'Dobs'], axis=1).plot(ax=ax[0], logy=logy, linewidth=8) df.drop(['S', 'D'], axis=1).plot(ax=ax[0], title=title, logy=logy, linewidth=4) if tdf is not None: tdf['Dobs'].plot(ax=ax[1], title=title, logy=logy, linewidth=8) df[['D']].plot(ax=ax[1], title='Décès', logy=logy, linewidth=4) ax[0].legend() ax[1].legend() else: if ax is None: fig, ax = plt.subplots(1, 1, figsize=(14, 4)) if safe: if tdf is not None: tdf.plot(ax=ax, title=title, logy=logy, linewidth=8) df.plot(ax=ax, title=title, logy=logy, linewidth=4) else: if tdf is not None: tdf.drop(['Sobs'], axis=1).plot(ax=ax, title=title, logy=logy, linewidth=8) df.drop(['S'], axis=1).plot(ax=ax, title=title, logy=logy, linewidth=4) ax.legend() return ax Q1 : écrire une fonction qui calcule la propagation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ On suppose que :math:`\beta, \mu, \nu, S_0, I_0, R_0, D_0` sont connus. On rappelle le modèle : - :math:`dS = - \beta \frac{S I}{N}` - :math:`dI = \frac{\beta S I}{N} - \mu I - \nu I` - :math:`dD = \nu I` - :math:`dR = \mu I` .. code:: ipython3 import numpy .. code:: ipython3 beta = 0.5 mu = 1./14 nu = 1./21 S0 = 9990 I0 = 10 R0 = 0 D0 = 0 Il faudra compléter le petit programme suivant : .. code:: ipython3 from pandas import DataFrame def simulation(beta, mu, nu, S0, I0, R0, D0, days=14): res = numpy.empty((days+1, 4), dtype=numpy.float64) res[0, :] = [S0, I0, R0, D0] N = sum(res[0, :]) for t in range(1, res.shape[0]): dR = res[t-1, 1] * mu # .... return res sim = simulation(beta, mu, nu, S0, I0, R0, D0, 30) plot_simulation(sim); .. image:: 2020_covid_25_0.png Q2 : on veut estimer les paramètres du modèle, une fonction d’erreur ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C’est compliqué parce que… les paramètres évoluent au cours du temps, en fonction du comportement des gens, masque, pas masque, confinement, reconfinement, température, manque de tests également… Tout d’abord les vraies données. .. code:: ipython3 lisse_mars = lisse[30:] dates = lisse_mars.index france = numpy.zeros((lisse_mars.shape[0], 4), dtype=numpy.dtype) france[:, 3] = lisse_mars['deaths'] france[:, 2] = lisse_mars['recovered'] france[:, 0] = lisse_mars['safe'] france[:, 1] = lisse_mars['infected'] france[:5] .. parsed-literal:: array([[29963471.89949666, 1.1640875027069664, 3.626756310367156, 0.9858981877075415], [29963471.89949666, 1.1640875027069664, 3.626756310367156, 0.9858981877075415], [29963471.89949666, 1.1640875027069664, 3.626756310367156, 0.9858981877075415], [29963471.89949666, 1.1640875027069664, 3.626756310367156, 0.9858981877075415], [29963471.89949666, 1.1640875027069664, 4.533445387958945, 0.9858981877075415]], dtype=object) .. code:: ipython3 plot_simulation(france, dates[0], safe=False, logy=True, title="Vraies données"); .. image:: 2020_covid_28_0.png Et sur les derniers jours. .. code:: ipython3 plot_simulation(france[-60:], dates[-60], two=True, safe=False, title="Vraies données, derniers mois"); .. image:: 2020_covid_30_0.png Bref, on part du principe que le modèle est plutôt fiable sur une courte période de temps, on tire plein de paramètres aléatoires et on regarde ce qui marche le mieux. Et pour comparer deux jeux de paramètres, il faut donc une fonction d’erreur qu’on prendra comme égal à la somme des erreurs de prédictions. Maintenant il faut faire attention à ce qu’on compare. La simulation calcule les catégories de population au temps *t*, mais pas toujours les séries cumulées. La série des personnes contaminées est **transitoire dans la simulation** et **cumulées dans les données récupérées**. La première étape consiste à transformer les données simulées pour qu’elles soient comparables aux données collectées. .. code:: ipython3 def simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, days=14): # ... pass Maintenant la fonction d’erreur : .. code:: ipython3 def error(data, simulation): # ... à compléter return 0 Q3 : optimisation ~~~~~~~~~~~~~~~~~ Pour optimiser, on tire des paramètres de façon aléatoire dans un intervalle donné et on choisit ceux qui minimisent l’erreur. .. code:: ipython3 from tqdm import tqdm # pour avoir une barre de progression def optimisation(true_data, i_range=(0, 0.2), beta_range=(0, 0.5), mu_range=(0., 0.2), nu_range=(0., 0.2), max_iter=1000, error_fct=error): N = sum(true_data[0, :]) rnd = numpy.random.rand(max_iter, 4) for i, (a, b) in enumerate([i_range, beta_range, mu_range, nu_range]): rnd[:, i] = rnd[:, i] # à compléter ... err_min = None for it in tqdm(range(max_iter)): i, beta, mu, nu = rnd[it, :] D0 = true_data[0, 3] # dI0 = # S0 = # I0 = # R0 = sim = simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, days=true_data.shape[0] - 1) err = error_fct(true_data, sim) if err_min is None or err < err_min: # à compléter pass return best Q4 : dessiner les résultats ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Q5 : vérifier que cela marche sur des données synthétiques ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ On simule, on vérifie que l’optimisation retrouve les paramètres de la simulation. Q6 : sur des données réelles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Réponses -------- Q1 : propagation ~~~~~~~~~~~~~~~~ On l’applique aux données réelles. .. code:: ipython3 from datetime import datetime, timedelta from pandas import DataFrame def simulation(beta, mu, nu, S0, I0, R0, D0, days=14): res = numpy.empty((days+1, 4), dtype=numpy.float64) res[0, :] = [S0, I0, R0, D0] N = sum(res[0, :]) for t in range(1, res.shape[0]): dR = res[t-1, 1] * mu dD = res[t-1, 1] * nu dI = res[t-1, 0] * res[t-1, 1] / N * beta res[t, 0] = res[t-1, 0] - dI res[t, 1] = res[t-1, 1] + dI - dR - dD res[t, 2] = res[t-1, 2] + dR res[t, 3] = res[t-1, 3] + dD return res beta = 0.5 mu = 1./14 nu = 1./21 S0 = 9990 I0 = 10 R0 = 0 D0 = 0 sim = simulation(beta, mu, nu, S0, I0, R0, D0, 60) plot_simulation(sim, dates[60], safe=False, two=True, title="Simulation pour essayer"); .. image:: 2020_covid_47_0.png Q2 : série cumulées et fonction erreur ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ On doit d’abord calculer la simulation qui modifie :math:`I_t` en :math:`J_t` qui correspond à l’ensemble des personnes contaminées jusqu’à présent. .. code:: ipython3 def simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, days=14): res = numpy.empty((days+1, 4), dtype=numpy.float64) cum = numpy.empty((days+1, 1), dtype=numpy.float64) res[0, :] = [S0, I0, R0, D0] cum[0, 0] = I0 N = sum(res[0, :]) for t in range(1, res.shape[0]): dR = res[t-1, 1] * mu dD = res[t-1, 1] * nu dI = res[t-1, 0] * res[t-1, 1] / N * beta res[t, 0] = res[t-1, 0] - dI res[t, 1] = res[t-1, 1] + dI - dR - dD res[t, 2] = res[t-1, 2] + dR res[t, 3] = res[t-1, 3] + dD cum[t, 0] = cum[t-1, 0] + dI res[:, 1] = cum[:, 0] return res beta = 0.5 mu = 1./14 nu = 1./21 S0 = 9990 I0 = 10 R0 = 0 D0 = 0 sim = simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, 60) plot_simulation(sim, dates[60], safe=False, two=True, title="Simulation pour essayer"); .. image:: 2020_covid_49_0.png On compare avec les vraies données en gras. .. code:: ipython3 beta = 0.04 mu = 0.03 nu = 0.0001 S0, I0, R0, D0 = france[120, :] sim = simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, 120) plot_simulation(sim, dates[120], safe=False, two=True, true_data=france[120:240], title="Simulation et vraies données (en gros)"); .. image:: 2020_covid_51_0.png Pas simple de choisir des paramètres pour approximer la courbe. .. code:: ipython3 def error(data, simulation): err = (data[:, 1] - simulation[:, 1]) ** 2 + (data[:, 3] - simulation[:, 3]) ** 2 total = (numpy.sum(err) / data.shape[0]) ** 0.5 return total beta = 0.5 mu = 1./14 nu = 1./21 S0 = 9990 I0 = 10 R0 = 0 D0 = 0 sim = simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, 30) plot_simulation(sim); .. image:: 2020_covid_53_0.png On regarde si on arrive à retrouver les paramètres de la simulation. Q3, Q4, Q5 : optimisation sur des données synthétiques ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 from tqdm import tqdm # pour avoir une barre de progression def optimisation(true_data, i_range=(0, 0.2), beta_range=(0, 0.5), mu_range=(0., 0.2), nu_range=(0., 0.2), max_iter=1000, error_fct=error): N = sum(true_data[0, :]) rnd = numpy.random.rand(max_iter, 4) for i, (a, b) in enumerate([i_range, beta_range, mu_range, nu_range]): rnd[:, i] = rnd[:, i] * (b - a) + a err_min = None for it in tqdm(range(max_iter)): i, beta, mu, nu = rnd[it, :] dI0 = true_data[0, 0] * i D0 = true_data[0, 3] S0 = true_data[0, 0] - dI0 I0 = true_data[0, 1] + dI0 R0 = N - D0 - I0 - S0 sim = simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, days=true_data.shape[0] - 1) err = error_fct(true_data, sim) if err_min is None or err < err_min: err_min = err best = dict(beta=beta, mu=mu, nu=nu, I0=I0, i=i, S0=S0, R0=R0, D0=D0, err=err, sim=sim) return best beta = 0.04 mu = 0.07 nu = 0.04 S0 = 67e6 I0 = 100000 R0 = 10000 D0 = 10000 sim = simulation_cumulee(beta, mu, nu, S0, I0, R0, D0, 30) res = optimisation(sim, max_iter=2000, error_fct=error, i_range=(0., 0.001)) sim_opt = res['sim'] del res['sim'] plot_simulation(sim_opt, dates[90], safe=False, two=True, true_data=sim, title="beta=%1.3f mu=%1.3f nu=%1.3f err=%1.3g i=%1.3g" % ( res['beta'], res['mu'], res['nu'], res['err'], res['i'])); .. parsed-literal:: 100%|██████████| 2000/2000 [00:00<00:00, 7060.32it/s] .. image:: 2020_covid_56_1.png Ca ne marche pas trop mal. Q6 : sur des données réelles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 res = optimisation(france[180:], error_fct=error, i_range=(0, 0.0001)) sim = res['sim'] del res['sim'] plot_simulation(sim, dates[180], safe=False, two=True, true_data=france[180:], title="beta=%1.3f mu=%1.3f nu=%1.3f err=%1.3f i=%1.3f" % ( res['beta'], res['mu'], res['nu'], res['err'], res['i'])); .. parsed-literal:: 100%|██████████| 1000/1000 [00:03<00:00, 329.94it/s] .. image:: 2020_covid_59_1.png Ca ne marche pas super. On peut modifier l’erreur pour donner plus d’importance à la courbe des morts. .. code:: ipython3 def error_norm(data, simulation): m1 = numpy.max(simulation[:, 1]) m3 = numpy.max(simulation[:, 3]) err = (data[:, 1] - simulation[:, 1]) ** 2 / m1 ** 2 + (data[:, 3] - simulation[:, 3]) ** 2 / m3 ** 2 total = (numpy.sum(err) / data.shape[0]) ** 0.5 return total res = optimisation(france[150:], error_fct=error_norm, i_range=(0, 0.001)) sim = res['sim'] del res['sim'] plot_simulation(sim, dates[150], safe=False, two=True, true_data=france[150:], title="beta=%1.3f mu=%1.3f nu=%1.3f err=%1.3f i=%1.3f" % ( res['beta'], res['mu'], res['nu'], res['err'], res['i'])); .. parsed-literal:: 100%|██████████| 1000/1000 [00:03<00:00, 314.34it/s] .. image:: 2020_covid_61_1.png L’erreur est répartie sur l’ensemble de la courbe mais cela ne tient pas compte de la dynamique. Il faudrait prendre en compte les dérivées comme dans l’aticle `A Modified SIR Model for the COVID-19 Contagion in Italy `__. Ou partir sur une approche plus directe comme celle proposée dans l’article `Estimating and Simulating a SIRD Model of COVID-19 for Many Countries, States, and Cities `__. :math:`\frac{dS}{dt} = - \beta \frac{S I}{N}` implique que :math:`\Delta(S_t) = S_{t+1} - S_t = -\beta \frac{S_t I_t}{N}`. On peut alors calculer une sorte de :math:`\beta_t` dépendant du temps : :math:`\beta_t = -N\frac{S_{t+1} - S_t}{S_t I_t}`. Le problème, c’est :math:`I_t` qu’on ne connaît pas puisque c’est le nombre de personnes contaminantes à un instant *t*. On ne connaît pas plus :math:`S_t` mais on peut le déduire si on connaît :math:`I_t`. Cela dit, le paramètre :math:`\nu` est plus ou moins connu puisqu’il s’agit de l’inverse de la durée moyenne d’incubation jusqu’au décès. De là, on peut retrouver :math:`\beta`. On poursuit en passant au logarithme :math:`\ln \beta_t = \ln N + \ln (S_t - S_{t+1}) - \ln S_t - \ln I_t`. Le nombre de cas positifs découverts chaque jour correspond à :math:`S_t - S_{t+1}`. On ajoute que :math:`\forall t, N = S_t + I_t + R_t + D_t` et :math:`D_{t+1} - D_t = \nu I_t`. Donc : .. math:: \ln \beta_t = \ln N + \ln (S_t - S_{t+1}) - \ln (S_t - S_{t-1} + S_{t-1}) - \ln (D_{t+1} - D_t) + \ln \nu Il y a deux inconnues, :math:`\ln \beta - \ln \nu` qu’on suppose constant pendant la période et :math:`S_{t-1}`. Il suffit d’écrire ces équations sur quelques jours puis de résoudre le système d’équations. .. math:: \begin{array}{l} \ln \beta - \ln \nu = \ln N + \ln (S_t - S_{t+1}) - \ln (S_t - S_{t-1} + S_{t-1}) - \ln (D_{t+1} - D_t) \\ \ln \beta - \ln \nu = \ln N + \ln (S_{t+1} - S_{t+2}) - \ln (S_{t+1} - S_{t} + S_{t} - S_{t-1} + S_{t-1}) - \ln (D_{t+2} - D_{t+1}) \end{array} D’autres directions sont possibles comme `The Parameter Identification Problem for SIR Epidemic Models: Identifying Unreported Cases `__ qui propose d’estimer les paramètres sur une plus grande période. D’autres articles : `Introduction to inference: parameter estimation `__, `Data-based analysis, modelling and forecasting of the COVID-19 outbreak `__, `Identification and estimation of the SEIRD epidemic model for COVID-19 `__. Quelques liens : - `CovidTracker `__ - `pyepidemics `__ - `COVID-19 pandemics control `__ - `modcov19 `__ - `aftercovid `__ - `COVID-19 - France `__ - `Awesome COVID19 resources `__