Analyse de données sur les pays européens

Nous allons chercher s’il existe des différences d’espérance de vie entre les pays européens. Si oui, existe-t-il certaines explications, sociétales, à ces différences ?

Pour mener cette analyse, nous nous baserons sur des données sur certains pays de l’Union Européenne, agrégées à partir de données issues d’un TP de l’Université Lyon1 et des données européennes.

Import des bibliothèques

import numpy as np                # Tableaux numériques
import pandas as pd               # Tables de données
import seaborn as sns; sns.set()  # Visualization
import matplotlib.pyplot as plt   # Visualization
# Configuration intégration dans Jupyter
%matplotlib inline

Première étape: description de la table de données

Les données sur les pays européens sont fournies dans le fichier CSV UE_health.csv que nous chargeons maintenant dans la table europe, en précisant que l’index est donné par la colonne pays.

europe = pd.read_csv("UE_health.csv", index_col="pays")

Les colonnes de cette table contiennent les informations suivantes :

  • depSante_hab : dépense pour la santé par habitant par an en 2016 par habitant

  • esp_vie_f (respectivement _h) : esperance de vie des femmes (resp. homme) à la naissance en 2008

  • vaccin_DTP = % de la population vaccinée pour la diphtérie, coqueluche et tétanos

  • vaccin_rougeole = % population vaccinee pour la rougeole

  • medecins : nombre de médecins exerçant leur profession et qui fournissent directement des soins aux patients pour 100,000 habitants

  • depSprive (respectivement public) : dépense dans le secteur public (resp. privé) en 2012 pour la santé en standard de pouvoir d’achat par habitant

  • entree_UE : année d’entrée dans l’UE

  • prop_scolarise = proportion des enfants entre trois ans et l’âge de début de l’enseignement primaire obligatoire.

  • esp_vie_bonne_sante : espérance de vie en bonne sante en 2019

  • deces_SIDA : nombre de deces dus au SIDA en 2018 (homme+femme)

  • brasserie : nombre de brasseries par millions d’habitants 2018

  • pates : Consommation de pâtes en kilogrammes par habitant en 2013

  • dep_ALD : dépense pour les soins de longue durée par habitant par an en 2016. Les soins de longue durée consistent en une gamme de services médicaux et de soins personnels qui sont consommés dans le but principal de soulager la douleur et la souffrance et de réduire ou de gérer la détérioration de l’état de santé chez les patients avec un degré de dépendance à long terme en standard de pouvoir dachat par habitant.

  • nobels : nombre de prix nobels par pays de naissance (toutes catégories) en 2020

Exercice :

Décrivez ci-dessous cette table par un texte de quelques lignes. Pour vous guider, répondez entre autres aux questions suivantes:

  • Quelle est la taille de la table ?

  • Quels sont les pays décrits ? Y a-t-il des pays de l’UE manquants ?

  • Quelles sont les colonnes numériques ? Et les autres ?

  • Y a-t-il des données manquantes ?

Pour trouver les informations, vous pouvez utiliser des commandes Python dans les cellules plus bas.

BEGIN SOLUTION

  • La table inclue 26 pays Européens; ni Chypre ni Malte ni le Royaume Uni ne sont présents.

  • Généralement on a les informations pour tous les pays mais parfois il en manque; par exemple il manque six pays pour le nombre de médecins et trois pour la depense privée.

END SOLUTION

### BEGIN SOLUTION
europe.shape
### END SOLUTION
### BEGIN SOLUTION
europe.describe(include='all')
### END SOLUTION

Deuxième étape : description de l’objectif

Nous allons chercher à apporter des questions à la question scientifique suivante: existe-t-il, d’un pays à l’autre, des déterminants de santé?

Un déterminant de santé est un « facteur définissables qui influence l’état de santé, ou qui y est associé » (Agence de la santé publique du Canada, 2007).

Exercice: Quelles données de la table proposez-vous d’utiliser, et comment, pour mesurer l’état de santé de la population?

BEGIN SOLUTION

Réponse : (esp_vie_f+esp_vie_h)/2 ou bien esp_vie_bonneSante (mais on va plutot considérer la première).

END SOLUTION

Troisième étape: observation de la table

On va commencer par observer nos données.

Exercice:

L’objectif est de donner des éléments de réponses aux questions descriptives suivantes:

  • Comment varie l’espérance de vie en fonction des pays ?

  • Comment varie l’espérance de vie en fonction du sexe ?

Pour cela:

  1. Commencez par créer une nouvelle colonne appelée esp_vie dans la table europe calculée en faisant la moyenne de l’espérance de vie des femmes et des hommes; pour simplifier, on pourra supposer qu’il y a autant de femmes que d’hommes dans la population.

    Indications: extrayez la colonne esp_vie_f et la colonne esp_vie_h de europe, faites en la somme, divisez par \(2\) et affectez le résultat à la colonne esp_vie de europe (qui sera créée au vol).

