MODELISATION DES EPIDEMIES

Grippe H1N1 dans la province du Guangdong - 2009

Coronavirus MERS en Corée du sud - 2015

Nous allons voir comment il nous est possible de modéliser l'évolution d'une épidémie afin de prédire le nombre de malades potentiels et permettre aux pouvoirs publics d'agir en conséquence.

Deux cas d'étude ont été choisis:

La grippe H1N1 qui a sévi dans la province du Guangdong en 2009 car de nombreuses informations peuvent être trouvées à son sujet.

Le coronavirus MERS (Middle East Respiratory Syndrome) déclaré en 2015 en Corée du sud.

Attention !
Les informations utilisées dans ce tutoriel n'ont pour objectif que d'être utilisées de le cadre d'études. Elles sont issues de différentes recherches et doivent être restreintes à un cadre de simulation et non à l'application et la déduction sur des phénomènes épidémiologiques réels. Nous ne sommes ni médecin, ni épidémiologiste et ne pouvons affirmer avec certitude que les données utilisées dans les différents modèles sont exactes. L'objectif de ce tutoriel étant avant tout de démontrer comment il est possible de modéliser une évolution épidémique à l'aide de modèles mathématiques.

Modéliser à l'aide de compartiments

C'est en 1980 que sont nés les modèles épidémiologiques par compartiments, dans le but d'étudier la maladie du VIH.

Un modèle épidémiologique est composé de deux parties :

  • De compartiments
  • De règles de passage d'un compartiment à un autre

Les compartiments divisent la population (N) dans les différents états possibles par rapport à la maladie (Cf schéma ci-dessous). Par exemple :

  • S = Personnes saines
  • E = Personnes exposées à l'épidémie
  • I = Personnes infectées
  • R = Personnes guéries Il existe également d'autres compartiments tel que D pour les personnes décédées.

Les règles permettent de définir les proportions des individus d'une population passant d'un compartiment à un autre. Par exemple :

  • Le taux d'infection (Beta) permettant de passer de S à E
  • Le taux d'incubation (Sigma) permettant de passer de E à I
  • Le taux de guérison (Delta) permettant de passer de I à R

D'autres notions sont également à prendre en compte :

  • Le taux de reproduction de base noté R0. Si R0 > 1 alors l'épidémie peut se propager dans la population
  • On peut ajouter au modèle des variables nommées u (mu) et v (nu) pour prendre en compte respectivement les taux de mortalité d'une population non liée à la maladie et le taux de vaccination.

Dans les modèles que nous allons établir, nous émettons l'hypothèse que la population ne change pas au fil du temps : Naissance, décés, déplacements... ce qui n'est pas le cas lors d'un phénomène réel

Quelques formules mathématiques

Voici à présent les formules mathématiques permettant de réaliser des prédictions sur l'évolution d'une épidémie dans le cadre d'un modèle épidémiologique SEIR :

Nous sommes en présence d'équations différentielles. Pour rappel, une équation différentielle est une équation où l’inconnue est une fonction, et qui se présente sous la forme d’une relation entre cette fonction et ses dérivées. Prenons l'exemple du compartiment S. Nous mettons en relation à l'aide du signe = la dérivée de la fonction S (dS/dt) avec son équation, il s'agit donc bien d'une équation différentielle.

Pour les résoudre à l'aide du langage Python, nous allons utiliser la fonction odeint du module scipy.integrate. Mais avant cela, nous devons écrire une fonction reprenant les différentes formules énoncées précédemment.

In [172]:
#Fonction de résolution
def SEIR (vecteurDeDonnees, t, N, tauxInfection, tauxIncubation, tauxGuerrison, tauxDeMortalite, tauxDeVaccination):
    S, E,I,R = vecteurDeDonnees #On récupere les valeurs S0, E0, I0 et R0 passées en paramètre
    dSdt = tauxDeMortalite * (N-S) - tauxInfection * ((S*I)/N) - tauxDeVaccination*S
    dEdt = tauxInfection * ((S*I)/N) - (tauxDeMortalite+tauxIncubation)*E
    dIdt = (tauxIncubation * E) - (tauxDeMortalite+tauxGuerrison) * I
    dRdt = (tauxGuerrison * I) + tauxDeMortalite*R + tauxDeVaccination*S
  
    return dSdt, dEdt, dIdt, dRdt

