Graphes - énoncé

Links: notebook, html ., PDF, python, slides ., presentation ., GitHub

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.

%matplotlib inline

On change le style pour un style plus moderne, celui de ggplot :

import matplotlib.pyplot as plt
plt.style.use('ggplot')
from jyquickhelper import add_notebook_menu
add_notebook_menu()
run previous cell, wait for 2 seconds

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.

from actuariat_python.data import elections_presidentielles
dict_df = elections_presidentielles(local=True, agg="dep")
list(dict_df.keys())
['circ1', 'circ2', 'dep1', 'dep2']
dict_df["dep1"].head()
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
dict_df["dep2"].head()
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.

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)
deps = dict_df["dep1"].merge(dict_df["dep2"],
                                       on="Code du département",
                                       suffixes=("T1", "T2"))
deps.columns
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')
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()
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
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 :

plot

La méthode plot permet de faire la plupart des graphiques standards (voir Plotting).

data.plot(x="Libellé du départementT1", y=["rHollandeT2", "rSarkozyT2"], figsize=(16,5))
<matplotlib.axes._subplots.AxesSubplot at 0x1e2a3e5bf28>
../_images/seance6_graphes_enonce_18_1.png
data.plot(x="Libellé du départementT1", y=["rHollandeT2", "rSarkozyT2"], figsize=(16,5), kind="bar", stacked=True)
<matplotlib.axes._subplots.AxesSubplot at 0x1e2a37c75f8>
../_images/seance6_graphes_enonce_19_1.png
data.plot(x="rHollandeT1", y="rHollandeT2", figsize=(16,5), kind="scatter", label="s1", title="correlation")
<matplotlib.axes._subplots.AxesSubplot at 0x1e2a4099a90>
../_images/seance6_graphes_enonce_20_1.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.

ax = data.plot(x="rHollandeT1", y="rHollandeT2", figsize=(16,5), kind="scatter", label="H", title="correlation")
print(type(ax))
data.plot(x="rSarkozyT1", y="rSarkozyT2", kind="scatter", label="S", ax=ax, c="red")
<class 'matplotlib.axes._subplots.AxesSubplot'>
<matplotlib.axes._subplots.AxesSubplot at 0x1e2a41aff28>
../_images/seance6_graphes_enonce_22_2.png

On ajoute une ligne avec la méthode Axes.plot ou du text avec text :

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")
<matplotlib.text.Text at 0x1e2a46cfcc0>
../_images/seance6_graphes_enonce_24_1.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.

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--")
[<matplotlib.lines.Line2D at 0x1e2a5c18ef0>]
../_images/seance6_graphes_enonce_26_1.png

matplolib 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.

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",) )
<matplotlib.legend.Legend at 0x1e2a5e726a0>
../_images/seance6_graphes_enonce_28_1.png

Pandas et graphes prêts à l’emploi

histogrammes

avec hist

data.hist()
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A48C7C88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A60E4748>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6020588>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A605E208>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A60FA160>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6132E48>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A617F400>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A61B8908>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A620A198>]], dtype=object)
../_images/seance6_graphes_enonce_31_1.png

Le paramètre figsize permettrait de modifier la taille du graphique.

correlations

avec scatter_matrix

from pandas.tools.plotting import scatter_matrix
scatter_matrix(data, alpha=0.2, figsize=(14, 14), diagonal='kde')
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A627ADA0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A64C7BA8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A650EC88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A654E320>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A659B358>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A65D4AC8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6620CF8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A66622B0>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A66A7A20>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A66E0D30>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A67335C0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A677E1D0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A67B8D68>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6806E80>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6844B00>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6890D30>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A68D23C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6917940>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6951E48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A69A36D8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A69F0128>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6A29E80>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6A76EB8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6AB6C18>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6B02D30>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6B424E0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6B8AC50>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6BC3F60>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6C147F0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6C60240>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6C99F98>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6CE6FD0>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6D25D30>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6D72E48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6DB25F8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6DF9D68>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6E3F0B8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6E85908>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6ED2358>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6F110F0>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6F5D128>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6F96E48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A6FE1F60>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A7022710>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A7069E80>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A70AE1D0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A80C4A20>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A8112470>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A8151208>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A819E240>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A81D7F60>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A82280B8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A8262828>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A82AAF98>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A82ED2E8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A8335B38>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A8382588>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A83C1320>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A840D358>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A844D0B8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A84981D0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A84D2940>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A85210F0>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001E2A855F400>]], dtype=object)
../_images/seance6_graphes_enonce_34_1.png

cartes avec basemap

Je recommande la lecture de ce tutoriel Visualization: Mapping Global Earthquake Activity. Les exemples se font avec le module basemap. 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.

Alternative à basemap : cartopy.