### BEGIN SOLUTION
europe['esp_vie'] = (europe['esp_vie_f'] + europe['esp_vie_h']) / 2
### END SOLUTION
  1. Représentez sous forme d’histogramme la distribution de l’espérance de vie pour chaque sexe et pour la moyenne des deux. Pour cela vous pouvez utiliser la méthode hist de DataFrame:

### BEGIN SOLUTION
europe.hist(column=['esp_vie_h', 'esp_vie_f', 'esp_vie'], layout=(1,3));
### END SOLUTION
  1. ♣ (à faire plus tard, lorsque vous aurez fini le reste du TP) Raffinez la figure pour que les trois histogrammes soient superposés, avec des couleurs pour les distinguer, des labels en Français, une légende.

    Indication: voir plt.hist.

### BEGIN SOLUTION
plt.figure()
plt.xlim(65,85)
plt.xlabel("Esperance de vie")
plt.hist(europe[['esp_vie_f','esp_vie_h','esp_vie']], color=["red", "blue","grey"],label=['Femmes', 'Hommes', 'Femmes + Hommes'])
plt.legend()
plt.show()
### END SOLUTION
  1. Calculez la différence d’espérance de vie entre hommes et femmes

### BEGIN SOLUTION
(europe['esp_vie_f'] - europe['esp_vie_h']).describe()
### END SOLUTION
  1. Décrivez les résultats observés

BEGIN SOLUTION

L’espérance de vie varie entre 72 et 82 ans selon les pays. La plupart étant autour de 80 ans.

Les femmes vivent plus longtemps que les hommes dans TOUS les pays. En moyenne six ans de plus mais cela varie entre 4,24 et 11,75

END SOLUTION

Nous allons continuer à observer les données.

Exercice

  1. Appliquez la méthode corr() de la bibliothèque Pandas à la table europe.

### BEGIN SOLUTION
europe.corr()
### END SOLUTION
  1. Pour mieux visualiser les corrélations, affichez celles-ci sous forme de carte de chaleur, comme vu au CM2:

### BEGIN SOLUTION
europe.corr().style.background_gradient(cmap ='coolwarm', axis=None)
### END SOLUTION
  1. Quelles sont les variables les plus corrélées à l”espérance de vie? Décrivez vos observations. Attention on ne demande pas d’interprétation à ce stade.

BEGIN SOLUTION

  • L’espérance de vie moyenne, celle des hommes et celle des femmes sont très fortement corrélées (>0.9).

  • On observe une forte corrélation >0.65 avec les dépenses de santé par habitant, avec les dépenses de santé publiques et, en valeur absolue, avec la date d’entrée dans l’UE.

END SOLUTION

  1. Quelles sont les informations qui ne corrèlent pas ou peu (< 0.3 en valeur absolue) avec l’espérance de vie des européen.nes ?

BEGIN SOLUTION

Il y a vaccin_DTP, medecins, depSprive, deces_SIDA, Brasseries

END SOLUTION

  1. Quelle(s) variable(s) corrèle(nt) fortement de façon négative avec l’espérance de vie?

BEGIN SOLUTION

La date d’entrée dans l’UE: plus on est entré tard plus l’espérance de vie est faible.

END SOLUTION

Première interprétation

Que pouvez vous dire de ces observations? Y a-t-il des choses étonnantes ou non attendues? Y a-t-il des choses attendues?

BEGIN SOLUTION

Il paraît étonnant que la dépense privée corrèle si peu, attendu que la dépense publique corrèle beaucoup.

END SOLUTION

Correlation n’est pas causalité

Exercice:

La corrélation entre la date d’entrée dans l’UE et l’espérance de vie moyenne est de -0.68 (exactement: -0.677157). Proposez deux hypothèses pour expliquer cette corrélation.

BEGIN SOLUTION

  1. Une fois dans l’UE on augmente son espérance de vie grâce aux politiques de cette union

  2. Les pays les plus riches sont dans l’UE depuis plus longtemps, tandis que les pays d’Europe de l’Est, entrés récemment dans l’UE, sont historiquement plus pauvres, donc avec moins de ressources à consacrer à la santé publique.

END SOLUTION

Préparation des données

Données manquantes

Exercice:

Complétez les valeurs manquantes de la table par la moyenne des colonnes correspondantes. Pour cela vous utiliserez la méthode fillna vue au CM2. Stockez le résultat dans la variable europe_filled.

### BEGIN SOLUTION
europe_filled = europe.fillna(europe.mean())
### END SOLUTION
assert isinstance(europe_filled, pd.DataFrame)
assert europe_filled.shape == europe.shape
assert (europe_filled.index == europe.index).all()
assert (abs(europe_filled.mean()-europe.mean()) < 0.001).all()
assert not (europe_filled.isna()).any(axis=None)

