L'âge moyen des députés français
Dans ce billet, je calcule la moyenne d'âge des députés français (c'est-à-dire les élus qui représentent les français à l'assemblée nationale, à ne pas confondre avec les sénateurs). Je me sers pour cela d'un document disponible sur le site de l'assemblée nationale, en PDF. J'en extrais les textes puis les traite afin de tracer un histogramme à l'aide du package d'analyse de données pour Python pandas
.
Introduction¶
Pourquoi calculer l'âge moyen des députés ? Bonne question : l'idée m'est venue suite à la lecture de ce tweet.
La moyennz d'âge des députés et sénateurs français est de 65 ans ! STOP ! Place à la jeunesse !
— Geof'☭ (@DebailleuxG) January 2, 2016
Comment savoir si ce qui est affirmé ici est vrai ou pas ? Je me concentre ici sur une réponse partielle : calculer l'âge moyen des députés et plus exactement leur distribution.
Ca tombe bien, car l'assemblée nationale met à disposition une liste alphabétique en PDF ici. Nous allons nous en servir pour calculer la distribution des âges.
Conversion du PDF en texte¶
Il s'agit tout d'abord de convertir le fichier PDF en fichier texte brut. Je me sers ici de pdftotext
, un utilitaire que l'on peut utiliser en ligne de commande.
!pdftotext.exe
pdftotext version 3.04 Copyright 1996-2014 Glyph & Cog, LLC Usage: pdftotext [options] <PDF-file> [<text-file>] -f <int> : first page to convert -l <int> : last page to convert -layout : maintain original physical layout -table : similar to -layout, but optimized for tables -lineprinter : use strict fixed-pitch/height layout -raw : keep strings in content stream order -fixed <fp> : assume fixed-pitch (or tabular) text -linespacing <fp> : fixed line spacing for LinePrinter mode -clip : separate clipped text -enc <string> : output text encoding name -eol <string> : output end-of-line convention (unix, dos, or mac) -nopgbrk : don't insert page breaks between pages -opw <string> : owner password (for encrypted files) -upw <string> : user password (for encrypted files) -q : don't print any messages or errors -cfg <string> : configuration file to use in place of .xpdfrc -v : print copyright and version info -h : print usage information -help : print usage information --help : print usage information -? : print usage information
Après avoir lu l'aide, on peut convertir le fichier PDF en texte à l'aide de la commande suivante.
!pdftotext.exe trombinoscope_alpha.pdf
Lisons maintenant le fichier texte créé pour en extraire les noms des députés et les dates de naissance.
Extraction du texte¶
Travail sur fragment¶
Après avoir regardé le contenu du fichier texte au bloc note, je me suis rendu compte que les noms et dates de naissance sont dans le désordre. Pas de problème, on va lire ça avec des expressions régulières et on va voir si on arrive à trouver ce qu'on cherche ! On va travailler sur la première page dans un premier temps (on commence petit avant d'étendre la méthode au document entier) :
first_page = """Ain - Circonscription n° 5
M. Damien Abad
Val-de-Marne - Circonscription n° 6
Mme Laurence Abeille
Les Républicains Né le 5 avril 1980 à Nîmes (Gard) Secrétaire d'âge de l'Assemblée nationale
Président du conseil départemental (Ain) Élu à l'Assemblée nationale le 20 juin 2012
Mayotte - Circonscription n° 2
M. Ibrahim Aboubacar
Écologiste Née le 17 juin 1960 à Neuilly-sur-Seine (Hauts-de-Seine)
Cadre du secteur privé Élue à l'Assemblée nationale le 20 juin 2012
Hérault - Circonscription n° 6
M. Élie Aboud
Socialiste, républicain et citoyen Né le 1erfévrier 1965 à Fomboni (Comores)
Ingénieur des travaux publics Élu à l'Assemblée nationale le 20 juin 2012
Haute-Savoie - Circonscription n° 1
M. Bernard Accoyer
Les Républicains Né le 12 octobre 1959 à Beyrouth (Liban)
Médecin cardiologue (Chef de service) Adjoint au Maire de Béziers Élu à l'Assemblée nationale le 20 juin 2007 Réélu le 16 décembre 2012
Finistère - Circonscription n° 2
Mme Patricia Adam
Les Républicains Né le 12 août 1945 à Lyon (Rhône)
Socialiste, républicain et citoyen Née le 15 avril 1953 à Saint-Cloud (Hauts-de-Seine)
Médecin ORL
Maire d'Annecy-le-Vieux
Élu à l'Assemblée nationale le 28 mars 1993 Réélu les 1erjuin 1997, 16 juin 2002, 20 juin 2007 et 20 juin 2012
Cadre d'action sociale Élue à l'Assemblée nationale le 16 juin 2002 Réélue les 20 juin 2007 et 20 juin 2012
15"""
Construisons d'abord une expression régulière pour les prénoms :
import re
p = re.compile("^(?:M\.|Mme) ((?:[\w\-]+.?)+)", re.MULTILINE)
p.findall(first_page)
['Damien Abad', 'Laurence Abeille', 'Ibrahim Aboubacar', 'Élie Aboud', 'Bernard Accoyer', 'Patricia Adam']
On peut maintenant passer aux dates de naissance :
p2 = re.compile('Née? le (\d+(?: |er)\w+ \d+)', re.MULTILINE)
p2.findall(first_page)
['5 avril 1980', '17 juin 1960', '1erfévrier 1965', '12 octobre 1959', '12 août 1945', '15 avril 1953']
L'expression régulière trouvée est un peu compliquée à cause de la présence de dates écrites sous la forme 1erfévrier
. On pourra aussi noter l'utilisation de groupes non-capturants, qui permettent de matcher des parties d'expression mais de ne pas les conserver dans la sortie finale. Ceux-ci s'écrivent (?:...)
au lieu de (...)
pour les groupes normaux.
Extraction du texte complet¶
Maintenant que la première page donne des résultats valides, passons au texte complet.
text = open('trombinoscope_alpha.txt').read()
names = p.findall(text)
names[:50]
['Damien Abad', 'Laurence Abeille', 'Ibrahim Aboubacar', 'Élie Aboud', 'Bernard Accoyer', 'Patricia Adam', 'Sylviane Alaux', 'Éric Alauzet', 'Yves Albarello', 'Brigitte Allain', 'Jean-Pierre Allossery', 'Nicole Ameline', 'Pouria Amirshahi', 'François André', 'Sylvie Andrieux', 'Benoist Apparu', 'Nathalie Appéré', 'Kader Arif', 'Laurence Arribagé', 'François Asensi', 'Christian Assaf', 'Isabelle Attard', 'Julien Aubert', 'Olivier Audibert Troin', 'Danielle Auroi', 'Pierre Aylagas', 'Jean-Marc Ayrault', 'Bruno Nestor Azerot', 'Alexis Bachelay', 'Guillaume Bachelay', 'Jean-Paul Bacquet', 'Dominique Baert', 'Guy Bailliart', 'Patrick Balkany', 'Gérard Bapt', 'Frédéric Barbier', 'Jean-Pierre Barbier', 'Serge Bardy', 'Ericka Bareigts', 'Claude Bartolone', 'Christian Bataille', 'Delphine Batho', 'Marie-Noëlle Battistel', 'Laurent Baumel', 'Philippe Baumel', 'Denis Baupin', 'Nicolas Bays', 'Catherine Beaubatie', 'Marie-Françoise Bechtel', 'Jean-Marie Beffara']
On voit que l'expression régulière fonctionne plutôt bien. Comptons le nombre de députés identifiés.
len(names)
577
On a le compte pour les noms ! Passons aux dates de naissance.
birthdates = p2.findall(text)
birthdates[:50]
['5 avril 1980', '17 juin 1960', '1erfévrier 1965', '12 octobre 1959', '12 août 1945', '15 avril 1953', '1erjuillet 1945', '7 juin 1958', '17 mars 1952', '23 avril 1956', '31 juillet 1945', '4 juillet 1952', '27 mars 1972', '19 juillet 1967', '15 décembre 1961', '24 novembre 1969', '8 juillet 1975', '3 juillet 1959', '25 mai 1970', '1erjuin 1945', '1erseptembre 1972', '14 novembre 1969', '11 juin 1978', '16 août 1960', '29 février 1944', '24 juillet 1942', '25 janvier 1950', '22 juillet 1961', '19 août 1973', '13 juillet 1974', '11 mars 1949', '24 octobre 1959', '24 septembre 1946', '16 août 1948', '4 février 1946', '30 août 1960', '11 novembre 1960', '6 novembre 1947', '16 avril 1967', '29 juillet 1951', '13 mai 1946', '23 mars 1973', '20 août 1956', '13 août 1965', '14 novembre 1961', '2 juin 1962', '1ermai 1977', '10 février 1964', '19 mars 1946', '13 octobre 1962']
len(birthdates)
577
On a aussi le compte pour les dates de naissance ! On peut mettre tout ça dans une dataframe Pandas.
Analyse des données à l'aide de Pandas¶
Avant de pouvoir utiliser pandas pour faire des calculs de type Excel, il nous faut encore convertir les dates de naissance, qui sont du texte, vers des objets de type date.
On va utiliser pd.Timestamp
pour stocker les dates au bon format. On peut donner à l'objet Timestamp des chaînes de caractère en entrée, chose que nous allons faire.
import pandas as pd
import matplotlib.pyplot as plt
plt.style.use('ggplot')
pd.Timestamp('2012/5/1')
Timestamp('2012-05-01 00:00:00')
Nous pouvons écire la fonction ci-dessous :
def convert2date(date_str):
d = {'janvier': 1, 'février': 2, 'mars': 3, 'avril': 4,
'mai': 5, 'juin': 6, 'juillet': 7, 'août': 8,
'septembre': 9, 'octobre': 10, 'novembre': 11, 'décembre':12}
splits = date_str.split(' ')
if len(splits) != 3:
start = splits.pop(0)
if start.startswith('1er'):
splits.insert(0, start[3:])
splits.insert(0, '1')
return pd.Timestamp(splits[2] + '/' + str(d[splits[1]]) + '/' + splits[0])
Testons notre fonction sur notre collection de dates de naissance. Nous sélectionnons ici seulement les dates qui commencent par le 3 du mois.
[b for b in birthdates if b.startswith('3 ')]
['3 juillet 1959', '3 septembre 1963', '3 janvier 1966', '3 janvier 1973', '3 février 1948', '3 janvier 1940', '3 mars 1951', '3 août 1941', '3 janvier 1958', '3 février 1967', '3 août 1956', '3 décembre 1950', '3 août 1953', '3 mars 1940', '3 mai 1955', '3 mars 1949', '3 mai 1951', '3 août 1945', '3 août 1951', '3 janvier 1974', '3 janvier 1948', '3 janvier 1947', '3 février 1958']
Nous vérifions ci-dessous que les résultats sont pertinents :
[convert2date(b) for b in birthdates if b.startswith('3 ')]
[Timestamp('1959-07-03 00:00:00'), Timestamp('1963-09-03 00:00:00'), Timestamp('1966-01-03 00:00:00'), Timestamp('1973-01-03 00:00:00'), Timestamp('1948-02-03 00:00:00'), Timestamp('1940-01-03 00:00:00'), Timestamp('1951-03-03 00:00:00'), Timestamp('1941-08-03 00:00:00'), Timestamp('1958-01-03 00:00:00'), Timestamp('1967-02-03 00:00:00'), Timestamp('1956-08-03 00:00:00'), Timestamp('1950-12-03 00:00:00'), Timestamp('1953-08-03 00:00:00'), Timestamp('1940-03-03 00:00:00'), Timestamp('1955-05-03 00:00:00'), Timestamp('1949-03-03 00:00:00'), Timestamp('1951-05-03 00:00:00'), Timestamp('1945-08-03 00:00:00'), Timestamp('1951-08-03 00:00:00'), Timestamp('1974-01-03 00:00:00'), Timestamp('1948-01-03 00:00:00'), Timestamp('1947-01-03 00:00:00'), Timestamp('1958-02-03 00:00:00')]
Nous pouvons maintenant fabriquer notre DataFrame avec l'ensemble des données :
df = pd.DataFrame(data=list(zip([n for n in names],
[convert2date(b) for b in birthdates])),
columns=('Noms', 'Dates de naissance'))
df
Noms | Dates de naissance | |
---|---|---|
0 | Damien Abad | 1980-04-05 |
1 | Laurence Abeille | 1960-06-17 |
2 | Ibrahim Aboubacar | 1965-02-01 |
3 | Élie Aboud | 1959-10-12 |
4 | Bernard Accoyer | 1945-08-12 |
5 | Patricia Adam | 1953-04-15 |
6 | Sylviane Alaux | 1945-07-01 |
7 | Éric Alauzet | 1958-06-07 |
8 | Yves Albarello | 1952-03-17 |
9 | Brigitte Allain | 1956-04-23 |
10 | Jean-Pierre Allossery | 1945-07-31 |
11 | Nicole Ameline | 1952-07-04 |
12 | Pouria Amirshahi | 1972-03-27 |
13 | François André | 1967-07-19 |
14 | Sylvie Andrieux | 1961-12-15 |
15 | Benoist Apparu | 1969-11-24 |
16 | Nathalie Appéré | 1975-07-08 |
17 | Kader Arif | 1959-07-03 |
18 | Laurence Arribagé | 1970-05-25 |
19 | François Asensi | 1945-06-01 |
20 | Christian Assaf | 1972-09-01 |
21 | Isabelle Attard | 1969-11-14 |
22 | Julien Aubert | 1978-06-11 |
23 | Olivier Audibert Troin | 1960-08-16 |
24 | Danielle Auroi | 1944-02-29 |
25 | Pierre Aylagas | 1942-07-24 |
26 | Jean-Marc Ayrault | 1950-01-25 |
27 | Bruno Nestor Azerot | 1961-07-22 |
28 | Alexis Bachelay | 1973-08-19 |
29 | Guillaume Bachelay | 1974-07-13 |
... | ... | ... |
547 | Stéphane Travert | 1969-10-12 |
548 | Catherine Troallic | 1974-05-10 |
549 | Jean-Paul Tuaiva | 1972-10-30 |
550 | Cécile Untermaier | 1951-12-28 |
551 | Jean-Jacques Urvoas | 1959-09-19 |
552 | Daniel Vaillant | 1949-07-19 |
553 | Jacques Valax | 1951-08-23 |
554 | François Vannson | 1962-10-20 |
555 | Catherine Vautrin | 1960-07-26 |
556 | Michel Vauzelle | 1944-08-15 |
557 | Francis Vercamer | 1958-05-10 |
558 | Patrice Verchère | 1973-12-29 |
559 | Fabrice Verdier | 1968-12-16 |
560 | Michel Vergnier | 1946-11-25 |
561 | Arnaud Viala | 1974-12-04 |
562 | Jean-Sébastien Vialatte | 1951-01-30 |
563 | Jean-Pierre Vigier | 1969-10-22 |
564 | Philippe Vigier | 1958-02-03 |
565 | Patrick Vignal | 1958-01-22 |
566 | François-Xavier Villain | 1950-05-31 |
567 | Jean-Michel Villaumé | 1946-03-27 |
568 | Philippe Vitel | 1955-02-22 |
569 | Jean Jacques Vlody | 1967-08-19 |
570 | Michel Voisin | 1944-10-06 |
571 | Jean-Luc Warsmann | 1965-10-22 |
572 | Laurent Wauquiez | 1975-04-12 |
573 | Éric Woerth | 1956-01-29 |
574 | Paola Zanetti | 1976-09-01 |
575 | Marie-Jo Zimmermann | 1951-04-29 |
576 | Michel Zumkeller | 1966-01-21 |
577 rows × 2 columns
Armé de cette dataframe, nous pouvons calculer l'âge des différents députés assez facilement :
import datetime
datetime.date.today()
datetime.date(2016, 1, 8)
df['Âge'] = pd.Timestamp(datetime.date.today()) - df['Dates de naissance']
df
Noms | Dates de naissance | Âge | |
---|---|---|---|
0 | Damien Abad | 1980-04-05 | 13061 days |
1 | Laurence Abeille | 1960-06-17 | 20293 days |
2 | Ibrahim Aboubacar | 1965-02-01 | 18603 days |
3 | Élie Aboud | 1959-10-12 | 20542 days |
4 | Bernard Accoyer | 1945-08-12 | 25716 days |
5 | Patricia Adam | 1953-04-15 | 22913 days |
6 | Sylviane Alaux | 1945-07-01 | 25758 days |
7 | Éric Alauzet | 1958-06-07 | 21034 days |
8 | Yves Albarello | 1952-03-17 | 23307 days |
9 | Brigitte Allain | 1956-04-23 | 21809 days |
10 | Jean-Pierre Allossery | 1945-07-31 | 25728 days |
11 | Nicole Ameline | 1952-07-04 | 23198 days |
12 | Pouria Amirshahi | 1972-03-27 | 15992 days |
13 | François André | 1967-07-19 | 17705 days |
14 | Sylvie Andrieux | 1961-12-15 | 19747 days |
15 | Benoist Apparu | 1969-11-24 | 16846 days |
16 | Nathalie Appéré | 1975-07-08 | 14794 days |
17 | Kader Arif | 1959-07-03 | 20643 days |
18 | Laurence Arribagé | 1970-05-25 | 16664 days |
19 | François Asensi | 1945-06-01 | 25788 days |
20 | Christian Assaf | 1972-09-01 | 15834 days |
21 | Isabelle Attard | 1969-11-14 | 16856 days |
22 | Julien Aubert | 1978-06-11 | 13725 days |
23 | Olivier Audibert Troin | 1960-08-16 | 20233 days |
24 | Danielle Auroi | 1944-02-29 | 26246 days |
25 | Pierre Aylagas | 1942-07-24 | 26831 days |
26 | Jean-Marc Ayrault | 1950-01-25 | 24089 days |
27 | Bruno Nestor Azerot | 1961-07-22 | 19893 days |
28 | Alexis Bachelay | 1973-08-19 | 15482 days |
29 | Guillaume Bachelay | 1974-07-13 | 15154 days |
... | ... | ... | ... |
547 | Stéphane Travert | 1969-10-12 | 16889 days |
548 | Catherine Troallic | 1974-05-10 | 15218 days |
549 | Jean-Paul Tuaiva | 1972-10-30 | 15775 days |
550 | Cécile Untermaier | 1951-12-28 | 23387 days |
551 | Jean-Jacques Urvoas | 1959-09-19 | 20565 days |
552 | Daniel Vaillant | 1949-07-19 | 24279 days |
553 | Jacques Valax | 1951-08-23 | 23514 days |
554 | François Vannson | 1962-10-20 | 19438 days |
555 | Catherine Vautrin | 1960-07-26 | 20254 days |
556 | Michel Vauzelle | 1944-08-15 | 26078 days |
557 | Francis Vercamer | 1958-05-10 | 21062 days |
558 | Patrice Verchère | 1973-12-29 | 15350 days |
559 | Fabrice Verdier | 1968-12-16 | 17189 days |
560 | Michel Vergnier | 1946-11-25 | 25246 days |
561 | Arnaud Viala | 1974-12-04 | 15010 days |
562 | Jean-Sébastien Vialatte | 1951-01-30 | 23719 days |
563 | Jean-Pierre Vigier | 1969-10-22 | 16879 days |
564 | Philippe Vigier | 1958-02-03 | 21158 days |
565 | Patrick Vignal | 1958-01-22 | 21170 days |
566 | François-Xavier Villain | 1950-05-31 | 23963 days |
567 | Jean-Michel Villaumé | 1946-03-27 | 25489 days |
568 | Philippe Vitel | 1955-02-22 | 22235 days |
569 | Jean Jacques Vlody | 1967-08-19 | 17674 days |
570 | Michel Voisin | 1944-10-06 | 26026 days |
571 | Jean-Luc Warsmann | 1965-10-22 | 18340 days |
572 | Laurent Wauquiez | 1975-04-12 | 14881 days |
573 | Éric Woerth | 1956-01-29 | 21894 days |
574 | Paola Zanetti | 1976-09-01 | 14373 days |
575 | Marie-Jo Zimmermann | 1951-04-29 | 23630 days |
576 | Michel Zumkeller | 1966-01-21 | 18249 days |
577 rows × 3 columns
Le problème, c'est que l'âge est en jours, on va donc le convertir en années :
df['Âge (années)'] = [x.days / 365 for x in df['Âge']]
df
Noms | Dates de naissance | Âge | Âge (années) | |
---|---|---|---|---|
0 | Damien Abad | 1980-04-05 | 13061 days | 35.783562 |
1 | Laurence Abeille | 1960-06-17 | 20293 days | 55.597260 |
2 | Ibrahim Aboubacar | 1965-02-01 | 18603 days | 50.967123 |
3 | Élie Aboud | 1959-10-12 | 20542 days | 56.279452 |
4 | Bernard Accoyer | 1945-08-12 | 25716 days | 70.454795 |
5 | Patricia Adam | 1953-04-15 | 22913 days | 62.775342 |
6 | Sylviane Alaux | 1945-07-01 | 25758 days | 70.569863 |
7 | Éric Alauzet | 1958-06-07 | 21034 days | 57.627397 |
8 | Yves Albarello | 1952-03-17 | 23307 days | 63.854795 |
9 | Brigitte Allain | 1956-04-23 | 21809 days | 59.750685 |
10 | Jean-Pierre Allossery | 1945-07-31 | 25728 days | 70.487671 |
11 | Nicole Ameline | 1952-07-04 | 23198 days | 63.556164 |
12 | Pouria Amirshahi | 1972-03-27 | 15992 days | 43.813699 |
13 | François André | 1967-07-19 | 17705 days | 48.506849 |
14 | Sylvie Andrieux | 1961-12-15 | 19747 days | 54.101370 |
15 | Benoist Apparu | 1969-11-24 | 16846 days | 46.153425 |
16 | Nathalie Appéré | 1975-07-08 | 14794 days | 40.531507 |
17 | Kader Arif | 1959-07-03 | 20643 days | 56.556164 |
18 | Laurence Arribagé | 1970-05-25 | 16664 days | 45.654795 |
19 | François Asensi | 1945-06-01 | 25788 days | 70.652055 |
20 | Christian Assaf | 1972-09-01 | 15834 days | 43.380822 |
21 | Isabelle Attard | 1969-11-14 | 16856 days | 46.180822 |
22 | Julien Aubert | 1978-06-11 | 13725 days | 37.602740 |
23 | Olivier Audibert Troin | 1960-08-16 | 20233 days | 55.432877 |
24 | Danielle Auroi | 1944-02-29 | 26246 days | 71.906849 |
25 | Pierre Aylagas | 1942-07-24 | 26831 days | 73.509589 |
26 | Jean-Marc Ayrault | 1950-01-25 | 24089 days | 65.997260 |
27 | Bruno Nestor Azerot | 1961-07-22 | 19893 days | 54.501370 |
28 | Alexis Bachelay | 1973-08-19 | 15482 days | 42.416438 |
29 | Guillaume Bachelay | 1974-07-13 | 15154 days | 41.517808 |
... | ... | ... | ... | ... |
547 | Stéphane Travert | 1969-10-12 | 16889 days | 46.271233 |
548 | Catherine Troallic | 1974-05-10 | 15218 days | 41.693151 |
549 | Jean-Paul Tuaiva | 1972-10-30 | 15775 days | 43.219178 |
550 | Cécile Untermaier | 1951-12-28 | 23387 days | 64.073973 |
551 | Jean-Jacques Urvoas | 1959-09-19 | 20565 days | 56.342466 |
552 | Daniel Vaillant | 1949-07-19 | 24279 days | 66.517808 |
553 | Jacques Valax | 1951-08-23 | 23514 days | 64.421918 |
554 | François Vannson | 1962-10-20 | 19438 days | 53.254795 |
555 | Catherine Vautrin | 1960-07-26 | 20254 days | 55.490411 |
556 | Michel Vauzelle | 1944-08-15 | 26078 days | 71.446575 |
557 | Francis Vercamer | 1958-05-10 | 21062 days | 57.704110 |
558 | Patrice Verchère | 1973-12-29 | 15350 days | 42.054795 |
559 | Fabrice Verdier | 1968-12-16 | 17189 days | 47.093151 |
560 | Michel Vergnier | 1946-11-25 | 25246 days | 69.167123 |
561 | Arnaud Viala | 1974-12-04 | 15010 days | 41.123288 |
562 | Jean-Sébastien Vialatte | 1951-01-30 | 23719 days | 64.983562 |
563 | Jean-Pierre Vigier | 1969-10-22 | 16879 days | 46.243836 |
564 | Philippe Vigier | 1958-02-03 | 21158 days | 57.967123 |
565 | Patrick Vignal | 1958-01-22 | 21170 days | 58.000000 |
566 | François-Xavier Villain | 1950-05-31 | 23963 days | 65.652055 |
567 | Jean-Michel Villaumé | 1946-03-27 | 25489 days | 69.832877 |
568 | Philippe Vitel | 1955-02-22 | 22235 days | 60.917808 |
569 | Jean Jacques Vlody | 1967-08-19 | 17674 days | 48.421918 |
570 | Michel Voisin | 1944-10-06 | 26026 days | 71.304110 |
571 | Jean-Luc Warsmann | 1965-10-22 | 18340 days | 50.246575 |
572 | Laurent Wauquiez | 1975-04-12 | 14881 days | 40.769863 |
573 | Éric Woerth | 1956-01-29 | 21894 days | 59.983562 |
574 | Paola Zanetti | 1976-09-01 | 14373 days | 39.378082 |
575 | Marie-Jo Zimmermann | 1951-04-29 | 23630 days | 64.739726 |
576 | Michel Zumkeller | 1966-01-21 | 18249 days | 49.997260 |
577 rows × 4 columns
df.sort_values(by='Âge (années)')
Noms | Dates de naissance | Âge | Âge (années) | |
---|---|---|---|---|
395 | Marion Maréchal-Le Pen | 1989-12-10 | 9525 days | 26.095890 |
359 | Marie Le Vern | 1983-01-11 | 12050 days | 33.013699 |
160 | Gérald Darmanin | 1982-10-11 | 12142 days | 33.265753 |
393 | Laurent Marcangeli | 1980-12-10 | 12812 days | 35.101370 |
105 | Fanélie Carrey-Conte | 1980-05-16 | 13020 days | 35.671233 |
0 | Damien Abad | 1980-04-05 | 13061 days | 35.783562 |
139 | Romain Colas | 1979-11-22 | 13196 days | 36.153425 |
195 | Virginie Duby-Muller | 1979-08-16 | 13294 days | 36.421918 |
290 | Razzy Hammadi | 1979-02-22 | 13469 days | 36.901370 |
176 | Sébastien Denaja | 1979-01-14 | 13508 days | 37.008219 |
292 | Mathieu Hanotin | 1978-08-22 | 13653 days | 37.405479 |
207 | Olivier Dussopt | 1978-08-16 | 13659 days | 37.421918 |
22 | Julien Aubert | 1978-06-11 | 13725 days | 37.602740 |
157 | Seybah Dagoma | 1978-06-09 | 13727 days | 37.608219 |
273 | Laurent Grandguillaume | 1978-01-20 | 13867 days | 37.991781 |
453 | Sébastien Pietrasanta | 1977-08-07 | 14033 days | 38.446575 |
46 | Nicolas Bays | 1977-05-01 | 14131 days | 38.715068 |
488 | Thierry Robert | 1977-04-01 | 14161 days | 38.797260 |
339 | Guillaume Larrivé | 1977-01-24 | 14228 days | 38.980822 |
469 | Christophe Premat | 1976-12-07 | 14276 days | 39.112329 |
574 | Paola Zanetti | 1976-09-01 | 14373 days | 39.378082 |
372 | Arnaud Leroy | 1976-04-23 | 14504 days | 39.736986 |
499 | Gwendal Rouillard | 1976-04-20 | 14507 days | 39.745205 |
100 | Yann Capet | 1975-12-31 | 14618 days | 40.049315 |
487 | Eduardo Rihan Cypel | 1975-11-13 | 14666 days | 40.180822 |
424 | Yannick Moreau | 1975-08-04 | 14767 days | 40.457534 |
528 | Julie Sommaruga | 1975-08-01 | 14770 days | 40.465753 |
16 | Nathalie Appéré | 1975-07-08 | 14794 days | 40.531507 |
461 | Barbara Pompili | 1975-06-13 | 14819 days | 40.600000 |
505 | Maina Sage | 1975-05-10 | 14853 days | 40.693151 |
... | ... | ... | ... | ... |
119 | Gérard Charasse | 1944-03-26 | 26220 days | 71.835616 |
24 | Danielle Auroi | 1944-02-29 | 26246 days | 71.906849 |
204 | Jean-Paul Dupré | 1944-02-05 | 26270 days | 71.972603 |
328 | Conchita Lacuey | 1943-09-30 | 26398 days | 72.323288 |
310 | Serge Janquin | 1943-08-05 | 26454 days | 72.476712 |
196 | Jean-Pierre Dufau | 1943-07-05 | 26485 days | 72.561644 |
535 | Suzanne Tallard | 1943-06-19 | 26501 days | 72.605479 |
232 | Michel Françaix | 1943-05-28 | 26523 days | 72.665753 |
521 | Roger-Gérard Schwartzenberg | 1943-04-17 | 26564 days | 72.778082 |
455 | Michel Piron | 1943-03-15 | 26597 days | 72.868493 |
69 | Jacques Bompard | 1943-02-24 | 26616 days | 72.920548 |
514 | Odile Saugues | 1943-01-26 | 26645 days | 73.000000 |
25 | Pierre Aylagas | 1942-07-24 | 26831 days | 73.509589 |
257 | Georges Ginesta | 1942-07-08 | 26847 days | 73.553425 |
120 | Gaby Charroux | 1942-06-25 | 26860 days | 73.589041 |
186 | Jean-Pierre Door | 1942-04-01 | 26945 days | 73.821918 |
200 | William Dumas | 1942-01-23 | 27013 days | 74.008219 |
382 | François Loncle | 1941-10-21 | 27107 days | 74.265753 |
237 | Yves Fromion | 1941-09-15 | 27143 days | 74.364384 |
188 | René Dosière | 1941-08-03 | 27186 days | 74.482192 |
283 | Jean-Claude Guibal | 1941-01-13 | 27388 days | 75.035616 |
512 | André Santini | 1940-10-20 | 27473 days | 75.268493 |
321 | Jacques Kossowski | 1940-10-11 | 27482 days | 75.293151 |
270 | Marc Goua | 1940-03-03 | 27704 days | 75.901370 |
152 | Jean-Michel Couve | 1940-01-03 | 27764 days | 76.065753 |
411 | Jean-Claude Mathis | 1939-08-15 | 27905 days | 76.452055 |
87 | Bernard Brochand | 1938-06-05 | 28341 days | 77.646575 |
167 | Lucien Degauchy | 1937-06-11 | 28700 days | 78.630137 |
397 | Alfred Marie-Jeanne | 1936-11-15 | 28908 days | 79.200000 |
518 | François Scellier | 1936-05-07 | 29100 days | 79.726027 |
577 rows × 4 columns
On peut maintenant faire un histogramme des âges des députés :
%matplotlib inline
df['Âge (années)'].hist(bins=25)
plt.xlabel('âge')
plt.ylabel('nombre de députés')
<matplotlib.text.Text at 0x8a0e208>
Les statistiques que l'on peut calculer sont :
df['Âge (années)'].describe()
count 577.000000 mean 57.998637 std 9.988493 min 26.095890 25% 50.967123 50% 58.627397 75% 66.167123 max 79.726027 Name: Âge (années), dtype: float64
Comparaison avec le restant de la France¶
On trouve sur le site de l'INSEE un lien vers des statistiques sur l'âge de la population française en 2014.
df_pop = pd.read_excel('http://www.insee.fr/fr/ppp/bases-de-donnees/donnees-detaillees/bilan-demo/fichiers-xls/pop-1janvier-fm.xls',
skiprows=7,
header=1,
skipfooter=3)
df_pop
Année de naissance | Âge révolu | Nombre d'hommes | Nombre de femmes | Ensemble | |
---|---|---|---|---|---|
0 | 2014 | 0 | 387051 | 370260 | 757311 |
1 | 2013 | 1 | 387916 | 370500 | 758416 |
2 | 2012 | 2 | 394045 | 376249 | 770294 |
3 | 2011 | 3 | 394471 | 380218 | 774689 |
4 | 2010 | 4 | 407929 | 388696 | 796625 |
5 | 2009 | 5 | 405344 | 386586 | 791930 |
6 | 2008 | 6 | 407169 | 389524 | 796693 |
7 | 2007 | 7 | 405278 | 386970 | 792248 |
8 | 2006 | 8 | 412787 | 394049 | 806836 |
9 | 2005 | 9 | 404066 | 386640 | 790706 |
10 | 2004 | 10 | 402204 | 384465 | 786669 |
11 | 2003 | 11 | 400289 | 383260 | 783549 |
12 | 2002 | 12 | 403767 | 384236 | 788003 |
13 | 2001 | 13 | 408339 | 390995 | 799334 |
14 | 2000 | 14 | 417354 | 399436 | 816790 |
15 | 1999 | 15 | 400215 | 381184 | 781399 |
16 | 1998 | 16 | 400967 | 381289 | 782256 |
17 | 1997 | 17 | 395245 | 375323 | 770568 |
18 | 1996 | 18 | 396418 | 378272 | 774690 |
19 | 1995 | 19 | 385463 | 369869 | 755332 |
20 | 1994 | 20 | 367552 | 354457 | 722009 |
21 | 1993 | 21 | 358340 | 348128 | 706468 |
22 | 1992 | 22 | 372304 | 364953 | 737257 |
23 | 1991 | 23 | 372851 | 371150 | 744001 |
24 | 1990 | 24 | 379819 | 379366 | 759185 |
25 | 1989 | 25 | 377886 | 381935 | 759821 |
26 | 1988 | 26 | 379413 | 387338 | 766751 |
27 | 1987 | 27 | 380395 | 386549 | 766944 |
28 | 1986 | 28 | 384267 | 394913 | 779180 |
29 | 1985 | 29 | 383224 | 393452 | 776676 |
... | ... | ... | ... | ... | ... |
76 | 1938 | 76 | 195545 | 249332 | 444877 |
77 | 1937 | 77 | 186798 | 244132 | 430930 |
78 | 1936 | 78 | 181433 | 243847 | 425280 |
79 | 1935 | 79 | 171016 | 239291 | 410307 |
80 | 1934 | 80 | 166256 | 240306 | 406562 |
81 | 1933 | 81 | 151458 | 228378 | 379836 |
82 | 1932 | 82 | 148284 | 231031 | 379315 |
83 | 1931 | 83 | 135213 | 217397 | 352610 |
84 | 1930 | 84 | 125189 | 211078 | 336267 |
85 | 1929 | 85 | 107349 | 189472 | 296821 |
86 | 1928 | 86 | 94475 | 176127 | 270602 |
87 | 1927 | 87 | 81730 | 161042 | 242772 |
88 | 1926 | 88 | 70129 | 147781 | 217910 |
89 | 1925 | 89 | 59217 | 133111 | 192328 |
90 | 1924 | 90 | 48109 | 114793 | 162902 |
91 | 1923 | 91 | 38600 | 99268 | 137868 |
92 | 1922 | 92 | 31205 | 85541 | 116746 |
93 | 1921 | 93 | 24783 | 71112 | 95895 |
94 | 1920 | 94 | 18943 | 58457 | 77400 |
95 | 1919 | 95 | 8352 | 27135 | 35487 |
96 | 1918 | 96 | 5013 | 18468 | 23481 |
97 | 1917 | 97 | 2925 | 12820 | 15745 |
98 | 1916 | 98 | 1808 | 8636 | 10444 |
99 | 1915 | 99 | 1574 | 6943 | 8517 |
100 | 1914 | 100 | 1601 | 7777 | 9378 |
101 | 1913 | 101 | 847 | 4804 | 5651 |
102 | 1912 | 102 | 546 | 3037 | 3583 |
103 | 1911 | 103 | 362 | 1943 | 2305 |
104 | 1910 | 104 | 230 | 1267 | 1497 |
105 | 1909 ou avant | 105 ou plus | 18 | 1086 | 1104 |
106 rows × 5 columns
Le nombre de français par classe d'âge est le suivant :
df_pop.plot('Âge révolu', 'Ensemble')
plt.ylabel('population')
<matplotlib.text.Text at 0x9273f28>
Afin de pouvoir comparer la distribution des députés avec celle des français normaux, on va utiliser une méthode d'estimation par noyaux, analogue à un histogramme. Le code vient d'ici :
from scipy.stats import gaussian_kde
def kde_scipy(x, x_grid, bandwidth=0.2, **kwargs):
"""Kernel Density Estimation with Scipy"""
# Note that scipy weights its bandwidth by the covariance of the
# input data. To make the results comparable to the other methods,
# we divide the bandwidth by the sample standard deviation here.
kde = gaussian_kde(x, bw_method=bandwidth / x.std(ddof=1), **kwargs)
return kde.evaluate(x_grid)
Afin d'utiliser ce code, il nous faut transformer les données cumulées disponibles pour la population française en données ponctuelles (une série d'âges). Nous faisons ceci en prenant une certaine classe d'âge proportionnellement à un facteur de décimation.
import numpy as np
decimation_factor = 1000
french_pop_ages = []
for ind, value in enumerate(df_pop['Ensemble']):
french_pop_ages += [ind for _ in range(int(value / decimation_factor))]
len(french_pop_ages)
64152
On va faire les graphes sur des âges entre 0 et 100 ans.
age_grid = np.linspace(0, 100)
pop_kde = kde_scipy(np.array(french_pop_ages), age_grid, bandwidth=0.4)
dep_kde = kde_scipy(df['Âge (années)'].values, age_grid, bandwidth=0.4)
plt.plot(age_grid,
pop_kde,
label='population française')
plt.plot(age_grid,
dep_kde,
label='députés')
plt.legend(loc='upper left')
plt.xlabel('âge')
plt.ylabel('densité')
<matplotlib.text.Text at 0xaaf2710>
Ces densités étant difficiles à visualiser, on leur préfère généralement la visualisation de la fréquence cumulée, qui permet de les comparer facilement en terme de distribution :
import statsmodels.api as sm # recommended import according to the docs
ecdf_mp = sm.distributions.ECDF(df['Âge (années)'].values)
ecdf_french_pop = sm.distributions.ECDF(french_pop_ages)
y_mp = ecdf_mp(age_grid)
y_french_pop = ecdf_french_pop(age_grid)
plt.step(age_grid, y_mp, label='députés')
plt.step(age_grid, y_french_pop, label='population française')
plt.legend(loc='upper left')
<matplotlib.legend.Legend at 0xb672be0>
Comme on le voit, les répartitions sont très différents : plus de députés vieux par rapport à la population, mais également moins de députés "très vieux" par rapport à cette même population.
On peut également calculer un "taux de représentation" à l'aide de ces valeurs : c'est-à-dire un ratio entre la densité de députés pour un certain âge et le nombre de députés de cet âge-là.
plt.plot(age_grid, dep_kde / pop_kde, label='représentation actuelle des députés')
plt.plot(age_grid, np.ones_like(age_grid), label='représentation équi-âge')
plt.legend(loc='upper left', fontsize=10)
plt.xlabel('âge')
plt.ylabel('densité')
<matplotlib.text.Text at 0xb6a8dd8>
On peut noter que les âges entre 50 et 70 sont surreprésentés, alors que les âges en dehors sont sous-représentés par la distribution actuelle des députés.
Si l'on compare les quantiles entre la population française et les députés, on obtient :
pd.Series(french_pop_ages).describe()
count 64152.000000 mean 40.538175 std 24.030176 min 0.000000 25% 20.000000 50% 41.000000 75% 59.000000 max 105.000000 dtype: float64
df['Âge (années)'].describe()
count 577.000000 mean 57.998637 std 9.988493 min 26.095890 25% 50.967123 50% 58.627397 75% 66.167123 max 79.726027 Name: Âge (années), dtype: float64
On peut résumer ces données par le tableau suivant :
population | âge moyen | âge minimal | âge maximal | 25% de la population a moins de | 50% a moins de | 50% a plus de | 25% a plus de |
---|---|---|---|---|---|---|---|
les Français | 41 ans | 0 ans | 105 ans | 20 ans | 41 ans | 41 ans | 59 ans |
les députés français | 58 ans | 26 ans | 79 ans | 51 ans | 59 ans | 59 ans | 66 ans |
Conclusions¶
Dans ce billet, nous avons étudié les données de l'âge disponibles pour les députés français. La distribution obtenue permet de retrouver les histogrammes officiels et fournir de plus amples détails, comme par exemple la moyenne d'âge d'environ 60 ans des députés français.
Dans la deuxième partie du billet, nous avons comparé la distribution des âges des députés avec celles du français moyen. On constate que les deux distributions sont assez différentes. En particulier, les jeunes (jusqu'à 40 ans environ) ne sont pas représentés dans les âges des députés, alors que les âges entre 50 et 70 sont surreprésentés (facteur entre 2 et 5).
De nombreuses questions peuvent être posées au vu de ces résultats : quels sont les avantages et les inconvénients de cette distribution inégale ? Peut-on supposer qu'elle induit un biais significatif sur les décisions et les manières de penser par rapport à la population ? Existe-t-il des points sur lesquels l'expérience des députés diffère sensiblement de celle de la population (internet est un point qui me vient à l'esprit) ? Cette répartition par rapport à la population se retrouve-t-elle dans d'autres pays comparables à la France ? Comment a-t-elle évolué dans le temps (il y a 50 ans par exemple) ?