1A - Enoncé 24 novembre 2020

Correction de l’examen du 24 novembre 2020.

Exercice 1 : guérison

On commence par générer des données artificielles à partir de véritables données, disponible depuis le site Données hospitalières relatives à l’épidémie de COVID-19.

  • rad : personnes retournées à domicile (cumul)

  • dc : personnes décédées (cumul)

[1]:
import pandas

df = pandas.read_csv(
    "https://www.data.gouv.fr/fr/datasets/r/63352e38-d353-4b54-bfd1-f1b3ee1cabd7",
    sep=";",
)
gr = df[["jour", "rad", "dc"]].groupby(["jour"]).sum()
gr.head()
[1]:
rad dc
jour
2020-03-18 1627 435
2020-03-19 2322 642
2020-03-20 3128 890
2020-03-21 3580 1041
2020-03-22 4188 1251
[2]:
diff = gr.diff().reset_index(drop=False)
diff.head()
[2]:
jour rad dc
0 2020-03-18 NaN NaN
1 2020-03-19 695.0 207.0
2 2020-03-20 806.0 248.0
3 2020-03-21 452.0 151.0
4 2020-03-22 608.0 210.0
[3]:
diff.tail()
[3]:
jour rad dc
1104 2023-03-27 810.0 84.0
1105 2023-03-28 966.0 77.0
1106 2023-03-29 1003.0 67.0
1107 2023-03-30 736.0 71.0
1108 2023-03-31 1008.0 61.0

Les données sont parfois erronnées et corrigées. Bien que les séries soient cumulées, la différence censée être positive peut ne pas l’être. Même s’il faudrait se pencher un peu sur le problème pour corriger les données, dans notre cas, on ne tient pas compte des valeurs négatives.

[4]:
import numpy

diff["rad"] = numpy.maximum(diff["rad"], 0)
diff["dc"] = numpy.maximum(diff["dc"], 0)

On convertit la date en jour de l’année puis on simule un loi exponentielle de paramètre 14 pour avoir la date de sortie.

[5]:
import numpy.random as rnd


def donnees_artificielles(hosp, mu=14, nu=21):
    dt = pandas.to_datetime(hosp["jour"])
    res = []
    for i in range(hosp.shape[0]):
        date = dt[i].dayofyear
        h = hosp.iloc[i, 1]
        try:
            delay = rnd.exponential(mu, int(h))
        except Exception as e:
            print(hosp.shape, i)
            print(hosp.iloc[i])
            print(e)
            raise e
        for j in range(delay.shape[0]):
            res.append([date - int(delay[j]), date, 1])
        h = hosp.iloc[i, 2]
        delay = rnd.exponential(nu, int(h))
        for j in range(delay.shape[0]):
            res.append([date - int(delay[j]), date, 0])
    return pandas.DataFrame(res, columns=["entree", "sortie", "issue"])


data = donnees_artificielles(diff[1:].reset_index(drop=True))
data.head()
[5]:
entree sortie issue
0 6 79 1
1 64 79 1
2 31 79 1
3 65 79 1
4 62 79 1
[6]:
data.to_csv("examen2021.csv", index=False)

Q1

On récupère les données.

[7]:
import pandas

df = pandas.read_csv(
    "https://github.com/sdpython/teachpyx/raw/main/_data/examen2021.zip"
)
df.head()
[7]:
entree sortie issue
0 49 79 1
1 27 79 1
2 73 79 1
3 74 79 1
4 48 79 1

Q2 : durée de guérison

[8]:
duree = df["sortie"] - df["entree"]
duree = duree.values  # conversion en numpy
issue = df["issue"].values
duree[:5], issue[:5]
[8]:
(array([30, 52,  6,  5, 31], dtype=int64), array([1, 1, 1, 1, 1], dtype=int64))

Q3 : estimateur Kaplan-Meier (1)

[9]:
t = 10
nt = duree[(duree >= t)].shape[0]
dt = duree[(duree == t) & (issue == 0)].shape[0]
st = 1.0 - dt / nt
st
[9]:
0.98965342710248

Q4 : courbe de Kaplan-Meier

[10]:
T = [0]
St = [1.0]
for t in range(0, 150):
    nt = duree[(duree >= t)].shape[0]
    dt = duree[(duree == t) & (issue == 0)].shape[0]
    st = 1.0 - dt / nt
    T.append(t)
    St.append(st * St[-1])

Q5 : graphe

[11]:
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1, figsize=(4, 4))
ax.plot(T, St);
../../_images/practice_exams_td_note_2021_19_0.png
[13]:
import numpy
from lifelines import KaplanMeierFitter

fig, ax = plt.subplots(1, 1, figsize=(10, 4))
ax.plot(T, St, label="custom", lw=10)

kmf = KaplanMeierFitter()
kmf.fit(duree, (issue == 0).astype(numpy.int32))
kmf.plot(ax=ax)
ax.legend();
../../_images/practice_exams_td_note_2021_20_0.png

Q6 : application aux données publiques

Les données accessibles librement sur le portail data.gouv.fr recensent les entrées et les sorties des personnes sans relier une entrée et une sortie spécifique. Si N est personnes sont sorties guéries, on ne sait pas quand elles sont entrées. Donc le calcul ci-dessus n’est pas possible.

Exercice 2

Q1 : t + 1

