<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Evoluation d'une population (correction)
Evolution d'une population à partir des tables de mortalités et d'une situation initiale.
Step1:  Exercice 1 
Step2:  Je reprends ici le code exposé à Damien Vergnaud's Homepage en l'adaptant un peu avec les fonctions de matplotlib via l'interface pyplot. Puis j'ajoute la différence par âge. On commence souvent par la gallerie pour voir si un graphe ou juste une partie est similaire à ce qu'on veut obtenir.
Step3:  Le même en utilisant la fonction insérée dans le module actuariat_python.
Step4:  Exercice 2 
Step5:  On note $P_t$ la population l'âge $t$. La probabilité de mourir à la date $t+d$ lorsqu'on a l'âge $t$ correspond à la probabilité de rester en vie à jusqu'à l'âge $t+d$ puis de mourir dans l'année qui suit 
Step6:  Enfin, on dessine le résultat avec matplotlib 
Step7:  Le calcul implémenté ci-dessus n'est pas le plus efficace. On fait deux boucles imbriquées dont le coût global est en $O(n^2)$ mais surtout on effectue les mêmes calculs plusieurs fois. Pour le réduire à un coût linéaire $O(n)$, il faut s'intéresser à la quantité 
Step8:  numpy et pandas ont plusieurs fonction en commun dès qu'il s'agit de parcourir les données. Il existe aussi la fonction DataFrame.cumsum.
Exercice 3 
Step9:  La population a été obtenue lors de l'exercice 1, on la convertit en un objet numpy 
Step10:  Ensuite on calcule la population en 2020 
Step11:  Exercice 4 
Step12:  Exercice 5 
Step13:  Il y a un peu plus de garçons qui naissent chaque année.
Step14:  On convertit ces données en une matrice numpy sur 120 lignes comme les précédentes. On se sert des méthodes fillna et merge.
Step15:  Si la matrice pop a plus de ligne que la matrice mat_fec, on doit compléter la seconde avec autant de lignes nulles que la précédente.
Step16:  Il faut maintenant coder une fonction qui calcule le naissances pour l'année suivantes.
Step17:  Et on reprend la fonction iteration et le code de l'exercice précédent 
Step18:  On va plus loin et on stocke la population dans un vecteur 
Step19:  Le code suivant permet de combiner les deux graphes sur la même ligne avec la fonction subplots 
Step20:  Retraites
La réforme de la retraite en 2019 laissera un souvenir indélébile. Une des mesures far est celle de l'âge pivot. Le modèle développé jusqu'à présent ne permet pas de simuler ce que chaque français pourrait obtenir avec la nouvelle réforme mais il permet de donner un ordre de grandeur sur le nombre de retraités selon que l'âge pivot est de 62 ans ou plus vieux. Si on en croit cette page 1,7 actif cotisant par retraité, il y a 1,7 cotisant par retraité.
Trois populations sont considérées 
<ASSISTANT_TASK:>
Python Code:
%matplotlib inline
import matplotlib.pyplot as plt
from jyquickhelper import add_notebook_menu
add_notebook_menu()
Explanation: Evoluation d'une population (correction)
Evolution d'une population à partir des tables de mortalités et d'une situation initiale.
End of explanation
from actuariat_python.data import population_france_year
population = population_france_year()
df = population
df.head(n=3)
hommes = df["hommes"]
femmes = df["femmes"]
somme = hommes - femmes
Explanation: Exercice 1 : pyramides des âges
End of explanation
from matplotlib import pyplot as plt
from numpy import arange
fig, ax = plt.subplots(figsize=(8,8))
ValH = ax.barh(arange(len(hommes)), hommes, 1.0, label="Hommes",
               color='b', linewidth=0, align='center')
ValF = ax.barh(arange(len(femmes)), -femmes, 1.0, label="Femmes",
               color='r', linewidth=0, align='center')