Modélisation de la grippe H1N1 à l'aide du modèle SEIR (province du Guangdong - 2009)

Nous allons à présent paramétrer les éléments de notre modèle en fonction des données épidémiologiques. Il s'agit d'une tâche assez compliquée car la pluspart de ces données sont difficiles à obtenir, notamment le taux d'infection (beta).
Pour y parvenir, nous nous sommes appuyés sur ce document https://www.sciencedirect.com/science/article/pii/S1201971212013112 nous permettant de définir les paramètres suivants concernant l'épidémie H1N1 :

In [173]:
N = 95440000 #Population de la province du Guangdong en 2009 
tauxInfection = 0.420 # Taux d'infection
tauxIncubation = 1/2.62 # Taux d'incubation
tauxGuerrison = 1/3.80 # Taux de guérison
tauxDeMortalite = 0
tauxDeVaccination = 0
nombreDeJoursDeSimulation = 365 #On décide de simuler l'évolution sur une année

#Etat de l'épidémie au premier jour
R0 = 0 # Aucune personne rétablie
I0 = 1 # 1 cas identifié
E0 = 0 # Aucune personne exposée
S0 = N - E0 - I0 - R0 #La population saine

Exécution de la simulation

Selon ce document : https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3154258/
"On May 18, 2009 the first case of pH1N1 was detected by Guangdong CDC in Guangdong province. The epidemic in Guangdong reached its peak in November, and attenuated to baseline levels by late December, 2009."

  • Le début de l'épidémie a été constatée au mois de mai
  • Le pic épidémique a été constaté au mois de novembre soit environ 210 jours plus tard
  • L'épidémie diminue ensuite jusque fin d'année soit 240 jours plus tard
In [174]:
#Import des librairies nécessaires
from scipy.integrate import odeint
import numpy as np


#On crée un tableau linspace
# numpy.linspace(start, stop, num) 
# Ce tableau
#- comporte autant de lignes que le nombre de jours de simulation : num = 365 dans notre cas
#- les valeurs du tableau vont de 0 à 365 (Les deux premiers paramètres) car elles serviront dans les équations différentielles
# pour le paramètre t des dérivées dS/dt...
# Lors de la première itération t vaudra 0, t vaudra 1 pour la seconde itération... 
vecteurTemporel = np.linspace(0,nombreDeJoursDeSimulation,nombreDeJoursDeSimulation)

#Création d'un tableau contenant les valeurs S0,E0,I0 et R0 c'est à dire le nombre de personnes par compartiment
datas = np.array([S0,E0,I0,R0])

#Solutions des équations différentielles à l'aide de la méthode Odeint.
#Les réponses seront stockées dans le tableau "Vecteur Temporel". Ce tableau sert aussi de paramètre
#Les paramètres utiles à la résolution des équations sont passés à l'aide de l'instruction args
solutions = odeint(SEIR,
                  datas,
                  vecteurTemporel, 
                  args=(N, tauxInfection, tauxIncubation, tauxGuerrison,tauxDeMortalite, tauxDeVaccination) )