[14]:
import numpy

N = 10
M = numpy.zeros((N, N))
M[4, 5] = 1
M
[14]:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
[15]:
def propagation(M):
    M2 = M.copy()
    M2[1:, :] = numpy.maximum(M2[1:, :], M[:-1, :])
    M2[:-1, :] = numpy.maximum(M2[:-1, :], M[1:, :])
    M2[:, 1:] = numpy.maximum(M2[:, 1:], M[:, :-1])
    M2[:, :-1] = numpy.maximum(M2[:, :-1], M[:, 1:])
    return M2


propagation(M.copy())
[15]:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

Q2 : après T itération

[16]:
def propagation_n(M, t):
    for i in range(t):
        M = propagation(M)
    return M


propagation_n(M, 3)
[16]:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 1., 1., 1., 1., 1., 0., 0.],
       [0., 0., 1., 1., 1., 1., 1., 1., 1., 0.],
       [0., 0., 0., 1., 1., 1., 1., 1., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

Q3 : vaccin

[17]:
p = 0.3
vaccine = (numpy.random.rand(N, N) <= p).astype(numpy.int32)
vaccine
[17]:
array([[0, 1, 0, 0, 0, 1, 1, 0, 0, 0],
       [1, 1, 0, 1, 0, 0, 1, 1, 1, 0],
       [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
       [0, 0, 0, 1, 0, 0, 1, 1, 1, 0],
       [1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
       [1, 0, 0, 1, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 0, 0, 0, 1],
       [0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
       [0, 0, 1, 0, 1, 0, 1, 0, 1, 0]])
[18]:
def propagation_vaccine(M, vaccine):
    M2 = M.copy()
    M2[1:, :] = numpy.maximum(M2[1:, :], M[:-1, :])
    M2[:-1, :] = numpy.maximum(M2[:-1, :], M[1:, :])
    M2[:, 1:] = numpy.maximum(M2[:, 1:], M[:, :-1])
    M2[:, :-1] = numpy.maximum(M2[:, :-1], M[:, 1:])
    M2 = numpy.minimum(M2, 1 - vaccine)
    return M2


vaccine[4, 5] = 0
propagation_vaccine(M, vaccine)
[18]:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

Q4 : après T heures

[19]:
def propagation_n_vaccine(M, t, vaccine):
    for i in range(t):
        M = propagation_vaccine(M, vaccine)
    return M


propagation_n_vaccine(M, 3, vaccine)
[19]:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 1., 1., 1., 0., 0.],
       [0., 0., 0., 0., 1., 1., 0., 0., 0., 0.],
       [0., 0., 0., 1., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

Q5 : variation

[20]:
import pandas

res = []
for p in [
    0.7,
    0.8,
    0.85,
    0.86,
    0.87,
    0.88,
    0.89,
    0.9,
    0.94,
    0.95,
    0.96,
    0.97,
    0.98,
    0.99,
    1.0,
]:
    cont = []
    for test in range(0, 10):
        vaccine = (numpy.random.rand(N, N) <= p).astype(numpy.int32)
        M[4, 5] = 1
        vaccine[4, 5] = 0
        M = propagation_n_vaccine(M, 3, vaccine)
        contamine = M.ravel().sum()
        cont.append(contamine)
    cont = numpy.array(cont)
    res.append(dict(p=p, prop=cont.mean()))

df = pandas.DataFrame(res)
df
[20]:
p prop
0 0.70 13.5
1 0.80 11.6
2 0.85 2.6
3 0.86 1.8
4 0.87 2.3
5 0.88 2.3
6 0.89 1.2
7 0.90 2.5
8 0.94 1.6
9 0.95 1.4
10 0.96 1.4
11 0.97 1.1
12 0.98 1.1
13 0.99 1.0
14 1.00 1.0
[21]:
ax = df.plot(x="p", y="prop")
ax.plot([0.7, 1.0], [2, 2], "--");
../../_images/practice_exams_td_note_2021_35_0.png

Dans cette configuration, en supposant qu’un seul élève est contaminé, il faudrait vacciner à plus de 85% pour avoir une chance de ne pas avoir une nouvelle contamination.

Ce résultat ne change pas si la taille de la matrice change. Il change en revanche en fonction du nombre de tirages, ici 10. Pour n’avoir aucune propagation, il faut entourer la personne contaminé de 4 personnes vaccinées. \(p\) est la probabilité pour une personné d’être vaccinée (ou immunisée si la vaccination est faite sur 100% des personnes). \(p^4\) est la probabilité d’avoir 4 personnes vaccinées. \(q=1-p^4\) est la probabilité qu’une personne ne soient pas immunisées parmi les 4. \(1 - (1-q)^{10}\) est la probabilité que 10 cours de 3h se passent bien sans contamination.

[22]:
p = 0.9
p4 = 1 - (1 - p) ** 4
p4
[22]:
0.9999
[23]:
df2 = df.copy()
df2["P4**100"] = 1 - df2["p"] ** 40
[24]:
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 2, figsize=(10, 4))
df2.plot(x="p", y="prop", ax=ax[0])
ax[0].plot([0.7, 1.0], [2, 2], "--")
df2.plot(x="p", y="P4**100", ax=ax[1]);
../../_images/practice_exams_td_note_2021_39_0.png
[ ]:

[ ]:


Notebook on github