diff, = ax.plot(somme, arange(len(femmes)), 'y', linewidth=2)
ax.set_title("Pyramide des âges")
ax.set_ylabel("Ages")
ax.set_xlabel("Habitants")
ax.set_ylim([0, 110])
ax.legend((ValH[0], ValF[0], diff), ('Hommes', 'Femmes', 'différence'));
Explanation: Je reprends ici le code exposé à Damien Vergnaud's Homepage en l'adaptant un peu avec les fonctions de matplotlib via l'interface pyplot. Puis j'ajoute la différence par âge. On commence souvent par la gallerie pour voir si un graphe ou juste une partie est similaire à ce qu'on veut obtenir.
End of explanation
from actuariat_python.plots import plot_population_pyramid
plot_population_pyramid(df["hommes"], df["femmes"], figsize=(8, 4));
Explanation: Le même en utilisant la fonction insérée dans le module actuariat_python.
End of explanation
from actuariat_python.data import table_mortalite_france_00_02
df=table_mortalite_france_00_02()
import pandas
pandas.concat([df.head(n=3), df.tail(n=3)])
Explanation: Exercice 2 : calcul de l'espérance de vie
Le premier objectif est de calculer l'espérance de vie à l'âge $t$ à partir de la table de mortalité. On récupère cette table.
End of explanation
import numpy
hf = df[["Homme", "Femme"]].values
hf = numpy.vstack([hf, numpy.zeros((8, 2))])
hf.shape
nb = hf.shape[0]
esp = numpy.zeros ((nb,2))
for t in range(0,nb):
    for i in (0,1):
        if hf[t,i] == 0:
            esp[t,i] = 0
        else:
            somme  = 0.0
            for d in range(1,nb-t):
                if hf[t+d,i] > 0:
                    somme += d * (hf[t+d,i] - hf[t+d+1,i]) / hf[t,i]
            esp[t,i] = somme
esp[:1]            
Explanation: On note $P_t$ la population l'âge $t$. La probabilité de mourir à la date $t+d$ lorsqu'on a l'âge $t$ correspond à la probabilité de rester en vie à jusqu'à l'âge $t+d$ puis de mourir dans l'année qui suit :
$$m_{t+d} = \frac{P_{t+d}}{P_t}\frac{P_{t+d} - P_{t+d+1}}{P_{t+d}} $$.
L'espérance de vie s'exprime :
$$\mathbb{E}(t) = \sum_{d=1}^\infty d m_{t+d} = \sum_{d=1}^\infty d \frac{P_{t+d}}{P_t}\frac{P_{t+d} - P_{t+d+1}}{P_{t+d}} = \sum_{d=1}^\infty d \frac{P_{t+d} - P_{t+d+1}}{P_{t}} $$
On crée une matrice allant de 0 à 120 ans et on pose $\mathbb{E}(120)=0$. On utilise le module numpy.
End of explanation
h = plt.plot(esp)
plt.legend(h, ["Homme", "Femme"])
plt.title("Espérance de vie");
Explanation: Enfin, on dessine le résultat avec matplotlib :
End of explanation
# à suivre
Explanation: Le calcul implémenté ci-dessus n'est pas le plus efficace. On fait deux boucles imbriquées dont le coût global est en $O(n^2)$ mais surtout on effectue les mêmes calculs plusieurs fois. Pour le réduire à un coût linéaire $O(n)$, il faut s'intéresser à la quantité :
$$P_{t+1} \mathbb{E}(t+1) - P_t \mathbb{E}(t) = \sum_{d=1}^\infty d (P_{t+d+1} - P_{t+d+2}) - \sum_{d=1}^\infty d (P_{t+d} - P_{t+d+1})$$
L'implémentation devra utiliser la fonction numpy.cumsum et cette astuce Pandas Dataframe cumsum by row in reverse column order?.
End of explanation
mortalite = (hf[:-1] - hf[1:]) / hf[:-1]
mortalite = numpy.nan_to_num(mortalite)  # les divisions nulles deviennent nan, on les remplace par 0
mortalite = numpy.vstack([mortalite, numpy.zeros((1, 2))])
m = mortalite
Explanation: numpy et pandas ont plusieurs fonction en commun dès qu'il s'agit de parcourir les données. Il existe aussi la fonction DataFrame.cumsum.
Exercice 3 : simulation de la pyramide l'année suivante
L'objectif est d'estimer la population française à l'année $T$. Si $P(a,T)$ désigne le nombre de personnes d'âge $a$ en $T$, on peut estimer $P(a,T+1)$ en utilisant la probabilité de mourir $m(a)$ :
$$ P(a+1, T+1) = P(a,T) * (1 - m(a))$$
On commence par calculer les coefficients $m(a)$ avec la table hf obtenue lors de l'exercice précédent tout en gardant la même dimension (on aura besoin de la fonction nan_to_num :
End of explanation
pop = population[["hommes","femmes"]].values
pop = numpy.vstack( [pop, numpy.zeros((m.shape[0] - pop.shape[0], 2))])
pop0 = pop.copy()
pop.shape
Explanation: La population a été obtenue lors de l'exercice 1, on la convertit en un objet numpy :
End of explanation
pop_next = pop * (1-m)
pop_next = numpy.vstack([numpy.zeros((1, 2)), pop_next[:-1]])
pop_next[:5]
pop[:5]
from actuariat_python.plots import plot_population_pyramid
plot_population_pyramid(pop_next[:, 0], pop_next[:, 1]);
Explanation: Ensuite on calcule la population en 2020 :
End of explanation
def iteration(pop, mortalite):
    pop_next = pop * (1-mortalite)
    pop_next = numpy.vstack([numpy.zeros((1, 2)), 
                             pop_next[:-1]]) # aucune naissance
    return pop_next