une carte simple

On la choisit centrée sur la France. On crée la carte à l’aide de l’objet Basemap. Comme elle accepte un argument ax, il est possible de changer sa taille ou de la juxtaposer à côté d’un autre graphe. Les couleurs peuvent être être décrite en hexadécimale #RRVVBB (rouge, vert, bleu) ou on peut utiliser la palette des couleurs.

from mpl_toolkits.basemap import Basemap
import numpy

import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 1, figsize=(8,8))

m = Basemap(llcrnrlon=-5,llcrnrlat=40,urcrnrlon=20,urcrnrlat=56,
            resolution='i',projection='cass',lon_0=2.34,lat_0=48,
           ax=axes)
m.drawcoastlines()
m.drawcountries()
m.fillcontinents(color='lightgrey', lake_color='#AAAAFF')

m.drawparallels(numpy.arange(-40,61.,2.))
m.drawmeridians(numpy.arange(-20.,21.,2.))
m.drawmapboundary(fill_color='#BBBBFF')
<matplotlib.patches.Rectangle at 0x1e2a8788198>
../_images/seance6_graphes_enonce_37_1.png

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).

def carte_france():
    from mpl_toolkits.basemap import Basemap
    import numpy

    import matplotlib.pyplot as plt
    fig, axes = plt.subplots(1, 1, figsize=(8,8))

    m = Basemap(llcrnrlon=-5,llcrnrlat=40,urcrnrlon=20,urcrnrlat=56,
                resolution='i',projection='cass',lon_0=2.34,lat_0=48,
               ax=axes)
    m.drawcoastlines()
    m.drawcountries()
    m.fillcontinents(color='lightgrey', lake_color='#AAAAFF')

    m.drawparallels(numpy.arange(-40,61.,2.))
    m.drawmeridians(numpy.arange(-20.,21.,2.))
    m.drawmapboundary(fill_color='#BBBBFF')
    return m, axes
import matplotlib.pyplot as plt

m, ax = carte_france()

lon = 2.3488000
lat = 48.853410
x,y = m(lon, lat)  # la conversion opère ici
m.plot(x, y, 'ro', markersize=6)
ax.text(x, y, "Paris")
<matplotlib.text.Text at 0x1e2aa471240>
../_images/seance6_graphes_enonce_42_1.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

import pyensae
pyensae.download_data("villes_france.csv", url="http://sql.sh/ressources/sql-villes-france/")
'villes_france.csv'
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)
df.head()
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 51546 45456 462330 170 205
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 51379 44953 461427 168 211
2 3 01 plagne-01 PLAGNE plagne Plagne P425 PLKN 01130 298 ... 20 6.20 5.73333 46.1833 3769 51324 54342 461131 560 922
3 4 01 tossiat TOSSIAT tossiat Tossiat T230 TST 01250 422 ... 138 10.17 5.31667 46.1333 3309 51268 51854 460828 244 501
4 5 01 pouillat POUILLAT pouillat Pouillat P430 PLT 01250 309 ... 14 6.23 5.43333 46.3333 3435 51475 52542 461938 333 770

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 :

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.

from pyensae 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")
from pyquickhelper.filehelper import un7zip_files
un7zip_files("GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01.7z", where_to="shapefiles")
['shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/3_METADONNEES_PRODUIT/IGNF_GEOFLAr_2-1.xml',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/3_METADONNEES_PRODUIT/IGNF_GEOFLAr_2-1.html',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/4_METADONNEES_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152/LISEZ-MOI.txt',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/5_SUPPLEMENTS_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152/LISEZ_MOI.TXT',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/2_DESCRIPTIFS_PRODUIT/DC_GEOFLA_2-1.pdf',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/2_DESCRIPTIFS_PRODUIT/DL_vecteur.pdf',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/2_DESCRIPTIFS_PRODUIT/Supplements_Gratuits.pdf',
 '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.dbf',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/1_DONNEES_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152/DEPARTEMENT/LIMITE_DEPARTEMENT.dbf',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/2_DESCRIPTIFS_PRODUIT.md5',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/3_METADONNEES_PRODUIT.md5',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/1_DONNEES_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152/DEPARTEMENT.md5',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/1_DONNEES_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152.md5',
 '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.prj',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/1_DONNEES_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152/DEPARTEMENT/LIMITE_DEPARTEMENT.prj',
 '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',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/1_DONNEES_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152/DEPARTEMENT/LIMITE_DEPARTEMENT.shp',
 '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.shx',
 'shapefiles\GEOFLA_2-1_DEPARTEMENT_SHP_LAMB93_FXX_2015-12-01/GEOFLA/1_DONNEES_LIVRAISON_2015/GEOFLA_2-1_SHP_LAMB93_FR-ED152/DEPARTEMENT/LIMITE_DEPARTEMENT.shx']

