.. _seance6graphesenoncerst: ================ Graphes - énoncé ================ .. only:: html **Links:** :download:`notebook `, :downloadlink:`html `, :download:`PDF `, :download:`python `, :downloadlink:`slides `, :githublink:`GitHub|_doc/notebooks/sessions/seance6_graphes_enonce.ipynb|*` Ce notebook introduit `matplotlib `__ et d’autres modules Python qui permettent de tracer des graphes et bâtis sur la même logique que matplotlib. Pour avoir des graphiques inclus dans le notebook, il faut ajouter cette ligne et l’exécuter en premier. .. code:: ipython3 %matplotlib inline On change le style pour un style plus moderne, celui de `ggplot `__ : .. code:: ipython3 import matplotlib.pyplot as plt plt.style.use('ggplot') .. code:: ipython3 from jyquickhelper import add_notebook_menu add_notebook_menu() .. contents:: :local: Données ------- Pour tous les exemples qui suivent, on utilise les résultat `élection présidentielle de 2012 `__. Si vous n’avez pas le module `actuariat_python `__, il vous suffit de recopier le code de la fonction `elections_presidentielles `__ qui utilise la fonction `read_excel `__. La fonction utilise des données stockées localement afin que le code ci-dessous fonctionne toujours même si le format des données change sur le site *data.gouv.fr*. .. code:: ipython3 from actuariat_python.data import elections_presidentielles dict_df = elections_presidentielles(local=True, agg="dep") .. code:: ipython3 list(dict_df.keys()) .. parsed-literal:: ['circ1', 'circ2', 'dep1', 'dep2'] .. code:: ipython3 dict_df["dep1"].head() .. raw:: html
Code du département Libellé du département Code de la circonscription Inscrits Votants Exprimés Blancs et nuls Nathalie ARTHAUD (LO) Philippe POUTOU (NPA) Jean-Luc MELENCHON (FG) François HOLLANDE (PS) Eva JOLY (EELV) François BAYROU (MODEM) Nicolas SARKOZY (UMP) Nicolas DUPONT-AIGNAN (DLR) Marine LE PEN (FN) Jacques CHEMINADE (SP)
0 1 AIN 15 393808 327812 321359 6453 1794 3323 30898 73096 7268 32650 97722 7208 66540 860
1 2 AISNE 15 376068 303140 297944 5196 2490 3860 30360 80751 3455 19895 72090 5853 78452 738
2 3 ALLIER 6 256275 211009 205950 5059 1482 2584 27969 61131 3232 17814 49477 4068 37736 457
3 4 ALPES-DE-HAUTE-PROVENCE 3 123933 102899 100788 2111 487 1394 15269 24551 2933 7483 25668 1845 20875 283
4 5 HAUTES-ALPES 3 106865 88619 86777 1842 488 1152 12175 21248 3147 8559 22655 1782 15359 212
.. code:: ipython3 dict_df["dep2"].head() .. raw:: html
Code du département Libellé du département Code de la circonscription Inscrits Votants Exprimés Blancs et nuls François HOLLANDE (PS) Nicolas SARKOZY (UMP)
0 979 SAINT-BARTHELEMY et SAINT-MARTIN 1 22686 9907 9492 415 3851 5641
1 01 AIN 15 393866 326587 307074 19513 131333 175741
2 02 AISNE 15 376073 302076 281020 21056 147260 133760
3 03 ALLIER 6 256211 211132 196208 14924 111615 84593
4 04 ALPES-DE-HAUTE-PROVENCE 3 123895 103581 96942 6639 49498 47444
On corrige le code du département ``01`` –> ``1``. .. code:: ipython3 def cleandep(s): if isinstance(s, str): r = s.lstrip('0') else: r = str(s) return r dict_df["dep1"]["Code du département"] = dict_df["dep1"]["Code du département"].apply(cleandep) dict_df["dep2"]["Code du département"] = dict_df["dep2"]["Code du département"].apply(cleandep) .. code:: ipython3 deps = dict_df["dep1"].merge(dict_df["dep2"], on="Code du département", suffixes=("T1", "T2")) deps.columns .. parsed-literal:: Index(['Code du département', 'Libellé du départementT1', 'Code de la circonscriptionT1', 'InscritsT1', 'VotantsT1', 'ExprimésT1', 'Blancs et nulsT1', 'Nathalie ARTHAUD (LO)', 'Philippe POUTOU (NPA)', 'Jean-Luc MELENCHON (FG)', 'François HOLLANDE (PS)T1', 'Eva JOLY (EELV)', 'François BAYROU (MODEM)', 'Nicolas SARKOZY (UMP)T1', 'Nicolas DUPONT-AIGNAN (DLR)', 'Marine LE PEN (FN)', 'Jacques CHEMINADE (SP)', 'Libellé du départementT2', 'Code de la circonscriptionT2', 'InscritsT2', 'VotantsT2', 'ExprimésT2', 'Blancs et nulsT2', 'François HOLLANDE (PS)T2', 'Nicolas SARKOZY (UMP)T2'], dtype='object') .. code:: ipython3 deps["rHollandeT1"] = deps['François HOLLANDE (PS)T1'] / (deps["VotantsT1"] - deps["Blancs et nulsT1"]) deps["rSarkozyT1"] = deps['Nicolas SARKOZY (UMP)T1'] / (deps["VotantsT1"] - deps["Blancs et nulsT1"]) deps["rNulT1"] = deps["Blancs et nulsT1"] / deps["VotantsT1"] deps["rHollandeT2"] = deps["François HOLLANDE (PS)T2"] / (deps["VotantsT2"] - deps["Blancs et nulsT2"]) deps["rSarkozyT2"] = deps['Nicolas SARKOZY (UMP)T2'] / (deps["VotantsT2"] - deps["Blancs et nulsT2"]) deps["rNulT2"] = deps["Blancs et nulsT2"] / deps["VotantsT2"] data = deps[["Code du département", "Libellé du départementT1", "VotantsT1", "rHollandeT1", "rSarkozyT1", "rNulT1", "VotantsT2", "rHollandeT2", "rSarkozyT2", "rNulT2"]] data_elections = data # parfois data est remplacé dans la suite data.head() .. raw:: html
Code du département Libellé du départementT1 VotantsT1 rHollandeT1 rSarkozyT1 rNulT1 VotantsT2 rHollandeT2 rSarkozyT2 rNulT2
0 1 AIN 327812 0.227459 0.304090 0.019685 326587 0.427692 0.572308 0.059748
1 2 AISNE 303140 0.271027 0.241958 0.017141 302076 0.524020 0.475980 0.069704
2 3 ALLIER 211009 0.296824 0.240238 0.023975 211132 0.568861 0.431139 0.070686
3 4 ALPES-DE-HAUTE-PROVENCE 102899 0.243591 0.254673 0.020515 103581 0.510594 0.489406 0.064095
4 5 HAUTES-ALPES 88619 0.244858 0.261071 0.020786 89405 0.508935 0.491065 0.067390
.. code:: ipython3 deps.to_excel("deps.xlsx") dict_df["dep1"].to_excel("T1.xlsx") dict_df["dep2"].to_excel("T2.xlsx") De pandas à matplotlib ---------------------- Lorsqu’on construit un graphique avec des données stockées dans un `DataFrame `__, on suit généralement le processus suivant : - Voir si un graphique correspond dans la page `visualisation `__ de pandas - Voir la `gallerie `__ de `matplotlib `__ - Chercher un exemple de graphique sur un moteur de recherche pour tomber sur une page comme celle-ci `Using Python libraries to plot two horizontal bar charts sharing same y axis `__ - Assembler différentes sources plot ~~~~ La méthode `plot `__ permet de faire la plupart des graphiques standards (voir `Plotting `__). .. code:: ipython3 data.plot(x="Libellé du départementT1", y=["rHollandeT2", "rSarkozyT2"], figsize=(16,5)); .. image:: seance6_graphes_enonce_18_0.png .. code:: ipython3 data.plot(x="Libellé du départementT1", y=["rHollandeT2", "rSarkozyT2"], figsize=(16,5), kind="bar", stacked=True); .. image:: seance6_graphes_enonce_19_0.png .. code:: ipython3 data.plot(x="rHollandeT1", y="rHollandeT2", figsize=(16,5), kind="scatter", label="s1", title="correlation"); .. image:: seance6_graphes_enonce_20_0.png superposition ~~~~~~~~~~~~~ La méthode `plot `__ retourne un objet de type `Axes `__. On peut superposer plusieurs courbes sur le même graphique en s’assurant que la seconde courbe utilise le même objet. .. code:: ipython3 ax = data.plot(x="rHollandeT1", y="rHollandeT2", figsize=(16,5), kind="scatter", label="H", title="correlation") data.plot(x="rSarkozyT1", y="rSarkozyT2", kind="scatter", label="S", ax=ax, c="red"); .. image:: seance6_graphes_enonce_22_0.png On ajoute une ligne avec la méthode `Axes.plot `__ ou du text avec `text `__ : .. code:: ipython3 ax = data.plot(x="rHollandeT1", y="rHollandeT2", figsize=(16,5), kind="scatter", label="H", title="correlation") data.plot(x="rSarkozyT1", y="rSarkozyT2", kind="scatter", label="S", ax=ax, c="red") ax.plot([0.2,0.7], [0.2,0.7], "g--") ax.text(0.5, 0.5, "rien au dessous", weight="bold", rotation="-30"); .. image:: seance6_graphes_enonce_24_0.png plusieurs graphes sur la même figure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *pandas* crée une `Figure `__ de façon implicite avec un seul graphe. Pour créer plusieurs graphes, il faut créer ce type d’objet en précisant qu’il y aura plusieurs `Axes `__ avec la fonction `subplots `__ et les transmettre à *pandas*. On peut également partager l’axe des X ou l’axe des Y. .. code:: ipython3 import matplotlib.pyplot as plt fig, axes = plt.subplots(1, 2, figsize=(16,5), sharey=True) data.plot(x="rHollandeT1", y="rHollandeT2", figsize=(16,5), kind="scatter", label="H", ax=axes[0]) data.plot(x="rSarkozyT1", y="rSarkozyT2", kind="scatter", label="S", ax=axes[1], c="red") axes[0].plot([0.2,0.7], [0.2,0.7], "g--") axes[1].plot([0.2,0.7], [0.2,0.7], "g--"); .. image:: seance6_graphes_enonce_26_0.png matplotlib sans pandas ~~~~~~~~~~~~~~~~~~~~~~ On peut se passer de pandas et s’inspirer d’un graphe de la gallerie pour ajouter des points dépendants du nombre de votants `scatter_demo `__ et ajouter une légende manuellement avec la méthode `legend `__. .. code:: ipython3 import matplotlib.pyplot as plt fig, axes = plt.subplots(1, 1, figsize=(16,5)) c = axes.scatter(x=data["rHollandeT1"], y=data["rHollandeT2"], s=data["VotantsT1"]/5000, alpha=0.5) axes.plot([0.2,0.7], [0.2,0.7], "g--") axes.legend( (c,), ("H",) ); .. image:: seance6_graphes_enonce_28_0.png Pandas et graphes prêts à l’emploi ---------------------------------- histogrammes ~~~~~~~~~~~~ avec `hist `__ .. code:: ipython3 data.hist(figsize=(8, 8)); .. image:: seance6_graphes_enonce_31_0.png Le paramètre *figsize* permettrait de modifier la taille du graphique. correlations ~~~~~~~~~~~~ avec `scatter_matrix `__ .. code:: ipython3 from pandas.plotting import scatter_matrix scatter_matrix(data, alpha=0.2, figsize=(14, 14), diagonal='kde') print("-"); .. parsed-literal:: - .. image:: seance6_graphes_enonce_34_1.png cartes avec cartopy ------------------- Les coordonnées sur une carte se font avec des `coordonnées géographiques `__ : longitude et latitude. La distance entre deux lieux géographiques se calcule grâce à la `distance de Haversine `__. Les graphes se font avec `cartopy `__. une carte simple ~~~~~~~~~~~~~~~~ On la choisit centrée sur la France. La carte se dessine avec `matplotlib `__ auquel `cartopy `__ ajoute un système de projection différent. Comme elle accepte un argument ``ax``, il est possible de changer sa taille ou de la juxtaposer à côté d’un autre graphe. Le module `cartopy `__ ne contient pas toutes les informations sur le territoire français et certaines `options `__ ne semble pas avoir d’effet. .. code:: ipython3 import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.pyplot as plt fig = plt.figure(figsize=(7,7)) ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree()) ax.set_extent([-5, 10, 42, 52]) ax.add_feature(cfeature.OCEAN.with_scale('50m')) # ax.add_feature(cfeature.COASTLINE) # ax.add_feature(cfeature.RIVERS) # pas d'effet, cartopy ne connaît pas les rivières en France # ax.add_feature(cfeature.BORDERS, linestyle=':') # cette instruction télécharge un fichier (10m=14Mo, 50m, 110m) # il faut la résolution 10m pour la France ax.add_feature(cfeature.STATES.with_scale('10m')) ax.set_title('France'); .. image:: seance6_graphes_enonce_37_0.png La méthode `with_scale `__ propose trois résolution 10m, 50m, 110m. La module `cartopy `__ n’inclut que la résolution 110m, le reste doit être téléchargé. exercice 1 : centrer la carte de la France ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ajouter du texte ou une marque ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sur une carte, on veut la plupart du temps ajouter du texte. On reprend le début de ce code qu’on place dans une fonction, puis on place Paris. On utilise pour cela les fonctions standard de *matplotlib* mais on convertit les coordonnées géographiques en coordonnées relatives au graphe (donc dans un repère différent). .. code:: ipython3 import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.pyplot as plt def carte_france(figsize=(7, 7)): fig = plt.figure(figsize=figsize) ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree()) ax.set_extent([-5, 10, 42, 52]) ax.add_feature(cfeature.OCEAN.with_scale('50m')) ax.add_feature(cfeature.RIVERS.with_scale('50m')) ax.add_feature(cfeature.BORDERS.with_scale('50m'), linestyle=':') ax.set_title('France'); return ax carte_france(); .. image:: seance6_graphes_enonce_42_0.png .. code:: ipython3 import matplotlib.pyplot as plt ax = carte_france() lon = 2.3488000 lat = 48.853410 ax.plot([lon], [lat], 'ro', markersize=6) ax.text(lon, lat, "Paris"); .. image:: seance6_graphes_enonce_43_0.png On connaît rarement les coordonnées de chaque ville mais un moteur de recherche donne rapidement des pistes pour trouver ces données. Il faut néanmoins s’assurer que le licence autorise ce qu’on l’intention de faire avec : `Liste des villes de France en SQL, CSV ou XML `__ .. code:: ipython3 from pyensae.datasource import download_data download_data("villes_france.csv", url="http://sql.sh/ressources/sql-villes-france/") .. parsed-literal:: 'villes_france.csv' .. code:: ipython3 cols = ["ncommune", "numero_dep", "slug", "nom", "nom_simple", "nom_reel", "nom_soundex", "nom_metaphone", "code_postal", "numero_commune", "code_commune", "arrondissement", "canton", "pop2010", "pop1999", "pop2012", "densite2010", "surface", "superficie", "dlong", "dlat", "glong", "glat", "slong", "slat", "alt_min", "alt_max"] import pandas df = pandas.read_csv("villes_france.csv", header=None,low_memory=False, names=cols) .. code:: ipython3 df.head() .. raw:: html
ncommune numero_dep slug nom nom_simple nom_reel nom_soundex nom_metaphone code_postal numero_commune ... surface superficie dlong dlat glong glat slong slat alt_min alt_max
0 1 01 ozan OZAN ozan Ozan O250 OSN 01190 284 ... 93 6.60 4.91667 46.3833 2866.0 51546.0 45456.0 462330.0 170.0 205.0
1 2 01 cormoranche-sur-saone CORMORANCHE-SUR-SAONE cormoranche sur saone Cormoranche-sur-Saône C65652625 KRMRNXSRSN 01290 123 ... 107 9.85 4.83333 46.2333 2772.0 51379.0 44953.0 461427.0 168.0 211.0
2 3 01 plagne-01 PLAGNE plagne Plagne P425 PLKN 01130 298 ... 20 6.20 5.73333 46.1833 3769.0 51324.0 54342.0 461131.0 560.0 922.0
3 4 01 tossiat TOSSIAT tossiat Tossiat T230 TST 01250 422 ... 138 10.17 5.31667 46.1333 3309.0 51268.0 51854.0 460828.0 244.0 501.0
4 5 01 pouillat POUILLAT pouillat Pouillat P430 PLT 01250 309 ... 14 6.23 5.43333 46.3333 3435.0 51475.0 52542.0 461938.0 333.0 770.0