popt = pop
for year in range(2020, 2051):
    popt = iteration(popt, mortalite)
                             
plot_population_pyramid(popt[:,0], popt[:,1], title="Pyramide des âges en 2050");
Explanation: Exercice 4 : simulation jusqu'en 2100
Il s'agit de répéter l'itération effectuée lors de l'exercice précédent. Le plus est de recopier le code dans une fonction et de l'appeler un grand nombre de fois.
End of explanation
ratio = pop[0, 0] / (pop[0, 1] + pop[0, 0])
ratio
Explanation: Exercice 5 : simulation avec les naissances
Dans l'exercice précédent, la seconde ligne de la fonction iteration correspond à cas où il n'y a pas de naissance. On veut remplacer cette ligne par quelque chose proche de la réalité :
les naissances sont calculées à partir de la population féminines et de la table de fécondité
on garde la même proportion homme/femme que celle actuellement observée
End of explanation
from actuariat_python.data import fecondite_france
df = fecondite_france()
df.head()
from matplotlib import pyplot as plt
df.plot(x="age", y=["2005", "2015"]);
Explanation: Il y a un peu plus de garçons qui naissent chaque année.
End of explanation
ages = pandas.DataFrame(dict(age=range(0,120)))
merge = ages.merge(df, left_on="age", right_on="age", how="outer")
fecondite = merge.fillna(0.0)
fecondite[13:17]
mat_fec = fecondite[["2015"]].values / 100  # les chiffres sont pour 100 femmes
mat_fec.shape
mat_fec.sum()
Explanation: On convertit ces données en une matrice numpy sur 120 lignes comme les précédentes. On se sert des méthodes fillna et merge.
End of explanation
if mat_fec.shape[0] < pop.shape[0]:
    zeros = numpy.zeros((pop.shape[0] - mat_fec.shape[0], mat_fec.shape[1]))
    mat_fec = numpy.vstack([mat_fec, zeros])
mat_fec.sum()
Explanation: Si la matrice pop a plus de ligne que la matrice mat_fec, on doit compléter la seconde avec autant de lignes nulles que la précédente.
End of explanation
def naissances(pop, fec):
    # on suppose que pop est une matrice avec deux colonnes homme, femme
    # et que fec est une matrice avec une colonne fécondité
    n = pop[:, 1] * fec[:, 0]
    return n.sum()
