Petit exercice “Open data” avec Python & Pandas

La plateforme de données ouvertes mises à disposition par le gouvernement (voir ici) est un formidable outil citoyen. C’est également une source de données avec laquelle jouer pour apprendre l’analyse avec Python.

Pour un premier exercice, j’ai utilisé la plateforme “Opendata” de l’assemblée nationale avec laquelle j’ai imaginé le petit exercice suivant, en deux étapes.

En préambule :

  • Comprendre la structure des données (pas évident)
  • Importer le fichier JSON avec Pandas

Puis l’exercice à proprement parler :

  • Déterminer si la parité est respectée
  • Représenter le nombre de députés par tranche d’âge
  • Calculer l’âge moyen par sexe
  • Lister les catégories professionnelles et métiers puis à représenter ceci de manière lisible par sexe.

Pour ce petit exercice, nous utilisons les données sur les députés en exercice au format JSON.

Les données : importation et compréhension

Importons tout d’abord le fichier dans “json_data”, à l’aide d’une instruction “with” grâce à laquelle on n’oubliera pas de refermer le fichier.

data_file_name = "AMO10_deputes_actifs_mandats_actifs_organes_XIV.json"

with open(data_file_name) as data_file:
    json_data = json.load(data_file, encoding='utf-8')

Ceci fait, nous pouvons essayer de comprendre la structure des données. On peut voir que c’est assez compliqué avec de nombreux sous-niveaux (état civil, mandature, adresse, date de début, etc.).

json_data.keys()
#dict_keys(['export']) 
json_data['export'].keys()
#dict_keys(['@xmlns:xsi', 'acteurs', 'organes'])
json_data['export']['acteurs'].keys()
#dict_keys(['acteur'])

Je vous laisserai regarder la structure du fichier pour repérer les éléments qui nous intéressent.

Données : extraction et traitement avec “pandas”

J’ai décidé de stocker les valeurs qui m’intéressent dans un DataFrame nommé “results”. Pour calculer l’âge, je dois récupérer la date de naissance, effectuer un petit regexp split. On pourrait le faire de manière plus élégante mais pour illustrer je fais comme-ceci :

dates_naissance = [json_data['export']['acteurs']['acteur'][i]['etatCivil']['infoNaissance']['dateNais'] for i in range(nb_acteurs)]
dates_naissance_split = [re.split('-',dates) for dates in dates_naissance]

La première ligne parcourt le fichier à la recherche de la variable “dateNais”. La date est exprimée sous la forme “AAAA-MM-JJ” et comme ici, seule l’année de naissance m’intéresse, je vais donc l’isoler du reste avec un split (une liste de liste 3 éléments) dont on ne conservera que le premier, comme indiqué ligne 1 ci-dessous :

results['annee_naissance'] = [int(dates_naissance_split[i][0]) for i in range(len(dates_naissance_split))]
results['age'] = (datetime.date.today().year)-results['annee_naissance']
results['profession'] = [json_data['export']['acteurs']['acteur'][i]['profession']['libelleCourant'] for i in range(nb_acteurs)]        
results['cat_prof'] = [json_data['export']['acteurs']['acteur'][i]['profession']['socProcINSEE']['famSocPro'] for i in range(nb_acteurs)]
results['cat_prof'] = results['cat_prof'].fillna('Sans profession')
results['civilité'] = [json_data['export']['acteurs']['acteur'][i]['etatCivil']['ident']['civ'] for i in range(nb_acteurs)]
results['civilité'] = results['civilité'].str.replace('Mme', 'Femme')
results['civilité'] = results['civilité'].str.replace('M.', 'Homme')

L’âge est tout simplement calculé en soustrayant l’année actuelle à celle de naissance. Les deux lignes suivantes récupèrent le métier et la catégorie professionnelle.

Ici nous avons un problème car les députés sans profession sont de type ‘None’ (on peut vérifier cela par exemple en tapant type(results['profession'][52]) )

La fonction “fillna” de Pandas permet de remplacer ces valeurs très rapidement, ici j’ai choisi “Sans profession”.

Enfin, pour trier par sexe, il nous faut trouver une information pertinente dans le fichier JSON. On pourrait imaginer travailler avec les prénoms mais heureusement pour nous dans l’état civil on trouve la civilité avec deux valeurs “M.” ou “Mme.”. Et donc, dans les deux dernières lignes, je me contente de remplacer ceci par “Homme” ou “Femme”.