5 rows × 27 columns

exercice 2 : placer les plus grandes villes de France sur la carte ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ départements ~~~~~~~~~~~~ Pour dessiner des formes sur une carte, il faut connaître les coordonnées de ces formes. L’article suivant `Matplotlib Basemap tutorial 10: Shapefiles Unleached, continued `__ permet de dessiner les départements belges. On va s’en inspirer pour dessiner les départements français. La première chose à faire est de récupérer des données géographiques. Une façon simple de les trouver est d’utiliser un moteur de recherche avec le mot clé **shapefile** inclus dedans : c’est le format du fichier. *shapefile france* permet d’obtenir quelques sources. En voici d’autres : - `GADM `__ : database of Global Administrative Areas - `OpenData.gouv commune `__ : base de données sur data.gouv.fr - `The National Map Small-Scale Collection `__ : Etats-Unis - `ArcGIS `__ : API Javascripts - `Natural Earth `__ : Natural Earth is a public domain map dataset available at 1:10m, 1:50m, and 1:110 million scales. Featuring tightly integrated vector and raster data, with Natural Earth you can make a variety of visually pleasing, well-crafted maps with cartography or GIS software. - `thematicmapping `__ : World Borders Dataset - `OpenStreetMap Data Extracts `__ : OpenStreetMap data - `OpenStreetMapData `__ : OpenStreetMap data - `Shapefile sur Wikipedia `__ : contient divers liens vers des sources de données La première chose à vérifier est la licence associées aux données : on ne peut pas en faire ce qu’on veut. Pour cet exemple, j’ai choisi la première source de données, GADM. La licence n’est pas précisée explicitement (on peut trouver *happy to share* sur le site, la page wikipedia `GADM `__ précise : *GADM is not freely available for commercial use. The GADM project created the spatial data for many countries from spatial databases provided by national governments, NGO, and/or from maps and lists of names available on the Internet (e.g. from Wikipedia).* C’est le choix que j’avais fait en 2015 mais l’accès à ces bases a probablement changé car l’accès est restreint. J’ai donc opté pour les bases accessibles depuis `data.gouv.fr `__. Leur seul inconvénient est que les coordonnées sont exprimées dans une projection de type `Lambert 93 `__. Cela nécessite une conversion. .. code:: ipython3 from pyensae.datasource import download_data try: download_data("GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01.7z", website="https://wxs-telechargement.ign.fr/oikr5jryiph0iwhw36053ptm/telechargement/inspire/" + \ "GEOFLA_THEME-DEPARTEMENTS_2015_2$GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/file/") except Exception as e: # au cas le site n'est pas accessible download_data("GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01.7z", website="xd") .. code:: ipython3 from pyquickhelper.filehelper import un7zip_files try: un7zip_files("GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01.7z", where_to="shapefiles") departements = 'shapefiles/GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/1_DONNEES_LIVRAISON_2015/' + \ 'GEOFLA_2-1_SHP_LAMB93_FR-ED152/DEPARTEMENT/DEPARTEMENT.shp' except FileNotFoundError as e: # Il est possible que cette instruction ne fonctionne pas. # Dans ce cas, on prendra une copie de ce fichier. import warnings warnings.warn("Plan B parce que " + str(e)) download_data("DEPARTEMENT.zip") departements = "DEPARTEMENT.shp" import os if not os.path.exists(departements): raise FileNotFoundError("Impossible de trouver '{0}'".format(departements)) La license accompagne les données : *ce produit est téléchargeable et utilisable gratuitement sous licence*\ `Etalab `__. Pour un usage commercial, il faut faire attentation à la license associée aux données. Le seul inconvénient des données *GEOFLA* est que certaines sont données dans le système de coordonnées `Lambert 93 `__ (voir aussi `Cartographie avec R `__). .. code:: ipython3 shp = departements import shapefile r = shapefile.Reader(shp) shapes = r.shapes() records = r.records() len(shapes), len(records) .. parsed-literal:: (96, 96) .. code:: ipython3 r.bbox .. parsed-literal:: [99217.1, 6049646.300000001, 1242417.2, 7110480.100000001] On regarde une zone en particulier mais on réduit la quantité de données affichées : .. code:: ipython3 d = shapes[0].__dict__.copy() d["points"] = d["points"][:10] d .. parsed-literal:: {'shapeType': 5, 'points': [(701742.0, 6751181.100000001), (701651.9, 6751166.9), (701552.0, 6751162.7), (700833.7000000001, 6751313.7), (700669.4, 6751380.0), (700475.4, 6751476.600000001), (700400.7000000001, 6751517.2), (700098.3, 6751789.600000001), (699993.8, 6751845.4), (699874.1000000001, 6751876.4)], 'parts': [0], 'bbox': [688654.4, 6690595.300000001, 800332.3, 6811114.5]} 350 départements, sûr ? .. code:: ipython3 records[0], records[1] .. parsed-literal:: (Record #0: ['DEPARTEM0000000000000004', '89', 'YONNE', '024', 'AUXERRE', 742447, 6744261, 748211, 6750855, '27', 'BOURGOGNE-FRANCHE-COMTE'], Record #1: ['DEPARTEM0000000000000028', '69', 'RHONE', '381', 'LYON', 842221, 6520526, 832095, 6530600, '84', 'AUVERGNE-RHONE-ALPES']) .. code:: ipython3 len(set([r[6] for r in records])) .. parsed-literal:: 96 Puis je récupère le code final (toujours à `Matplotlib Basemap tutorial 10: Shapefiles Unleached, continued `__) en l’adaptant pour la France. Petite astuce, on utilie la fonction `lambert932WGPS `__ du module `ensae_teaching_cs `__. On recopie le code ici : .. code:: ipython3 import math def lambert932WGPS(lambertE, lambertN): class constantes: GRS80E = 0.081819191042816 LONG_0 = 3 XS = 700000 YS = 12655612.0499 n = 0.7256077650532670 C = 11754255.4261 delX = lambertE - constantes.XS delY = lambertN - constantes.YS gamma = math.atan(-delX / delY) R = math.sqrt(delX * delX + delY * delY) latiso = math.log(constantes.C / R) / constantes.n sinPhiit0 = math.tanh(latiso + constantes.GRS80E * math.atanh(constantes.GRS80E * math.sin(1))) sinPhiit1 = math.tanh(latiso + constantes.GRS80E * math.atanh(constantes.GRS80E * sinPhiit0)) sinPhiit2 = math.tanh(latiso + constantes.GRS80E * math.atanh(constantes.GRS80E * sinPhiit1)) sinPhiit3 = math.tanh(latiso + constantes.GRS80E * math.atanh(constantes.GRS80E * sinPhiit2)) sinPhiit4 = math.tanh(latiso + constantes.GRS80E * math.atanh(constantes.GRS80E * sinPhiit3)) sinPhiit5 = math.tanh(latiso + constantes.GRS80E * math.atanh(constantes.GRS80E * sinPhiit4)) sinPhiit6 = math.tanh(latiso + constantes.GRS80E * math.atanh(constantes.GRS80E * sinPhiit5)) longRad = math.asin(sinPhiit6) latRad = gamma / constantes.n + constantes.LONG_0 / 180 * math.pi longitude = latRad / math.pi * 180 latitude = longRad / math.pi * 180 return longitude, latitude lambert932WGPS(99217.1, 6049646.300000001), lambert932WGPS(1242417.2, 7110480.100000001) .. parsed-literal:: ((-4.1615802638173065, 41.303505287589545), (10.699505053975292, 50.85243395553585)) .. code:: ipython3 import cartopy.crs as ccrs import matplotlib.pyplot as plt ax = carte_france((8,8)) from matplotlib.collections import LineCollection import shapefile import geopandas from shapely.geometry import Polygon from shapely.ops import cascaded_union, unary_union shp = departements r = shapefile.Reader(shp) shapes = r.shapes() records = r.records() polys = [] for i, (record, shape) in enumerate(zip(records, shapes)): # les coordonnées sont en Lambert 93 if i == 0: print(record, shape.parts) geo_points = [lambert932WGPS(x,y) for x, y in shape.points] if len(shape.parts) == 1: # Un seul polygone poly = Polygon(geo_points) else: # Il faut les fusionner. ind = list(shape.parts) + [len(shape.points)] pols = [Polygon(geo_points[ind[i]:ind[i+1]]) for i in range(0, len(shape.parts))] try: poly = unary_union(pols) except Exception as e: print("Cannot merge: ", record) print([_.length for _ in pols], ind) poly = Polygon(geo_points) polys.append(poly) data = geopandas.GeoDataFrame(geometry=polys) # cmap -> voir https://matplotlib.org/users/colormaps.html data.plot(ax=ax, cmap='tab20', edgecolor='black'); # Ou pour définir des couleurs spécifiques. # geopandas.plotting.plot_polygon_collection(ax, data['geometry'], data['colors'], values=None) .. parsed-literal:: Record #0: ['DEPARTEM0000000000000004', '89', 'YONNE', '024', 'AUXERRE', 742447, 6744261, 748211, 6750855, '27', 'BOURGOGNE-FRANCHE-COMTE'] [0] .. image:: seance6_graphes_enonce_63_1.png exercice 3 : résultats des élections par départements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ce n’est pas toujours évident ! seaborn ------- `seaborn `__ propose des graphiques inéressants pour un statisticien. La `gallerie `__ en donne un bon aperçu. On retravaille peu les graphiques. Le code suivant montre les corrélations entre variables `pairplot `__. **warning** les warnings sont la plupart du temps dûs au fait que *seaborn* a été testé sur une version antérieure d’une de ses dépendances comme *matplotlib* et qu’il n’est pas encore à jour pour tenir compte des derniers développement. .. code:: ipython3 import seaborn seaborn.pairplot(data_elections); .. image:: seance6_graphes_enonce_67_0.png Celui-ci est aussi intéressant : `clustermap `__ pour étudier les corrélations. .. code:: ipython3 import seaborn seaborn.set(font="monospace") cmap = seaborn.diverging_palette(h_neg=210, h_pos=350, s=90, l=30, as_cmap=True) seaborn.clustermap(data_elections.corr(), linewidths=.5, figsize=(13, 13), cmap=cmap); .. image:: seance6_graphes_enonce_69_0.png bokeh ----- `bokeh `__ propose des graphiques en javascript. La `gallerie `__ est moins fournie que celle de matplotib. Le principale avantage de bokeh est de proposer gaphiques zoomables (interactifs). initialisation ~~~~~~~~~~~~~~ La première étape est de signifier que la sortie se fera dans un notebook. .. code:: ipython3 from bokeh.plotting import output_notebook output_notebook() .. raw:: html
Loading BokehJS ...
premier graphe ~~~~~~~~~~~~~~ On utilise bokeh pour un simple graphique XY. on peut choisir les différentes options interactives, zoom … Voir `tools `__. .. code:: ipython3 from bokeh.plotting import figure, show p = figure(title = "élections") p.title.text = "élections" p.circle(data_elections["rHollandeT1"], data_elections["rHollandeT2"], color="red", fill_alpha=0.2, size=10, legend="H") p.circle(data_elections["rSarkozyT1"], data_elections["rSarkozyT2"], color="blue", fill_alpha=0.2, size=10, legend="S") p.line([0.2,0.7], [0.2,0.7], line_color="green", line_dash="dashed") p.xaxis.axis_label = "tour 1" p.yaxis.axis_label = "tour 2" show(p) .. parsed-literal:: BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead .. raw:: html
ajouter du texte ~~~~~~~~~~~~~~~~ Les différents éléments qu’on peut ajouter au graphe s’appelle des `Glyphes `__. La documentation manque parfois de précision. Pour la fonction `text `__, il faut préciser le texte à afficher sous forme de liste. .. code:: ipython3 from bokeh.plotting import figure, show p = figure(title = "élections") p.title.text = "élections" p.circle(data_elections["rHollandeT1"], data_elections["rHollandeT2"], color="red", fill_alpha=0.2, size=10, legend="H") p.circle(data_elections["rSarkozyT1"], data_elections["rSarkozyT2"], color="blue", fill_alpha=0.2, size=10, legend="S") p.line([0.2,0.7], [0.2,0.7], line_color="green", line_dash="dashed") p.xaxis.axis_label = "tour 1" p.yaxis.axis_label = "tour 2" def display_text(p, row): p.text(x=row["rHollandeT1"], y=row["rHollandeT2"], text=[row["Libellé du départementT1"]], text_font_size="8pt", color="black", text_align="left", text_baseline="middle", angle=0) return row["Libellé du départementT1"] data_elections.apply(lambda row: display_text(p, row), axis=1) show(p) .. parsed-literal:: BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead .. raw:: html
composition de graphes ~~~~~~~~~~~~~~~~~~~~~~ Voir `linked brushing `__. .. code:: ipython3 from bokeh.plotting import figure, show, gridplot size = 400 ph = figure(title = "élections", width=size, height=size) ph.title.text = "élections" ph.circle(data_elections["rHollandeT1"], data_elections["rHollandeT2"], color="red", fill_alpha=0.2, size=10, legend="H") ph.line([0.2,0.7], [0.2,0.7], line_color="green", line_dash="dashed") ph.xaxis.axis_label = "tour 1" ph.yaxis.axis_label = "tour 2" ps = figure(title = "élections", width=size, height=size) ps.title.text = "élections" ps.circle(data_elections["rSarkozyT1"], data_elections["rSarkozyT2"], color="blue", fill_alpha=0.2, size=10, legend="S") ps.line([0.2,0.7], [0.2,0.7], line_color="green", line_dash="dashed") ps.xaxis.axis_label = "tour 1" ps.yaxis.axis_label = "tour 2" p = gridplot([[ph, ps]]) show(p) .. parsed-literal:: BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead .. raw:: html
interactions avec bokeh ~~~~~~~~~~~~~~~~~~~~~~~ Tout est expliqué dans la page `interaction `__. .. code:: ipython3 from bokeh.models.widgets import Panel, Tabs from bokeh.io import show from bokeh.plotting import figure ph = figure(title="élections") ph.circle(data_elections["rHollandeT1"], data_elections["rHollandeT2"], color="red", fill_alpha=0.2, size=10, legend="H") ph.line([0.2,0.7], [0.2,0.7], line_color="green", line_dash="dashed") ph.xaxis.axis_label = "tour 1" ph.yaxis.axis_label = "tour 2" ps = figure(title = "élections") ps.title.text = "élections" ps.circle(data_elections["rSarkozyT1"], data_elections["rSarkozyT2"], color="blue", fill_alpha=0.2, size=10, legend="S") ps.line([0.2,0.7], [0.2,0.7], line_color="green", line_dash="dashed") ps.xaxis.axis_label = "tour 1" ps.yaxis.axis_label = "tour 2" tab1 = Panel(child=ph, title="Hollande") tab2 = Panel(child=ps, title="Sarkozy") tabs = Tabs(tabs=[ tab1, tab2 ]) show(tabs) .. parsed-literal:: BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead BokehDeprecationWarning: 'legend' keyword is deprecated, use explicit 'legend_label', 'legend_field', or 'legend_group' keywords instead .. raw:: html
Si on connaît un peu de javascript, on peut créer des graphes qui peuvent interagir avec la souris à n’importe quel endroit du graphe. interaction avec matplotlib (ou bokeh) -------------------------------------- dropdown ~~~~~~~~ Le module `interact `__ permet de changer le contenu d’une cellule en fonction d’un bouton, d’une barre de défilement… Le code de cette fonction est sur `github `__. La liste des widget possible est dans le notebook `WidgetList `__ .. code:: ipython3 import matplotlib.pyplot as plt from IPython.html.widgets import interact, Dropdown def plot(cand): fig, axes = plt.subplots(1, 1, figsize=(14,5), sharey=True) if cand=="Hollande": data_elections.plot(x="rHollandeT1", y="rHollandeT2", kind="scatter", label="H", ax=axes) else: data_elections.plot(x="rSarkozyT1", y="rSarkozyT2", kind="scatter", label="S", ax=axes, c="red") axes.plot([0.2,0.7], [0.2,0.7], "g--") return axes cand = Dropdown(options=['Hollande', 'Sarkozy'], value='Hollande', description='candidat') interact(plot, cand=cand) print("") .. parsed-literal:: C:\Python395_x64\lib\site-packages\IPython\html.py:12: ShimWarning: The `IPython.html` package has been deprecated since IPython 4.0. You should import from `notebook` instead. `IPython.html.widgets` has moved to `ipywidgets`. warn("The `IPython.html` package has been deprecated since IPython 4.0. " .. parsed-literal:: interactive(children=(Dropdown(description='candidat', options=('Hollande', 'Sarkozy'), value='Hollande'), Out… .. parsed-literal:: .. image:: seance6_graphes_enonce_86_3.png .. image:: seance6_graphes_enonce_86_4.png Ces interactions ne sont pas limitées à matplotlib. Tout type de sortie peut dépendre d’un *widget*. exercice 4 : même code, widget différent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ autres options -------------- Il existe un grande nombre de modules permettant de dessiner. Les plus récents utilisent le javascript. - static - `ggplot `__ : aspect similare à matplotlib version ggplot mais la syntaxe est différente - Javascript - `pygal `__ : voir exemple plus bas, aspect réussi, le module prévoit une extension pour les `carte `__, le résultat nécessite l’ajout de quelques scripts Javascript - `mpld3 `__ : créer un graphe avec la syntaxe Javascript et le transformer en Javascript - `folium `__ : Javascript + `OpenStreetMap `__ .. code:: ipython3 html_pygal = """ {pygal_render} """ from IPython.core.display import HTML import pygal xy_chart = pygal.XY(stroke=False) xy_chart.title = 'Elections 2012' xy = list(zip(data_elections["rHollandeT1"], data_elections["rHollandeT2"])) xy_chart.add('H', xy) HTML(html_pygal.format(pygal_render=xy_chart.render().decode())) .. raw:: html Elections 20120.360.360.40.40.440.440.480.480.520.520.560.560.60.60.640.640.680.680.20.20.240.240.280.280.320.320.360.360.40.40.440.440.480.480.520.520.560.56Elections 20120.22745901: 0.427691696581.0647766051942407.55023461349210.2710274414: 0.5240196427157.53588768153753276.218512312604960.296824472: 0.5688605969202.81471748793922215.083193110457560.2435905068: 0.5105939634109.37870583566756294.52283268606950.2448575083: 0.5089349964111.6025410958509296.78463729870630.1921446979: 0.35686400619.081257791223354504.115384615384640.2598471487: 0.5344739544137.91228924254577261.965299151331240.289346381: 0.5188984103189.68920646254398283.200704897625540.3436094453: 0.6469461912284.9314882292195108.622752733234220.2279310664: 0.42630168781.89332776118067409.44534751695650.3040610498: 0.5625124908215.51632572983564223.738082393949530.2943817999: 0.544246979198.52735063445377248.64093992767930.2450954861: 0.4717058377112.02023854046952347.542176748614570.2933821918: 0.5311928218196.77284304770458266.438734015677770.3084466394: 0.5180154747223.21389256997236284.40448283684310.3279448484: 0.5882997589257.4370595761621188.580202831661670.2846826723: 0.5156611402181.50348642317812287.61433854394490.2676430912: 0.5404444289151.59569164452353253.825265670418050.4296742368: 0.6486280588435.99201592959423106.329725839475940.2778590727: 0.4845220526169.5267356040422330.0687887599560.3302260386: 0.5919094547261.440994112384183.65881107910360.3401543408: 0.6101681388278.8671047493355158.765262276418530.3208868145: 0.591355662245.04883077777768184.413841626240130.2628306315: 0.4808757424143.14888459607607335.040100056867230.2504986939: 0.4918420525121.50392408286245320.088837656147460.2465475746: 0.4754584072114.56893759888008342.42599363614860.2570867555: 0.4652706844133.06725965853144356.31574456610860.336956677: 0.5886362423273.25457983769303188.121447629068540.2410806983: 0.4880095439104.97350150059782325.314008248975140.3282833016: 0.5877897563258.0311110732751189.275530834168480.31861502: 0.5664088131241.0613874895372218.425909406186630.3175492669: 0.5660996845239.19078266702041218.84736946199480.2669087013: 0.5131409025150.30669394784496291.050383548272860.3176627647: 0.5571454684239.38999342035038231.055380518671480.2987088695: 0.5566048435206.12220342390205231.792458316519060.2810099775: 0.512271165175.05718925933752292.23616734743950.2784438552: 0.521245461170.55314311561494280.00077986417410.2460202605: 0.494249103113.64339836862091316.807109962286860.3275273756: 0.5698881114256.70431329313305213.682299040627360.2501102377: 0.4757464378120.82210754353578342.03329798660880.2645875715: 0.5050122963146.2326575481917302.132773399117470.2636337949: 0.5137946367144.55859315948732290.1590945411050.3175662576: 0.5635370249239.22060466449628222.341251712710740.2545099636: 0.459727615128.54448640285398363.87306177128880.3451301832: 0.6188924192287.60068049768853146.870741352721840.2691317444: 0.5135409093154.2085690839581290.50502169221910.251549814: 0.4995315135123.34884538169514309.60517017535560.2710428218: 0.4885272896157.5628832139739324.60812353122910.2703860137: 0.4990071182156.41005679414104310.3201209643330.2405223124: 0.4468609643103.99342511879745381.41521296112410.237487769: 0.455749636198.6672083527552369.29656390515290.2586588102: 0.4692814875135.82652294865477350.84749044533680.279223294: 0.5306282164171.9212106021703267.2085063781840.2339890088: 0.461998146792.5262006096088360.77746127218390.2828813552: 0.5173251854178.34182285177977285.34561031655230.2452890545: 0.4650352796112.3599890058706356.63669103121050.326434194: 0.5881212704254.78556591053663188.823550754017220.2796814818: 0.5287937639172.72541971235609269.709564679204960.2490406428: 0.4733560214118.94475954890072345.29234710050220.2426361408: 0.4711294378107.70360690887996348.32802963727930.2943817208: 0.5618435736198.52721181098605224.650071598683160.3308864131: 0.6046696171262.60008045796314166.261844052320270.2993037086: 0.571203481207.16626220401236211.888948721674860.3315941508: 0.6246555037263.84229846350917139.013459632639580.2596311801: 0.5058602498137.5332221133715300.976689387301240.1957599175: 0.36559510325.42667476225948492.21156997511940.1888947266: 0.3666556213.376923076923061490.7656809130560.2690870038: 0.4797769249154.13004051252685336.53820725366020.2638327783: 0.4963742183144.9078479745138313.90976753843620.2869166877: 0.5185616496185.42461997218203283.659838204494750.2812678242: 0.5266959464175.5097606016877272.56968988266020.2363860724: 0.470667203796.73351564644082348.958231000166850.2051714488: 0.398973035741.94575135321092446.704720114506760.3482559745: 0.5560344101293.0870549611268232.570176645922740.2940001524: 0.5494242986197.85748479486088241.582279119142980.2764595121: 0.4925310938167.07023329370008319.14941166913250.2731564701: 0.4570477056161.2727491567879367.526800251787340.3333603951: 0.5730718101266.9424023349598209.341703785836840.2837086077: 0.5440857882179.79381258636184248.86070444112680.3068977754: 0.5555442098220.4953334740937233.23850647706410.2763020762: 0.5125240293166.79390249462145291.89141691273880.1964659664: 0.373608318326.665928510694187481.28650200119250.2233315968: 0.435665582173.82035989354489396.67878793437170.2481172555: 0.4440166679117.32403433260615385.293073486327670.3198787089: 0.5716431017243.279408577325211.28957811354560.3590408325: 0.6399392027312.016588403223118.17594986395630.2468086779: 0.4906111046115.0272250764751321.76708898658540.2399627718: 0.4688413485103.0113219018644351.44756773667290.2600536551: 0.5052332051138.27474824133535301.831590340875830.3039023794: 0.5343166443215.23782819111605262.17977275073670.3015503263: 0.4948467982211.10951538954086315.99222359255120.3868349831: 0.6531908835360.80075409648975100.108855867461610.3293363765: 0.5647584825259.8794632657387220.675939316577970.3241095145: 0.5390821527250.7052990460707255.68256765203870.2831581923: 0.4694964979178.82772604226005350.55434934451680.5699612149: 0.7193677038682.2230769230779.8846153846153580.5198195119: 0.6843316045594.214589485001257.652178671278990.4261466909: 0.6204747063429.80048346042724144.713480646970370.5328673249: 0.7148632408617.116051195152916.025916129696610.3375142531: 0.6530612245274.2332349178825100.285630549117510.2677704469: 0.4057100716151.81922587698762437.5195712298540.3655059334: 0.4905494278323.3641037504895321.851178061315640.4827532386: 0.5606441129529.1560366191901226.28539396060250.324318908: 0.4694047691251.07282565902872350.679410710537870.2491065327: 0.3697442124119.06040915828333486.55475180912390.2228659974: 0.423966623573.00314198098302412.628929467622750.2599373534: 0.455932439138.0706159719668369.0473338349626H