La license accompagne les données : ce produit est téléchargeable et utilisable gratuitement sous licence `Etalab <https://www.etalab.gouv.fr/licence-ouverte-open-licence>`__. 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).

shp = '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'
import shapefile
r = shapefile.Reader(shp)
shapes = r.shapes()
records = r.records()
len(shapes), len(records)
(96, 96)
r.bbox
[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 :

d = shapes[0].__dict__.copy()
d["points"] = d["points"][:10]
d
{'bbox': [688654.4, 6690595.300000001, 800332.3, 6811114.5],
 'parts': [0],
 '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]],
 'shapeType': 5}

350 départements, sûr ?

records[0], records[1]
(['DEPARTEM0000000000000004',
  '89',
  'YONNE',
  '024',
  'AUXERRE',
  742447,
  6744261,
  748211,
  6750855,
  '27',
  'BOURGOGNE-FRANCHE-COMTE'],
 ['DEPARTEM0000000000000028',
  '69',
  'RHONE',
  '381',
  'LYON',
  842221,
  6520526,
  832095,
  6530600,
  '84',
  'AUVERGNE-RHONE-ALPES'])
len(set([r[6] for r in records]))
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 :

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)
((-4.1615802638173065, 41.303505287589545),
 (10.699505053975292, 50.85243395553585))
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap

fig = plt.figure(figsize=(20,10))
#Custom adjust of the subplots
#plt.subplots_adjust(left=0.05,right=0.95,top=0.90,bottom=0.05,wspace=0.15,hspace=0.05)
ax = plt.subplot(111)
#Let's create a basemap of Europe
x1 = -5.0
x2 = 12.
y1 = 40.
y2 = 54.

m = Basemap(resolution='i',projection='merc', llcrnrlat=y1,urcrnrlat=y2,llcrnrlon=x1,urcrnrlon=x2,lat_ts=(x1+x2)/2)
m.drawcountries(linewidth=0.5)
m.drawcoastlines(linewidth=0.5)
if False:
    # provoque l'erreur
    # ValueError: All values in the dash list must be positive
    m.drawparallels(np.arange(y1,y2,2.),labels=[1,0,0,0],color='black',
                    dashes=[1,0],labelstyle='+/-',linewidth=0.2) # draw parallels
    m.drawmeridians(np.arange(x1,x2,2.),labels=[0,0,0,1],color='black',
                    dashes=[1,0],labelstyle='+/-',linewidth=0.2) # draw meridians

from matplotlib.collections import LineCollection
from matplotlib import cm
import shapefile

shp = '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'
r = shapefile.Reader(shp)
shapes = r.shapes()
records = r.records()

for record, shape in zip(records,shapes):
    # les coordonnées sont en Lambert 93
    geo_points = [lambert932WGPS(x,y) for x, y in shape.points]
    lons = [_[0] for _ in geo_points]
    lats = [_[1] for _ in geo_points]
    data = np.array(m(lons, lats)).T

    if len(shape.parts) == 1:
        segs = [data,]
    else:
        segs = []
        for i in range(1,len(shape.parts)):
            index = shape.parts[i-1]
            index2 = shape.parts[i]
            segs.append(data[index:index2])
        segs.append(data[index2:])

    lines = LineCollection(segs,antialiaseds=(1,))
    # pour changer les couleurs c'est ici, il faudra utiliser le champ records
    # pour les changer en fonction du nom du départements
    lines.set_facecolors(cm.jet(np.random.rand(1)))
    lines.set_edgecolors('k')
    lines.set_linewidth(0.1)
    ax.add_collection(lines)
../_images/seance6_graphes_enonce_62_0.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.

import seaborn
seaborn.pairplot(data_elections)
<seaborn.axisgrid.PairGrid at 0x1e285311eb8>
../_images/seance6_graphes_enonce_66_1.png

Celui-ci est aussi intéressant : clustermap pour étudier les corrélations.

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)
<seaborn.matrix.ClusterGrid at 0x1e2853112b0>
../_images/seance6_graphes_enonce_68_1.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.

from bokeh.plotting import output_notebook
output_notebook()
Loading BokehJS ...

premier graphe

On utilise bokeh pour un simple graphique XY. on peut choisir les différentes options interactives, zoom ... Voir tools.

from bokeh.plotting import figure, show

p = figure(title = "élections")
p.title = "é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], "g--")
p.xaxis.axis_label = "tour 1"
p.yaxis.axis_label = "tour 2"
show(p)

<Bokeh Notebook handle for In[126]>

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.

from bokeh.plotting import figure, show

p = figure(title = "élections")
p.title = "é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], "g--")
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)