A ce stade, notre tri de données est terminé et nous pouvons passer à quelques petites analyses.

Calculs élémentaires : parité, âge moyen.

Puisque nous avons stocké l’âge de chaque député dans notre DataFrame “results”, on peut facilement calculer l’âge moyen des députés  ainsi que l’âge moyen par sexe en groupant (DataFrame.groupby) puis filtrant (.get_group) les résultats.

age_moyen = np.mean(results['age'])
age_moy_hommes = np.mean(results.groupby(['civilité']).get_group('Homme')['age'])
age_moy_femmes = np.mean(results.groupby(['civilité']).get_group('Femme')['age'])

On voit donc que l’âge moyen est de 58 ans et 6 mois et que les femmes sont légèrement plus jeunes (57.1 ans) que les hommes (presque 59 ans).

nb_hommes = results.groupby(['civilité']).civilité.count()['Homme']
nb_femmes = results.groupby(['civilité']).civilité.count()['Femme']

La parité est donc loin d’être respectée puisque l’on a 426 hommes pour seulement 148 femmes.

Intéressons nous maintenant aux métiers exercés par les députés que nous pouvons également regrouper par catégories professionnelles

#On compte à l'aide de Counter les différentes professions
liste_professions = Counter(results['profession'])
#On peut faire un tri
liste_professions_tri = d.most_common() #[('Sans profession', 33), ('Avocat', 24), ('Fonctionnaire de catégorie A', 22), ('Cadre supérieur du secteur privé', 19),
#Extraction de la liste de métiers et de la fréquence (i.e. le nombre de députés exerçant chacun de ces métiers)
metier,nb_metier = zip(*liste_professions_tri)

On voit donc que 33 députés sont sans profession (i.e. politique professionnel), puis nous avons 24 avocats, 22 fonctionnaires de catégorie A, etc.

Représentations graphiques

Pour terminer, passons à la visualisation de ces résultats :

Hommes vs. Femmes

La parité à l’assemblée nationale

Nombre de députés par âge

Répartition des députés par catégorie professionnelle et par sexe

On voit que c’est assez bien réparti, sauf par exemple pour les journalistes qui ont tous plus de 60 ans chez les hommes et seulement 2 femmes journalistes dans les 50 ans.

Métiers députés

Principaux métiers exercés par les députés

Je donne ci-dessous le code utilisé pour l’affichage de ces graphes :

sns.set_context("notebook", font_scale=1)

#Barplot tout simple
plt.figure(1)
fig1 = sns.barplot(("Hommes","Femmes"),(nb_hommes,nb_femmes))
sns.axlabel(xlabel="",ylabel="Nombre")

#Barplot tout aussi simple (âge et fréquence)
plt.figure(2)
sns.axlabel(xlabel = 'Age des députés', ylabel = 'Nombre') 
fig2 = sns.barplot(ages,valeurs,color="steelblue")

#Stripplot
plt.figure(3)
plt.xticks(rotation='vertical') #Rotation du texte pour plus de lisibilité
plt.ylim(20,100)
fig3 = sns.stripplot(x="cat_prof", y="age", hue="civilité", data=results,split=True, size = 8)
sns.axlabel(xlabel = '', ylabel = 'Age des députés') 
plt.legend(loc="upper right", fontsize="small") #Positionnement de la légende
plt.tight_layout()
sns.plt.show()

plt.figure(4)
sns.axlabel(xlabel = 'Nombre', ylabel = '') 
fig4 = sns.barplot(nb_metier,metier)
plt.tight_layout()
sns.plt.show()

Conclusion

Nous avons vu que Python, à l’aide de la librairie Pandas, permet d’analyser les données ouvertes très simplement. J’espère que ce petit exercice vous permettra de démarrer dans l’exploitation des données ouvertes.

Bien entendu, il est possible d’y consacrer beaucoup plus de temps pour extraire des informations plus pertinentes, mais ça n’était pas le but de cet article.

Petit exercice supplémentaire : pouvez-vous représenter les métiers exercés par les députés en fonction de leur sexe (i.e. a-t-on plus de femmes avocat ? d’hommes chefs d’entreprise ?)

NB : Les labels sur certains graphes ne sont pas toujours très lisibles, il suffit de jouer avec la ligne sns.set_context, mais ça n’est pas le but de l’exercice 😉

 

Leave a Reply

Your email address will not be published. Required fields are marked *