.. _electioncarteelectoralerst: ======================================== Elections et cartes électorales - énoncé ======================================== .. only:: html **Links:** :download:`notebook `, :downloadlink:`html `, :download:`PDF `, :download:`python `, :downloadlink:`slides `, :githublink:`GitHub|_doc/notebooks/sessions/election_carte_electorale.ipynb|*` 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 `__. .. code:: ipython3 %matplotlib inline .. code:: ipython3 from jyquickhelper import add_notebook_menu add_notebook_menu() .. contents:: :local: 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 `__ - `Countours des circonscriptions des législatives `__ - `Localisation des buraux de votes `__ - `Localisation des villes `__ - `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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 from actuariat_python.data import elections_legislatives_bureau_vote tour = elections_legislatives_bureau_vote(source='xd') .. code:: ipython3 tour["T2"].sort_values(["Code département", "N° de circonscription Lg"]).head() .. raw:: html
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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 from actuariat_python.data import elections_legislatives_circonscription_geo geo = elections_legislatives_circonscription_geo() .. code:: ipython3 geo.sort_values(["department", "code_circonscription"]).head() .. raw:: html
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
.. code:: ipython3 c = list(geo.sort_values(["department", "code_circonscription"])["communes"])[0].split("-") c.sort() c[:5] .. parsed-literal:: ['01016', '01024', '01029', '01038', '01040'] .. code:: ipython3 list(geo.sort_values(["department", "code_circonscription"])["kml_shape"])[:1] .. parsed-literal:: ['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'] 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 : .. code:: ipython3 from actuariat_python.data import elections_vote_places_geo bureau_geo = elections_vote_places_geo() .. code:: ipython3 bureau_geo.head() .. raw:: html
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 `__. .. code:: ipython3 from actuariat_python.data import elections_vote_place_address bureau = elections_vote_place_address(hide_warning=True) .. code:: ipython3 bureau.head() .. raw:: html
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") .. code:: ipython3 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) .. parsed-literal:: 1 .. code:: ipython3 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.") .. parsed-literal:: Les données ont déjà été geocodées. On regarde les valeurs manquantes. .. code:: ipython3 import missingno missingno.matrix(bureau_geo, figsize=(12, 6)); .. image:: election_carte_electorale_22_0.png On pourra finalement récupérer la base des géocodes comme ceci : .. code:: ipython3 from actuariat_python.data import elections_vote_places_geo places = elections_vote_places_geo() places.head() .. raw:: html
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 ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython3 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() .. raw:: html
city zip n
0 abbeville 80100 17
1 ableiges 95450 2
2 ablis 78660 2
3 ablon 94480 4
4 achères 78260 12
.. code:: ipython3 from actuariat_python.data import villes_geo villes_geo = villes_geo(as_df=True) villes_geo.head() .. raw:: html
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-...
.. code:: ipython3 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) .. parsed-literal:: 0 .. code:: ipython3 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") .. code:: ipython3 villes_geo.head() .. raw:: html
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. .. code:: ipython3 villes_geo.to_csv("villes_geo.txt", sep="\t", index=False, encoding="utf-8") .. code:: ipython3 villes_geo.shape .. parsed-literal:: (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. .. code:: ipython3 from pyensae.datasource import download_data shp_vote = download_data("base_cartelec_2007_2010.zip") shp_vote .. parsed-literal:: ['fond0710.dbf', 'fond0710.shp', 'fond0710.shx'] .. code:: ipython3 # 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) .. parsed-literal:: (50578, 50578) .. code:: ipython3 {k[0]: v for k, v in zip(r.fields, records[0])} .. parsed-literal:: {'DeletionFlag': '01001', 'BUREAU': '01001', 'CODE': "L'Abergement-Clmenciat", 'NOM': '012', 'CODEARRT': '01', 'CODEDEP': '82', 'CODEREG': '10', 'CODECANT': 'CHATILLON-SUR-CHALARONNE', 'CANTON': '04'} .. code:: ipython3 shapes[0].points .. parsed-literal:: [(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.