Standarisation de la table

Les résultats des calculs peuvent être fortement influencés, si ce n’est distordus, par l’ordre de grandeur des données, par exemple selon les unités utilisées pour les exprimer. Pour éviter cela, il est souvent souhaitable d’utiliser des tables de données standardisées, c’est-à-dire dans lesquelles chaque colonne est de moyenne \(0\) et d’écart type \(1\). Sauf cas particulier (colonne constante), toute table peut être standardisée: il suffit, colonne par colonne, de soustraire la moyenne puis de diviser par l’écart type. Nous reverrons cela en détails lors du CM3.

Exercice:

Standardisez la table europe_filled et stockez le résultat dans europe_std. Puis utilisez describe pour vérifier que la table obtenue est bien standardisée.

Pour rappel, la moyenne se calcule par colonne avec la méthode mean et l’écart-type avec la méthode std.

### BEGIN SOLUTION
europe_std =  (europe_filled - europe_filled.mean()) / europe_filled.std()

europe_std.describe()
### END SOLUTION
assert isinstance(europe_std, pd.DataFrame)
assert europe_std.shape == europe.shape
assert (europe_std.index == europe.index).all()
assert (abs(europe_std.mean()) < 0.001).all()
assert (abs(europe_std.std()-1) < 0.001).all()

Exercice:

Affichez la matrice de corrélation sous forme de carte de chaleur:

### BEGIN SOLUTION
europe_std.corr().style.background_gradient(cmap ='coolwarm', axis=None)
### END SOLUTION

Régressions linéaires

On peut représenter des regressions linéaires entre l’espérance de vie et n’importe quelle autre donnée. Prenons l’exemple de la dépense publique de santé qui a la plus forte corrélation. On représente l’espérance de vie (variable aléatoire) en fonction de la dépense publique de santé (variable explicative): chaque point correspond à un pays. La droite bleue correspond au modèle linéaire du type: esp_vie = a * dep_Spublic + b avec (a,b) les paramètres ajustés:

sns.regplot(x="dep_Spublic", y="esp_vie", data=europe_std)

Exercice: Représenter les régressions linéaires pour les six variables données dans le vecteur “determinants”. Pour cela remplissez le code ci-dessous en utilisant une boucle for et, comme ci-dessus, la fonction regplot du package seaborn. Quels déterminants semblent prédictifs de l’espérance de vie? Est-ce les mêmes qu’avec le calcul de la corrélation ?

determinants=['esp_vie_bonne_sante', 'entree_UE','brasseries', 'dep_Spublic','dep_Sprive','medecins']

# Ici, écrivez une boucle for pour repésenter graphiquement la régression linéaire 
# pour chaque déterminant dans l'objet plot.
plt.figure(figsize=(10,10))
### BEGIN SOLUTION
for param in determinants:
    plot = sns.regplot(x=param, y="esp_vie", data=europe_std) 
    #oui cest les memes qui sont intéressants
### END SOLUTION
plot.set_xlim(-2,2.5)
plot.set_xlabel("Potentiels déterminants")
plot.set_ylabel("Espérance de vie")
plot.legend(labels=determinants, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);

Exercice: Commentez et interprétez cette figure.

Quatrième étape: la modélisation

Comme vu en cours CM2, on peut combiner les déterminants et construire un modèle linéaire généralisé (GLM). Par exemple, sachant que l’espérance de vie dépend de la dépense en santé par habitant, on peut construire un modèle simple qui ne dépend que de ce déterminant:

import statsmodels.formula.api as smf
import statsmodels.api as sm
frm = "esp_vie ~ depSante_hab "
model = smf.glm(formula = frm, data = europe_filled)
result = model.fit()
result.summary()
result.pvalues

La p-value est inférieure à \(0.05\) : on peut rejeter l’hypothèse nulle et conclure que la dépense en santé par habitant permet d’estimer l’espérance de vie moyenne. Le modèle est du type: esp_vie = 0.0014 * depSante_hab + 73.8531.

Les modèles linéaires généralisés peuvent etre utilisés dans le cas où :

  • Il n’y a pas de données extrêmes qui fausseraient le modèle: les résultats du GLM sont fortement influencées par une ou plusieurs valeurs beaucoup plus grandes que les autres dans les variables explicatives.

  • Les variables explicatives sont indépendantes : quand \(X_i\) augmente \(Y\) peut etre modifié mais les autres variables explicatives restent inchangées. Ceci est impossible quand les variables sont corrélées entre elles.

  • Lorsqu’on a trop de variables explicatives par rapport aux données même si elles n’ont aucun pouvoir explicatif, le modèle peut toujours parfaitement expliquer les données (chaque variable explique une donnée, indépendemment les une des autres). Le problème c’est que le modèle n’est pas généralisable à un autre jeu de données.