In [175]:
#On transpose la matrice et on stocke chaque résultat dans des tableaux
S, E, I, R = solutions.T
S
Out[175]:
array([95439999.        , 95439998.62073237, 95439998.2858734 ,
       95439997.95935893, 95439997.62266959, 95439997.26565135,
       95439996.88207965, 95439996.46751107, 95439996.01823714,
       95439995.53076921, 95439995.0015784 , 95439994.42695752,
       95439993.80294073, 95439993.12525013, 95439992.38925384,
       95439991.5899281 , 95439990.72182007, 95439989.77900928,
       95439988.75506662, 95439987.64301014, 95439986.43525699,
       95439985.12357205, 95439983.69901155, 95439982.15186206,
       95439980.47157438, 95439978.64669143, 95439976.66477025,
       95439974.5122971 , 95439972.17459534, 95439969.63572547,
       95439966.87837633, 95439963.88374662, 95439960.6314175 ,
       95439957.09921312, 95439953.26304914, 95439949.0967687 ,
       95439944.57196413, 95439939.65778312, 95439934.32071842,
       95439928.52437937, 95439922.22924398, 95439915.39238887,
       95439907.96719727, 95439899.90304083, 95439891.14493449,
       95439881.63316157, 95439871.30286631, 95439860.08361234,
       95439847.89890216, 95439834.66565537, 95439820.29364225,
       95439804.68486865, 95439787.73290788, 95439769.3221761 ,
       95439749.32714236, 95439727.6114717 , 95439704.02709961,
       95439678.4132208 , 95439650.59519169, 95439620.38334112,
       95439587.57167692, 95439551.93648052, 95439513.23478334,
       95439471.20270957, 95439425.5536775 , 95439375.97644544,
       95439322.13299039, 95439263.65620175, 95439200.14738472,
       95439131.17353664, 95439056.26439734, 95438974.9092439 ,
       95438886.55340992, 95438790.59450732, 95438686.37831746,
       95438573.19432211, 95438450.27088143, 95438316.76996645,
       95438171.78144644, 95438014.31688987, 95437843.30282652,
       95437657.57343395, 95437455.86259982, 95437236.79529227,
       95436998.87819685, 95436740.48954213, 95436459.86805089,
       95436155.10095319, 95435824.11095218, 95435464.64209282,
       95435074.24440739, 95434650.25722538, 95434189.7910726 ,
       95433689.70800962, 95433146.60025027, 95432556.7671622 ,
       95431916.18975987, 95431220.50364402, 95430464.96927877,
       95429644.43978606, 95428753.32594919, 95427785.55827607,
       95426734.54577553, 95425593.13123828, 95424353.54273592,
       95423007.34086424, 95421545.36150615, 95419957.65383379,
       95418233.4129768 , 95416360.90679482, 95414327.3966106 ,
       95412119.0511837 , 95409720.85329479, 95407116.49852891,
       95404288.28516322, 95401216.99531849, 95397881.76552063,
       95394259.94664964, 95390326.95199446, 95386056.09259778,
       95381418.3986863 , 95376382.4263614 , 95370914.04794352,
       95364976.22496714, 95358528.76264316, 95351528.04334344,
       95343926.73878665, 95335673.49827056, 95326712.61137028,
       95316983.64426447, 95306421.04440895, 95294953.71470453,
       95282504.5550288 , 95268989.96540073, 95254319.31022802,
       95238394.34147891, 95221108.5767199 , 95202346.62913184,
       95181983.48803887, 95159883.74428149, 95135900.75886649,
       95109875.77131183, 95081636.94409217, 95050998.34044561,
       95017758.83190137, 94981700.93199699, 94942589.5582662 ,
       94900170.70995197, 94854170.0696583 , 94804291.5229125 ,
       94750215.59636068, 94691597.81156546, 94628066.97612825,
       94559223.35973656, 94484636.84112684, 94403844.97176056,
       94316350.98569886, 94221621.77144034, 94119085.81909351,
       94008131.1575391 , 93888103.30483745, 93758303.30460574,
       93617985.81396739, 93466357.33588444, 93302574.63456497,
       93125743.39745909, 92934917.2040282 , 92729096.88718246,
       92507230.37705171, 92268213.12171577, 92010889.21055177,
       91734053.27762593, 91436453.39641088, 91116795.02602373,
       90773746.20027275, 90405944.09575003, 90012003.11206825,
       89590524.67640878, 89140108.8517235 , 88659367.75662993,
       88146941.10505918, 87601513.7706362 , 87021835.37551332,
       86406741.82856415, 85755178.76711328, 85066226.50583132,
       84339126.32206929, 83573307.57011797, 82768415.06982252,
       81924336.15379775, 81041226.58775601, 80119534.54261182,
       79160021.69532092, 78163780.48466171, 77132246.72985268,
       76067206.24523677, 74970795.51257624, 73845494.91921037,
       72694114.49307999, 71519772.0861363 , 70325863.82560895,
       69116027.41462114, 67894099.00885819, 66664064.5965431 ,
       65430007.22321658, 64196051.5563051 , 62966307.37793411,
       61744813.69171166, 60535485.03024112, 59342061.57659716,
       58168064.37582408, 57016756.70407104, 55891112.37680938,
       54793791.69748803, 53727124.83846336, 52693102.68870375,
       51693374.99419651, 50729254.88153541, 49801729.16216091,
       48911473.51942638, 48058871.58135045, 47244037.06733957,
       46466837.97798607, 45726922.04533867, 45023742.72164976,
       44356584.96971878, 43724590.41447026, 43126781.3468429 ,
       42562083.29550025, 42029345.91943977, 41527362.08009826,
       41054885.05188856, 40610643.7824977 , 40193356.3124759 ,
       39801741.43643986, 39434528.64907259, 39090466.51117961,
       38768329.60833739, 38466924.18708759, 38185092.65489572,
       37921717.01951752, 37675721.43191882, 37446073.94338536,
       37231787.59443865, 37031920.92221466, 36845577.98517805,
       36671907.98879556, 36510104.57292193, 36359404.84077102,
       36219088.1725527 , 36088474.87547855, 35966924.71707378,
       35853835.36145274, 35748640.76323087, 35650809.52494775,
       35559843.23755847, 35475274.83390512, 35396666.9645501 ,
       35323610.40006286, 35255722.47835725, 35192645.59868188,
       35134045.76746117, 35079611.20234299, 35029050.99311706,
       34982093.82534668, 34938486.75996196, 34897994.07923222,
       34860396.18968953, 34825488.58444729, 34793080.86269167,
       34762995.80438799, 34735068.49998173, 34709145.53176495,
       34685084.20192105, 34662751.81073873, 34642024.97868557,
       34622789.01120151, 34604937.30438196, 34588370.78901044,
       34572997.40887722, 34558731.63679821, 34545494.0198774 ,
       34533210.75622678, 34521813.29979978, 34511237.99235427,
       34501425.71847273, 34492321.58742896, 34483874.63417476,
       34476037.54199747, 34468766.38403709, 34462020.38280925,
       34455761.68665134, 34449955.15869747, 34444568.19186351,
       34439570.51838531, 34434934.04745342, 34430632.7106999 ,
       34426642.31483874, 34422940.40924919, 34419506.16062482,
       34416320.23653207, 34413364.69826745, 34410622.90072437,
       34408079.39913149, 34405719.86303116, 34403530.996078  ,
       34401500.46161039, 34399616.81358129, 34397869.43242476,
       34396248.46548527, 34394744.7717896 , 34393349.87071737,
       34392055.89444353, 34390855.54346729, 34389742.04612882,
       34388709.12006897, 34387750.93704315, 34386862.09016066,
       34386037.5634511 , 34385272.70362591, 34384563.19388064,
       34383905.02958743, 34383294.49573885, 34382728.14601839,
       34382202.78337517, 34381715.44201795, 34381263.37069404,
       34380844.01717279, 34380455.01386503, 34380094.1644697 ,
       34379759.43157577, 34379448.92519221, 34379160.89207053,
       34378893.70582283, 34378645.85774391, 34378415.94829745,
       34378202.67921939, 34378004.84619948, 34377821.33205902,
       34377651.10047473, 34377493.19011851, 34377346.70923228,
       34377210.83059218, 34377084.78684061, 34376967.86614342,
       34376859.40821508, 34376758.80053486, 34376665.47491297,
       34376578.90428247, 34376498.59972361, 34376424.10770223,
       34376355.00751052, 34376290.90888835, 34376231.44982118,
       34376176.29449793, 34376125.1314126 , 34376077.67160343,
       34376033.64702471, 34375992.80903098, 34375954.92697393,
       34375919.78689933, 34375887.19033994, 34375856.95319114,
       34375828.90467531, 34375802.88637562])