nais = naissances(pop, mat_fec)
nais
Explanation: Il faut maintenant coder une fonction qui calcule le naissances pour l'année suivantes.
End of explanation
def iteration(pop, mortalite, fec, ratio):
    pop_next = pop * (1 - mortalite)
    nais = naissances(pop, fec)
    row = numpy.array([[nais * ratio, nais * (1 - ratio)]])
    pop_next = numpy.vstack([row, pop_next[:-1]]) # aucune naissance
    return pop_next
popt = pop
for year in range(2020, 2101):
    popt = iteration(popt, m, mat_fec, ratio)
    if year == 2050:
        popt2050 = popt.copy()
    if year == 2100:
        popt2100 = popt.copy()
                             
fig, ax = plt.subplots(1, 2, figsize=(10, 4))
plot_population_pyramid(popt2050[:, 0], popt2050[:, 1], ax=ax[0],
                        title="Pyramide des âges en 2050")
plot_population_pyramid(popt2100[:, 0], popt2100[:, 1], ax=ax[1],
                        title="Pyramide des âges en 2100");
Explanation: Et on reprend la fonction iteration et le code de l'exercice précédent :
End of explanation
total = [[2020, pop[:,0].sum(),pop[:,1].sum()]]
popt = pop
for year in range(2020, 2101):
    popt = iteration(popt, m, mat_fec, ratio)
    total.append([year, popt[:,0].sum(),popt[:,1].sum()])
                             
plot_population_pyramid(popt[:, 0], popt[:, 1], title="Pyramide des âges en 2101");
df = pandas.DataFrame(data=total, columns=["année","hommes","femmes"])
df.plot(x="année", y=["hommes", "femmes"], title="projection population française");
Explanation: On va plus loin et on stocke la population dans un vecteur :
End of explanation
from matplotlib import pyplot as plt
fig, ax = plt.subplots(1, 2, figsize=(14,6))
plot_population_pyramid(popt[:,0], popt[:,1], title="Pyramide des âges en 2050", ax=ax[0])
df.plot(x="année", y=["hommes", "femmes"], title="projection population française", ax=ax[1]);
Explanation: Le code suivant permet de combiner les deux graphes sur la même ligne avec la fonction subplots :
End of explanation
from tqdm import tqdm
evol = []
popt = pop0.copy()
age_etude = 23
pivot = list(range(60, 68))
for year in tqdm(range(2020, 2101)):
    popt = iteration(popt, m, mat_fec, ratio)
    jeune = popt[:age_etude + 1].sum()
    row = dict(year=year)
    for p in pivot:
        actif = popt[age_etude + 1:p].sum()
        retraite = popt[p:].sum()
        rt = actif / retraite
        row['r%d' % p] = rt
    evol.append(row)
df_evol = pandas.DataFrame(evol)
df_evol.head()
ax = df_evol.plot(x="year", y=["r%d" % p for p in pivot], figsize=(10, 4))
years = df_evol.year
ax.plot(years, [1.7 for _ in years], 'b--')
ax.set_title("Ratio actifs / retraités en fonction de l'âge pivot");
Explanation: Retraites
La réforme de la retraite en 2019 laissera un souvenir indélébile. Une des mesures far est celle de l'âge pivot. Le modèle développé jusqu'à présent ne permet pas de simuler ce que chaque français pourrait obtenir avec la nouvelle réforme mais il permet de donner un ordre de grandeur sur le nombre de retraités selon que l'âge pivot est de 62 ans ou plus vieux. Si on en croit cette page 1,7 actif cotisant par retraité, il y a 1,7 cotisant par retraité.
Trois populations sont considérées : les jeunes de moins de 21 ans, à la charge de leurs aînés, les actifs, les retraités dont le nombre dépend de l'âge pivot. on veut donc calculer l'évolution de ces trois populations.
End of explanation
<END_TASK>