Analyse de données sur les pays européens
Contenu
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 habitantesp_vie_f
(respectivement_h
) : esperance de vie des femmes (resp. homme) à la naissance en 2008vaccin_DTP
= % de la population vaccinée pour la diphtérie, coqueluche et tétanosvaccin_rougeole
= % population vaccinee pour la rougeolemedecins
: nombre de médecins exerçant leur profession et qui fournissent directement des soins aux patients pour 100,000 habitantsdepSprive
(respectivement public) : dépense dans le secteur public (resp. privé) en 2012 pour la santé en standard de pouvoir d’achat par habitantentree_UE
: année d’entrée dans l’UEprop_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 2019deces_SIDA
: nombre de deces dus au SIDA en 2018 (homme+femme)brasserie
: nombre de brasseries par millions d’habitants 2018pates
: Consommation de pâtes en kilogrammes par habitant en 2013dep_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:
Commencez par créer une nouvelle colonne appelée
esp_vie
dans la tableeurope
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 colonneesp_vie_h
deeurope
, faites en la somme, divisez par \(2\) et affectez le résultat à la colonneesp_vie
deeurope
(qui sera créée au vol).
### BEGIN SOLUTION
europe['esp_vie'] = (europe['esp_vie_f'] + europe['esp_vie_h']) / 2
### END SOLUTION
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
deDataFrame
:
### BEGIN SOLUTION
europe.hist(column=['esp_vie_h', 'esp_vie_f', 'esp_vie'], layout=(1,3));
### END SOLUTION
♣ (à 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
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
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
Appliquez la méthode
corr()
de la bibliothèquePandas
à la tableeurope
.
### BEGIN SOLUTION
europe.corr()
### END SOLUTION
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
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
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
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
Une fois dans l’UE on augmente son espérance de vie grâce aux politiques de cette union
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):
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
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
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 utiliserezreset_index
pour refaire de l’index une colonne à part entièrespecies
.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.