In [176]:
import matplotlib.pyplot as plt

plt.figure(figsize=(10,10))
titre = "Epidémie: H1N1"
plt.title(titre.upper())
plt.autoscale(enable=True, axis='both', tight=None)
plt.plot(vecteurTemporel,S,'yellow',label="Personnes saines")
plt.plot(vecteurTemporel,E,'blue',label="Personnes exposées")
plt.plot(vecteurTemporel,I,'red',label="Personnes infectées")
plt.plot(vecteurTemporel,R,'green',label="Personnes guéries")
plt.grid(b=True, which='major', c='#bbbbbb', lw=1, ls='-')

plt.xlabel('Temps (jours)')
plt.ylabel('Nombre de personnes')
legend = plt.legend()
legend.get_frame().set_alpha(0.5)

plt.show()

Sur ce graphique, on constate qu'avec les paramètres utilisés, nous obtenons effectivement un pic vers le 210 jours.

A noter que les paramètres utilisés ont été calculés a posteriori par les équipes de recherche.
Si nous modifions de quelques centièmes les paramètres, nous obtenons d'autres résultats qui parfois peuvent s'avérer incohérents vis à vis de la réalité. Seuls des spécialistes peuvent valider ou non le modèle.

In [177]:
np.amax(I) #Nombre de cas grippaux constatés (On choisit la valeur maximale de I)
Out[177]:
4531577.617658164
In [166]:
np.unravel_index(np.argmax(I), I.shape) #Jour du pic épidémique
Out[166]:
(210,)
In [161]:
Taux_Reproduction_R0 = tauxInfection / tauxGuerrison
Taux_Reproduction_R0
Out[161]:
1.596