Exercice \(\clubsuit\):

Proposez un modèle explicatif avec un GLM avec la dépense en santé par habitant et la dépense dans le secteur public. Décrivez brièvement les résultats (on ne vous demande pas de les interpréter). Est-ce qu’on a le droit de faire un modèle GLM avec ces deux paramètres ? Pourquoi ?

Consigne: Vous utiliserez les cellules ci-dessous pour faire les calculs dont vous aurez besoin (en supprimant celles que vous n’utiliserez pas) et répondrez aux questions ici:

BEGIN SOLUTION

  • esp_vie = 71.76 + 0.0010 * depSante_hab

  • La dep_Spublic n’entre pas en compte dans le modèle car la p-value est > 0.05; on ne peut pas rejeter l’hypothèse nulle.

  • La dépense dans le secteur public et la dépense en Santé par habitant corrèlent, on ne peut pas faire de GLM car une des hypothèses n’est pas valide !

END SOLUTION

### BEGIN SOLUTION
frm = "esp_vie ~ depSante_hab + dep_Spublic"
model = smf.glm(formula = frm, data = europe_filled)
result = model.fit()
result.summary()
### END SOLUTION 
### BEGIN SOLUTION
result.pvalues
### END SOLUTIONS

Nouveau jeu de données : les fleurs d’iris

Le jeu de données suivant a été collecté par Fisher en 1936 et comprend des échantillons de trois espèces d’iris (Iris setosa, Iris virginica et Iris versicolor). Pour chaque échantillon, vous avez accès à : la longueur (length) et la largeur (width) des sépales (sepal) et des pétales (petal), en centimètres.

Ce jeu de données est fourni avec la bibliothèque seaborn et vous pouvez le charger avec:

import seaborn
iris = seaborn.load_dataset("iris").set_index("species")

L’appel à set_index transforme la colonne species en index; cela a notamment comme avantage que toutes les colonnes de la table obtenue sont numériques.

Exercice \(\clubsuit\):

Menez sur ce jeu de données une analyse similaire à celles que nous avons mené depuis le début du TP.

Consigne: Vous répondrez aux questions ici, en utilisant les cellules ci-dessous pour faire les calculs dont vous aurez besoin (et en supprimant celles que vous n’utiliserez pas):

  1. Décrivez les données. En particulier répondez au minimum aux questions suivantes: Quelle est la taille du jeu de données? Y a-t-il des données manquantes ?

    BEGIN SOLUTION

    Il y a 150 échantillons (50 échantillons de chaque espèce) et pas de données manquantes.

    END SOLUTION

  2. Calculez les corrélations deux à deux et représentez les valeurs à l’aide d’un carte de chaleur. Que pouvez vous en dire ?

    BEGIN SOLUTION

    La longueur et la largeur des pétales sont très fortement correllés entre elles, fortement correllées positivement avec la longueur des sépales, et anticorrellés avec la largeur de ces derniers.

    En revanche, longueur et largeur des sépales sont très peu corrélés.

    END SOLUTION

  3. Représentez les variations de données à l’aide de pair plots vus en cours. Selon vous, existe-t-il une ou plusieurs de ces quatre caractéristiques qui permet de différencier les espèces ?

    Indication: nous avons vu en cours l’utilisation de l’argument hue pour classer les échantillons en fonction du contenu d’une colonne. Ici, on veut classer en fonction de l’espèce. Pour cela, vous utiliserez reset_index pour refaire de l’index une colonne à part entière species.

    BEGIN SOLUTION

    La largeur et la longueur des pétales séparent plutôt bien les différentes espèces.

    END SOLUTION

### BEGIN SOLUTION
iris.head()
### END SOLUTION
### BEGIN SOLUTION
iris.shape
### END SOLUTION
### BEGIN SOLUTION
iris.describe()
### END SOLUTION
### BEGIN SOLUTION
iris.groupby("species").count()
### END SOLUTION
### BEGIN SOLUTION
iris.corr().style.background_gradient(cmap ='coolwarm', axis=None)
### END SOLUTION
### BEGIN SOLUTION
sns.pairplot(iris.reset_index(), hue="species")
plt.show()
### END SOLUTION

Conclusion

Nous avons fini cette première partie de ce cours consacré aux analyses de données, avec un accent mis sur la [VI]sualisation des données. La semaine prochaine et jusqu’à fin du cours nous nous concentrerons sur la classification d’image par apprentissage statistiques, avec le schema d’analyse complet: VI-ME-BA-BAR.

Bravo, vous avez fini le TP.

Déposez votre travail (avec submit) et téléchargez votre score (avec fetch_feedback). Vous pourrez utiliser à nouveau fetch_feedback pour télécharger votre score et les commentaires après correction par votre enseignant.