Elections et cartes électorales - énoncé#
Links: notebook
, html, PDF
, python
, slides, GitHub
D’après wikipédia, le Gerrymandering est un terme politique nord-américain pour désigner le découpage des circonscriptions électorales ayant pour objectif de donner l’avantage à un parti, un candidat, ou un groupe donné. Et c’est ce que nous allons faire dans cette séance. C’est un problème tout-à-fait d’actualité : Primaire de la droite : 10 228 bureaux de vote stratégiquement répartis.
%matplotlib inline
from jyquickhelper import add_notebook_menu
add_notebook_menu()
Données#
Les données sont de plusieurs types et sont regroupées en trois fichiers :
Résultat des élections législatives françaises de 2012 au niveau bureau de vote
Localisation des bureaux de vote avec Cartelec, cette base requiert la conversion des coordonnées (lire ce notebook)
Il est conseillé de télécharger directement ces données. Les paragraphes suivants expliquent comment ceux-ci ont été récupérés ou construit. Il n’est pas immédiat d’obtenir la localisation des bureaux de vote. Celle-ci n’est d’ailleurs pas complète.
Résultats des élections législatives#
from actuariat_python.data import elections_legislatives_bureau_vote
tour = elections_legislatives_bureau_vote(source='xd')
tour["T2"].sort_values(["Code département", "N° de circonscription Lg"]).head()
N° tour | Code département | Code de la commune | Nom de la commune | N° de circonscription Lg | N° de canton | N° de bureau de vote | Inscrits | Votants | Exprimés | N° de dépôt du candidat | Nom du candidat | Prénom du candidat | Code nuance du candidat | Nombre de voix du candidat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
3858 | 2 | 01 | 16 | Arbigny | 1 | 26 | 0001 | 309 | 146 | 144 | 32 | BRETON | Xavier | UMP | 87 |
3859 | 2 | 01 | 16 | Arbigny | 1 | 26 | 0001 | 309 | 146 | 144 | 33 | DEBAT | Jean-François | SOC | 57 |
3871 | 2 | 01 | 24 | Attignat | 1 | 21 | 0001 | 746 | 425 | 411 | 32 | BRETON | Xavier | UMP | 233 |
3872 | 2 | 01 | 24 | Attignat | 1 | 21 | 0001 | 746 | 425 | 411 | 33 | DEBAT | Jean-François | SOC | 178 |
3873 | 2 | 01 | 24 | Attignat | 1 | 21 | 0002 | 1311 | 801 | 792 | 32 | BRETON | Xavier | UMP | 446 |
Géolocalisation des circonscription#
from actuariat_python.data import elections_legislatives_circonscription_geo
geo = elections_legislatives_circonscription_geo()
geo.sort_values(["department", "code_circonscription"]).head()
code_circonscription | department | numero | communes | kml_shape | simple_form | |
---|---|---|---|---|---|---|
11 | 01001 | 01 | 1 | 01053-01072-01106-01150-01177-01184-01195-0124... | <Polygon><outerBoundaryIs><LinearRing><coordin... | False |
12 | 01002 | 01 | 2 | 01008-01047-01099-01202-01213-01224-01366-0138... | <Polygon><outerBoundaryIs><LinearRing><coordin... | True |
13 | 01003 | 01 | 3 | 01033-01044-01081-01091-01174-01189-01215-0125... | <Polygon><outerBoundaryIs><LinearRing><coordin... | True |
14 | 01004 | 01 | 4 | 01023-01025-01026-01144-01159-01231-01320-0133... | <Polygon><outerBoundaryIs><LinearRing><coordin... | False |
15 | 01005 | 01 | 5 | 01002-01004-01007-01041-01089-01149-01345-0137... | <Polygon><outerBoundaryIs><LinearRing><coordin... | True |
c = list(geo.sort_values(["department", "code_circonscription"])["communes"])[0].split("-")
c.sort()
c[:5]
['01016', '01024', '01029', '01038', '01040']
list(geo.sort_values(["department", "code_circonscription"])["kml_shape"])[:1]
['<Polygon><outerBoundaryIs><LinearRing><coordinates>5.294455999999968,46.193934 5.279780999999957,46.201967 5.2820520000000215,46.211633 5.258239693115229,46.21151582325097 5.2581992646485105,46.2083773977195 5.246123010742167,46.20489445115752 5.245254158935495,46.20280868033653 5.240432576904368,46.199348983660926 5.23716710070812,46.19529946757567 5.229953412841837,46.195408656674 5.226543291137659,46.20120799400516 5.214944595275824,46.20383315680887 5.210212354492228,46.20764618417071 5.205343111083948,46.20376238484074 5.206997000000001,46.191323 5.19510600000001,46.18786 5.187468999999965,46.190827 5.170859999999948,46.203126 5.162395999999944,46.199387 5.15610700000002,46.189904 5.142047000000048,46.192223 5.123888999999963,46.207671 5.111119000000031,46.203891 5.097426000000041,46.206108 5.089811999999938,46.195268 5.068122000000017,46.208969 5.058287999999948,46.22074 5.05758000000003,46.224014 5.065923999999995,46.231556 5.093984999999975,46.232529 5.095609999999965,46.235634 5.0915129999999635,46.250725 5.067033000000038,46.253046 5.047692999999981,46.250829 5.034760000000006,46.25606 5.027125000000069,46.264835 5.022599000000014,46.270653 5.025237999999945,46.27702 5.0366390000000365,46.286329 5.03779499999996,46.296424 5.011349999999993,46.304235 5.008049000000028,46.313805 5.025981999999999,46.319235 5.0349670000000515,46.332568 5.018135000000029,46.347235 5.004950000000008,46.356205 4.98293000000001,46.359589 4.976036000000022,46.352773 4.958721999999966,46.358602 4.947116999999935,46.369538 4.932804000000033,46.367719 4.930385999999999,46.372948 4.8962810000000445,46.384617 4.897916000000009,46.398713 4.888207999999963,46.402982 4.888682000000017,46.417448 4.891531999999984,46.427525 4.891796999999997,46.438555 4.892886999999973,46.442572 4.894444000000021,46.445374 4.914447999999993,46.462163 4.914530000000013,46.480113 4.914522000000034,46.481374 4.91528500000004,46.488234 4.926993000000039,46.499251 4.92961600000001,46.50408 4.933591999999976,46.512453 4.935599000000025,46.514229 4.947349000000031,46.513723 4.946786999999972,46.507113 4.953710999999998,46.504112 4.962766999999985,46.506749 4.97047299999997,46.51421 4.980216000000041,46.515203 5.00636899999995,46.509703 5.010593999999969,46.51107 5.01423299999999,46.50045 5.05475100000001,46.484281 5.07144900000003,46.485817 5.09522800000002,46.497508 5.112967000000026,46.493137 5.134029999999939,46.501447 5.136367000000064,46.507808 5.14104199999997,46.508585 5.164164000000028,46.504745 5.165839000000005,46.517675 5.177177000000029,46.511434 5.201221000000032,46.507821 5.203565000000026,46.504803 5.200342999999975,46.502568 5.2094809999999825,46.492105 5.206684999999993,46.486177 5.213031000000001,46.481541 5.215063999999984,46.468359 5.224697999999989,46.468352 5.236259000000018,46.457785 5.248004000000037,46.459374 5.25627099999997,46.451879 5.273677000000021,46.448588 5.310561000000007,46.446775 5.32172700000001,46.428955 5.309671999999978,46.423615 5.307822999999985,46.417157 5.312332999999967,46.416021 5.298806000000013,46.415528 5.299958999999944,46.412289 5.309146000000055,46.410257 5.334092000000055,46.399723 5.3481160000000045,46.399417 5.367105000000038,46.388692 5.377881000000002,46.382324 5.365074999999933,46.373077 5.363472000000002,46.37002 5.375389000000041,46.364844 5.375761000000011,46.358431 5.373462000000018,46.352236 5.383367000000021,46.344877 5.400995999999964,46.3393 5.401686000000041,46.332799 5.4040410000000065,46.315668 5.404053999999974,46.314767 5.410239999999931,46.309105 5.416931999999974,46.342873 5.423534000000018,46.347732 5.437145999999984,46.315127 5.454476,46.317998 5.465180000000032,46.323542 5.475304999999935,46.315385 5.466531000000032,46.293242 5.459000999999944,46.290531 5.457672000000002,46.276848 5.471308000000022,46.26721 5.475362000000018,46.265221 5.499594999999999,46.268205 5.502609000000007,46.270211 5.511369999999943,46.26436 5.51186400000006,46.257853 5.50287800000001,46.245411 5.494106999999985,46.242144 5.46908099999996,46.212365 5.464535999999953,46.210869 5.460665000000063,46.213097 5.454173999999966,46.216805 5.450403000000051,46.215184 5.451294999999959,46.208723 5.46410000000003,46.204347 5.466176000000019,46.191076 5.46054300000003,46.18609 5.454643000000033,46.181506 5.461671000000024,46.177417 5.446460000000002,46.152159 5.430090999999948,46.139357 5.43449899999996,46.125677 5.431788999999981,46.122699 5.421692000000007,46.1226 5.4127750000000106,46.124015 5.4093820000000505,46.118726 5.405267999999978,46.118273 5.39823899999999,46.098224 5.401965000000018,46.08837 5.399834999999939,46.085253 5.386187000000064,46.081649 5.383172000000059,46.075109 5.376326000000063,46.059 5.378545000000031,46.053161 5.374886999999944,46.048147 5.367076999999995,46.044365 5.352794000000017,46.04495 5.351631999999995,46.035335 5.339034999999967,46.039632 5.334828000000016,46.038052 5.324753999999984,46.04179 5.3118409999999585,46.025424 5.310352999999964,46.011554 5.305977999999982,46.001878 5.299062000000049,45.99691 5.29071399999998,45.993548 5.286965000000009,45.987002 5.275816999999961,45.998064 5.27370099999996,46.015052 5.25696199999993,46.021486 5.263284999999996,46.034044 5.261357999999973,46.03719 5.2586579999999685,46.043559 5.254462999999987,46.048534 5.2451469999999745,46.041863 5.2245040000000245,46.034586 5.210403000000042,46.035808 5.2019599999999855,46.050404 5.187967999999955,46.052846 5.176518999999985,46.064011 5.178200000000061,46.081266 5.1829139999999825,46.080995 5.1864799999999605,46.087331 5.194266999999968,46.09137 5.235487000000035,46.100474 5.239908000000014,46.112963 5.235547999999994,46.129318 5.238933999999972,46.135783 5.2344419999999445,46.145429 5.246138999999971,46.14909 5.248986999999943,46.154682 5.2586180000000695,46.156266 5.277324000000021,46.151893 5.29046500000004,46.154917 5.307536000000027,46.153346 5.312217000000032,46.16107 5.3077429999999595,46.166278 5.310639000000037,46.168562 5.304237000000057,46.172322 5.300876000000017,46.18093 5.294455999999968,46.193934</coordinates></LinearRing></outerBoundaryIs></Polygon>']
Géolocation des bureaux de vote#
Ces données sont importantes afin de pouvoir associer chaque bureau à une circonscription. C’est la donnée la plus compliquée à obtenir car elle nécessite de combiner plusieurs techniques et sources de données pour obtenir une table propre. Le fichier final peut être obtenu comme suit :
from actuariat_python.data import elections_vote_places_geo
bureau_geo = elections_vote_places_geo()
bureau_geo.head()
address | city | n | place | zip | full_address | latitude | longitude | geo_address | |
---|---|---|---|---|---|---|---|---|---|
0 | cours verdun | bourg | 1 | salle des fêtes | 1000 | cours verdun 01000 bourg | 46.206605 | 5.228364 | Cours de Verdun, Le Peloux, Les Vennes, Bourg-... |
1 | cours verdun | bourg | 2 | salle des fêtes | 1000 | cours verdun 01000 bourg | 46.206605 | 5.228364 | Cours de Verdun, Le Peloux, Les Vennes, Bourg-... |
2 | rue antoine de saint exupéry | bourg-en-bresse | 3 | groupe scolaire saint-exupéry - salle de jeux,... | 1000 | rue antoine de saint exupéry 01000 bourg-en-br... | 46.210030 | 5.233330 | Rue Antoine de Saint-Exupéry, Bourg-en-Bresse,... |
3 | 11 avenue de l’égalité | bourg-en-bresse | 4 | charles perrault - école primaire | 1000 | 11 avenue de l’égalité 01000 bourg-en-bresse | 46.214848 | 5.231941 | 11, Avenue de l'Égalité, Saint-Georges, La Gla... |
4 | 11 avenue de l’égalité | bourg-en-bresse | 5 | charles perrault - école primaire | 1000 | 11 avenue de l’égalité 01000 bourg-en-bresse | 46.214848 | 5.231941 | 11, Avenue de l'Égalité, Saint-Georges, La Gla... |
Ce qui suit explique la façon dont j’ai constuit cette table.
Les bureaux sont assez stables d’une élection à l’autre et cela ne devrait pas trop poser de problèmes si on mélange les données. En revanche, ces données sont assez difficiles à obtenir. open.data.fr propose ces données mais il faut récupérer chaque ville ou chaque région séparément sans garantie de réussir à couvrir tout le territoire. Le site NosDonnes.fr recense bien toutes ces informations mais il n’est pas possible - au moment où j’écris ces lignes - de récupérer ces données sous la forme d’un fichier plat. De plus, certaines régions ne sont disponibles que sous forme de scan d’impressions papier. C’est en lisant l’article Comment redécoupe-t-on la carte électorale? que je suis tombé finalement sur la base constituée pour rédiger l’article Etude sur le redécoupage électoral : évaluer le poids politique de la réforme. Les données sont accessibles sur RegardsCitoyens.fr et en cherchant bien, on trouve le répertoire redécoupage et le fichier 2014041401_resultats.csv.zip. Cependant, la géolocalisation des bureaux n’est pas souvent renseignée. On peut se contenter des fichiers obtenus pour quelques zones seulement : Paris, Gironde, Montpellier, Marseille, Saint-Malo, Nogent-Sur-Marne, Haut-de-Seine, Toulouse, Grand-Poitiers, Calvados. Cette approche risque d’être fastidieuse dans la mesure où les formats pour chaque ville ont de grande chance d’être différent.
La meilleure option est peut-être de scrapper le site bureaudevote.fr - qui fonctionne bien quand il n’est pas en maintenance - en espérant que les numéros des bureaux de votes correspondent. Le site propose seulement les adresses. Il faudra utiliser une API de geocoding. Quelque soit la source, on supposera alors que tous les bureaux de vote associés au même emplacement feront nécessairement partie de la même circonscription. Bref, l’open data passe aussi par une certaine standardisation !
Certains départements ne sont pas renseignés, la Drôme par exemple. Il faut aller sur d’autres sites comme linternaute.com, accéder à la carte, mais il faudra un peu plus de temps pour récupérer toutes ces informations. Le code proposé ci-dessus récupère les coordonnées des bureaux de vote. Cela prend plusieurs heures et il faut relancer le processus quand il s’arrête pour une erreur de réseau. Il est conseillé de télécharger le résultat. Le géocodeur d’OpenStreetMap n’est pas de très bonne qualité sur la France. Il ne retourne rien dans plus d’un tiers des cas. On peut compléter avec l’API de Bing Maps.
from actuariat_python.data import elections_vote_place_address
bureau = elections_vote_place_address(hide_warning=True)
bureau.head()
address | city | n | place | zip | |
---|---|---|---|---|---|
0 | cours verdun | bourg | 1 | salle des fêtes | 01000 |
1 | cours verdun | bourg | 2 | salle des fêtes | 01000 |
2 | rue antoine de saint exupéry | bourg-en-bresse | 3 | groupe scolaire saint-exupéry - salle de jeux,... | 01000 |
3 | 11 avenue de l’égalité | bourg-en-bresse | 4 | charles perrault - école primaire | 01000 |
4 | 11 avenue de l’égalité | bourg-en-bresse | 5 | charles perrault - école primaire | 01000 |
On récupère une clé pour utiliser l’API de Bing Maps avec le module keyring. Pour stocker son mot de passe sur la machine, il suffit d’écrire :
- ::
import keyring keyring.get_password(« bing », « actuariat_python,key »)
import keyring, os
bing_key = keyring.get_password("bing", "actuariat_python,key")
coders = ["Nominatim"]
if bing_key:
# si la clé a été trouvée
coders.append(("bing", bing_key))
len(coders)
1
import os
if not os.path.exists("bureauxvotegeo.zip"):
from actuariat_python.data import geocode
from pyquickhelper.loghelper import fLOG
fLOG(OutputPrint=True)
bureau_geo = geocode(bureau, fLOG=fLOG, index=False, encoding="utf-8",
exc=False, save_every="bureau.dump.txt", sep="\t", every=100,
coders=coders)
else:
print("Les données ont déjà été geocodées.")
Les données ont déjà été geocodées.
On regarde les valeurs manquantes.
import missingno
missingno.matrix(bureau_geo, figsize=(12, 6));
On pourra finalement récupérer la base des géocodes comme ceci :
from actuariat_python.data import elections_vote_places_geo
places = elections_vote_places_geo()
places.head()
address | city | n | place | zip | full_address | latitude | longitude | geo_address | |
---|---|---|---|---|---|---|---|---|---|
0 | cours verdun | bourg | 1 | salle des fêtes | 1000 | cours verdun 01000 bourg | 46.206605 | 5.228364 | Cours de Verdun, Le Peloux, Les Vennes, Bourg-... |
1 | cours verdun | bourg | 2 | salle des fêtes | 1000 | cours verdun 01000 bourg | 46.206605 | 5.228364 | Cours de Verdun, Le Peloux, Les Vennes, Bourg-... |
2 | rue antoine de saint exupéry | bourg-en-bresse | 3 | groupe scolaire saint-exupéry - salle de jeux,... | 1000 | rue antoine de saint exupéry 01000 bourg-en-br... | 46.210030 | 5.233330 | Rue Antoine de Saint-Exupéry, Bourg-en-Bresse,... |
3 | 11 avenue de l’égalité | bourg-en-bresse | 4 | charles perrault - école primaire | 1000 | 11 avenue de l’égalité 01000 bourg-en-bresse | 46.214848 | 5.231941 | 11, Avenue de l'Égalité, Saint-Georges, La Gla... |
4 | 11 avenue de l’égalité | bourg-en-bresse | 5 | charles perrault - école primaire | 1000 | 11 avenue de l’égalité 01000 bourg-en-bresse | 46.214848 | 5.231941 | 11, Avenue de l'Égalité, Saint-Georges, La Gla... |
Géolocalisation des villes#
from actuariat_python.data import elections_vote_places_geo
bureau_geo = elections_vote_places_geo()
villes_geo = bureau_geo[["city", "zip", "n"]].groupby(["city", "zip"], as_index=False).count()
villes_geo.head()
city | zip | n | |
---|---|---|---|
0 | abbeville | 80100 | 17 |
1 | ableiges | 95450 | 2 |
2 | ablis | 78660 | 2 |
3 | ablon | 94480 | 4 |
4 | achères | 78260 | 12 |
from actuariat_python.data import villes_geo
villes_geo = villes_geo(as_df=True)
villes_geo.head()
city | zip | n | full_address | latitude | longitude | geo_address | |
---|---|---|---|---|---|---|---|
0 | abbeville | 80100 | 17 | 80100 abbeville France | 30.206659 | -92.008957 | Nan Dr, Lafayette, LA 70503, United States |
1 | ableiges | 95450 | 2 | 95450 ableiges France | 49.090165 | 1.981233 | Ableiges, Pontoise, Val-d'Oise, Île-de-France,... |
2 | ablis | 78660 | 2 | 78660 ablis France | 48.517153 | 1.836876 | Ablis, Rambouillet, Yvelines, Île-de-France, 7... |
3 | ablon | 94480 | 4 | 94480 ablon France | 48.723640 | 2.414800 | Rue d'Ablon, Ablon-sur-Seine, IdF 94480, France |
4 | achères | 78260 | 12 | 78260 achères France | 48.960266 | 2.070165 | Achères, Saint-Germain-en-Laye, Yvelines, Île-... |
import keyring, os
bing_key = keyring.get_password("bing", "actuariat_python,key")
coders = []
if bing_key:
# si la clé a été trouvée
coders.append(("bing", bing_key))
len(coders)
0
import os
geocode = True
if geocode:
if os.path.exists("villes_geo.txt"):
import pandas
villes_geo = pandas.read_csv("villes_geo.txt", sep="\t", encoding="utf-8")
else:
from actuariat_python.data import geocode
from pyquickhelper.loghelper import fLOG
fLOG(OutputPrint=True)
villes_geo = geocode(villes_geo, fLOG=fLOG, index=False, encoding="utf-8",
exc=False, save_every="villes.dump.txt", sep="\t", every=100,
coders=coders, country="France")
villes_geo.head()
city | zip | n | full_address | latitude | longitude | geo_address | |
---|---|---|---|---|---|---|---|
0 | abbeville | 80100 | 17 | 80100 abbeville France | 50.109188 | 1.833270 | Abbeville, Nord-Pas-de-Calais-Picardie 80100, ... |
1 | ableiges | 95450 | 2 | 95450 ableiges France | 49.092121 | 1.982560 | Ableiges, IdF, France |
2 | ablis | 78660 | 2 | 78660 ablis France | 48.504829 | 1.860777 | Ablis, IdF 78660, France |
3 | ablon | 94480 | 4 | 94480 ablon France | 48.723869 | 2.419967 | Ablon-sur-Seine, IdF 94480, France |
4 | achères | 78260 | 12 | 78260 achères France | 48.967705 | 2.065866 | Achères, IdF 78260, France |
On conserve les données pour éviter de les reconstuire et faire appel à l’API Bing à nouveau.
villes_geo.to_csv("villes_geo.txt", sep="\t", index=False, encoding="utf-8")
villes_geo.shape
(584, 7)
Géolocation des bureaux de vote avec Cartélec#
Le site cartelec recense beaucoup plus de bureaux de vote mais pour les élections 2007. Ils ne devraient pas avoir changé beaucoup.
from pyensae.datasource import download_data
shp_vote = download_data("base_cartelec_2007_2010.zip")
shp_vote
['fond0710.dbf', 'fond0710.shp', 'fond0710.shx']
# La version 2.0.0.dev de pyshp est buggée. Il vaut mieux ne pas l'utiliser.
import shapefile
if "dev" in shapefile.__version__:
raise ImportError("Use a different version of pyshp not '{0}'".format(shapefile.__version__))
r = shapefile.Reader("fond0710.shp", encoding="utf8", encodingErrors="ignore")
shapes = r.shapes()
records = r.records()
len(shapes), len(records)
(50578, 50578)
{k[0]: v for k, v in zip(r.fields, records[0])}
{'DeletionFlag': '01001',
'BUREAU': '01001',
'CODE': "L'Abergement-Clmenciat",
'NOM': '012',
'CODEARRT': '01',
'CODEDEP': '82',
'CODEREG': '10',
'CODECANT': 'CHATILLON-SUR-CHALARONNE',
'CANTON': '04'}
shapes[0].points
[(846774.7025280485, 6563840.655779875),
(847430.4726776106, 6566444.631470905),
(848975.0615885032, 6566530.102978201),
(849532.5253064571, 6565971.4588501565),
(848969.0813380895, 6564398.911644492),
(850941.7401535356, 6563209.5425065085),
(849896.4212796891, 6562719.844144765),
(849632.2745031306, 6561522.415193593),
(849891.0276243397, 6560738.406460746),
(848732.0257644501, 6559575.068823495),
(848585.9032087281, 6560169.582690463),
(847664.0345600601, 6560616.395794825),
(847793.2580021, 6562243.125831007),
(846774.7025280485, 6563840.655779875)]
Exercice 1#
Etablir un plan d’action
Détailler la mise en oeuvre de ce plan à partir des données
Répartir les tâches sur plusieurs équipes
Exercice 2#
Mettre en oeuvre le plan d’action.