Comme R0 > 1 l'épidémie peut donc se propager dans la population.

Coronavirus MERS en Corée du sud - 2015

Penchons-nous à présent sur le cas du Coronavirus qui a sévi en 2015 en Corée du Sud.

A l'aide de ces documents :
https://www.sciencedirect.com/science/article/pii/S0022519316302430
http://www.koreascience.or.kr/article/JAKO201823954940175.page

Nous pouvons établir les paramètres suivants :

In [178]:
N = 51069000 #Population de corée du sud en 2015 
tauxInfection = 1.3 # Taux d'infection
tauxIncubation = 0.2 # Taux d'incubation
tauxGuerrison = 0.2 # Taux de guérison
tauxDeMortalite = 0
tauxDeVaccination = 0
nombreDeJoursDeSimulation = 365 

#Etat de l'épidémie au premier jour
R0 = 0 # Aucune personne rétablie
I0 = 3 # 3 cas identifiés
E0 = 2 # 2 personnes exposées
S0 = N - E0 - I0 - R0 #La population saine
In [179]:
vecteurTemporel = np.linspace(0,nombreDeJoursDeSimulation,nombreDeJoursDeSimulation)

datas = np.array([S0,E0,I0,R0])

solutions = odeint(SEIR,
                  datas,
                  vecteurTemporel, 
                  args=(N, tauxInfection, tauxIncubation, tauxGuerrison,tauxDeMortalite, tauxDeVaccination) )

S, E, I, R = solutions.T

plt.figure(figsize=(10,10))
titre = "Epidémie: Coronavirus Corée 2015"
plt.title(titre.upper())
plt.autoscale(enable=True, axis='both', tight=None)
plt.plot(vecteurTemporel,S,'yellow',label="Personnes saines")
plt.plot(vecteurTemporel,E,'blue',label="Personnes exposées")
plt.plot(vecteurTemporel,I,'red',label="Personnes infectées")
plt.plot(vecteurTemporel,R,'green',label="Personnes guéries")
plt.grid(b=True, which='major', c='#bbbbbb', lw=1, ls='-')

plt.xlabel('Temps (jours)')
plt.ylabel('Nombre de personnes')
legend = plt.legend()
legend.get_frame().set_alpha(0.5)

plt.show()
In [180]:
np.amax(I) #Nombre de cas coronavirus constatés
Out[180]:
13280090.023775049
In [139]:
Taux_Reproduction_R0 = tauxInfection / tauxGuerrison
Taux_Reproduction_R0
Out[139]:
6.5

Le taux R0 (Taux de reproduction de base) est supérieur au H1N1 ce qui le rend trés contagieux, mais cohérent avec les informations médicales que nous avons récoltées sur le sujet avec un R0 < 7. (http://www.kjim.org/journal/view.php?number=170028).

Ce taux de base de reproduction est comparable aux oreillons, rubéole, variole (https://fr.wikipedia.org/wiki/Mod%C3%A8les_compartimentaux_en_%C3%A9pid%C3%A9miologie)

Pour conclure

Bien entendu, nous ne disposons pas de moyens ni des compétences médicales et épidémiologiques nécessaires pour vérifier ces différentes données.

Cependant à l'aide de ce tutoriel nous avons pu découvrir comment à l'aide des modèles par compartiments, il est possible de prédire l'évolution d'une épidémie. N'oublions pas que ces modélisations sont réalisées dans des cas d'hypothèses ne reflétant pas la réalité (mobilité, naissance et décés de la population).

Si vous souhaitez aller plus loin dans la compréhension de ces modèles et les tester sur différents cas, nous vous invitons à consulter ce site : https://institutefordiseasemodeling.github.io/Documentation/general/index.html