Données spatiales dans Python : GeoPandas¶

< Page précédente | Sommaire | | Retour Site | Page suivante >

Pour traiter de l'information géographique, il convient de maitriser des outils et des méthodes particulières. En termes d'outils, les SIG permettent surtout de rassembler dans un même outil des formats géographiques variés. Pour charger ces formats dans un environnement Python, il conviendra donc d'utiliser des bibliothèques dédiées comme GeoPandas. GeoPandas repose notamment sur la bibliothèque Pandas qui n'est pas spécifique aux données spatiales. Pandas est directement accessible depuis l'environnement Anaconda de base. Concernant GeoPandas, il faudra l'installer.

Les données pour la partie "Traitement de données avec Pandas" reposent sur un fichier CSV produit avec Excel. Pour la suite, on utilisera le shapefile Departements de ce dossier zippé.

Taitement de données avec Pandas¶

La bibliothèque Pandas est très utilisée pour manipuler des données avec Python. Ses fonctions read_* permettent par exemple d'ouvrir simplement de nombreux types de fichiers dans Python. Il suffira la plupart du temps de préciser l'adresse du fichier. Ici notre fichier CSV utilise des ; pour séparer les colonnes de notre tableau, c'est pourquoi on utilise l'argument sep.

In [1]:
import pandas as pd
df = pd.read_csv('C:/Users/Serge/Desktop/TPR.csv',sep =';')

La bibliothèque Pandas propose de nombreuses fonctions pratiques pour visualiser les données comme la fonction head(), la fonction describe() ou la fonction columns(). La fonction sort_values() permet aussi de simplement trier le tableau en fonction des valeurs d'une colonne.

In [2]:
df.shape
Out[2]:
(96, 10)
In [3]:
df.head()
Out[3]:
ID NOM Agriculteur Artisan Commercant Cadre Profession Intermediaire Employe Ouvrier Retraite Autres
0 01 Ain 4067 16745 36426 70663 77349 78998 30417 29910
1 02 Aisne 5201 11414 18629 48889 71578 78906 32063 42108
2 03 Allier 6159 9396 11842 31102 46196 42101 24218 21344
3 04 Alpes-de-Haute-Provence 2027 6387 6951 16140 20885 15560 10687 10278
4 05 Hautes-Alpes 2040 5286 5883 15687 19707 12415 8865 7222
In [4]:
df.describe()
Out[4]:
Agriculteur Artisan Commercant Cadre Profession Intermediaire Employe Ouvrier Retraite Autres
count 96.000000 96.000000 96.000000 96.000000 96.000000 96.000000 96.000000 96.000000
mean 5167.927083 16791.958333 44756.697917 72467.885417 87531.510417 71985.291667 36122.697917 38277.260417
std 2880.807943 11351.777258 66336.081640 61831.768172 66873.834456 49306.632356 22541.444178 32808.739639
min 106.000000 2680.000000 2670.000000 7431.000000 10310.000000 7209.000000 5195.000000 4535.000000
25% 3336.750000 8060.750000 10946.500000 27883.000000 36901.250000 39219.500000 18981.250000 16427.750000
50% 4752.500000 13935.000000 23069.500000 53551.500000 70585.500000 64643.000000 31231.500000 27480.500000
75% 6588.750000 23142.250000 46584.500000 94845.000000 113523.500000 94012.250000 50156.250000 48593.000000
max 12517.000000 55687.000000 503432.000000 281989.000000 329242.000000 319098.000000 135770.000000 199332.000000
In [5]:
print(df.columns)
Index(['ID', 'NOM', 'Agriculteur', 'Artisan Commercant', 'Cadre',
       'Profession Intermediaire', 'Employe', 'Ouvrier', 'Retraite', 'Autres'],
      dtype='object')
In [6]:
df.sort_values(by='Cadre',ascending=False)
Out[6]:
ID NOM Agriculteur Artisan Commercant Cadre Profession Intermediaire Employe Ouvrier Retraite Autres
75 75 Paris 520 55687 503432 281989 252536 96986 67814 111042
92 92 Hauts-de-Seine 379 34216 277629 203704 201237 82504 52406 69392
78 78 Yvelines 1139 28397 195091 189244 181152 95537 60583 72112
69 69 Rhone 6642 42359 156210 216844 217093 157509 76906 89749
59 59 Nord 7763 45594 147874 273832 329242 319098 135770 199332
... ... ... ... ... ... ... ... ... ... ...
4 05 Hautes-Alpes 2040 5286 5883 15687 19707 12415 8865 7222
8 09 Ariege 2348 4888 5537 14442 20788 16635 10379 9741
14 15 Cantal 7431 4976 4547 12517 19508 17183 10386 7786
21 21 Cote-dOr 5259 3696 3669 9387 16039 12764 9811 6780
48 48 Lozere 3381 2706 2670 7431 10310 7209 5195 4535

96 rows × 10 columns

Ces fonctions s'appliquent à des dataframes (l'objet créé en utilisant les fonctions read_*). Ces dataframes s'utilisent comme ci-dessous :

In [7]:
cadre = df['Cadre'] # Récupère les valeurs d'une colonne
cadre_proint = df[['Cadre','Profession Intermediaire']] # Récupère les valeurs de plusieurs colonnes
df[df.columns[1]] # Si on ne connait pas les noms des colonnes on peut utiliser les index des colonnnes
Out[7]:
0                         Ain
1                       Aisne
2                      Allier
3     Alpes-de-Haute-Provence
4                Hautes-Alpes
               ...           
91                    Essonne
92             Hauts-de-Seine
93          Seine-Saint-Denis
94               Val-de-Marne
95                  Val-dOise
Name: NOM, Length: 96, dtype: object

Pandas permet aussi d'avoir recours simplement à des fonctions permettant d'effectuer des graphiques.

In [8]:
df.plot.scatter(x='Employe', y='Profession Intermediaire')
Out[8]:
<Axes: xlabel='Employe', ylabel='Profession Intermediaire'>
No description has been provided for this image
In [9]:
cadre_proint.plot.box()
Out[9]:
<Axes: >
No description has been provided for this image

Pour filtrer les valeurs, il convient d'utiliser des tests comme ci-dessous :

In [10]:
 df[df["Cadre"] > 200000]
Out[10]:
ID NOM Agriculteur Artisan Commercant Cadre Profession Intermediaire Employe Ouvrier Retraite Autres
75 75 Paris 520 55687 503432 281989 252536 96986 67814 111042
92 92 Hauts-de-Seine 379 34216 277629 203704 201237 82504 52406 69392
In [11]:
 df[df["NOM"] == 'Val-de-Marne']
Out[11]:
ID NOM Agriculteur Artisan Commercant Cadre Profession Intermediaire Employe Ouvrier Retraite Autres
94 94 Val-de-Marne 191 29593 146443 177963 200237 102786 51540 66572

A noter que la fonction iloc() permet de travailler à partir des index :

In [12]:
df.iloc[94,:]
Out[12]:
ID                                    94
NOM                         Val-de-Marne
Agriculteur                          191
Artisan Commercant                 29593
Cadre                             146443
Profession Intermediaire          177963
Employe                           200237
Ouvrier                           102786
Retraite                           51540
Autres                             66572
Name: 94, dtype: object
In [13]:
df.iloc[94,4]
Out[13]:
146443
In [14]:
sum(df.iloc[94,2:10])
Out[14]:
775325

Pour généraliser le dernier calcul, on pourra utiliser la fonction apply(). On pourra alors simplement créer une nouvelle colonne "Actif". Attention, par défaut, apply() s'applique au niveau des colonnes. Pour effectuer le calcul par ligne, il faudra utiliser l'argument axis.

In [15]:
df.iloc[:,2:10].apply(sum)
Out[15]:
Agriculteur                  496121
Artisan Commercant          1612028
Cadre                       4296643
Profession Intermediaire    6956917
Employe                     8403025
Ouvrier                     6910588
Retraite                    3467779
Autres                      3674617
dtype: int64
In [16]:
df['Actif'] = df.iloc[:,2:10].apply(sum,axis=1)
df.head()
Out[16]:
ID NOM Agriculteur Artisan Commercant Cadre Profession Intermediaire Employe Ouvrier Retraite Autres Actif
0 01 Ain 4067 16745 36426 70663 77349 78998 30417 29910 344575
1 02 Aisne 5201 11414 18629 48889 71578 78906 32063 42108 308788
2 03 Allier 6159 9396 11842 31102 46196 42101 24218 21344 192358
3 04 Alpes-de-Haute-Provence 2027 6387 6951 16140 20885 15560 10687 10278 88915
4 05 Hautes-Alpes 2040 5286 5883 15687 19707 12415 8865 7222 77105

Enfin, la fonction to_numpy() permet de repasser en array.

In [17]:
adf = df.to_numpy()

Pandas propose plein d'autres fonctions très pertinentes. Je pense notamment à la fonction groupby() pour faire des regroupements attributaires ou la fonction merge() qui permet des jointures attributaires. Cela sera présenté dans la partie "Manipulations de la table attributaire", car les fonctions Pandas fonctionnent pour GeoPandas.

Installer GeoPandas et produire ses premières cartes¶

Pour installer la bibliothèque GeoPandas, qui n'est pas installée par défaut, c'est en théorie assez simple. Il est possible d'utiliser l'Anaconda Powershell Prompt ou l'Anaconda Prompt. Il suffit alors de rentrer l'une des deux lignes de codes ci-dessous.

Attention, n'exécutez pas immédiatement les lignes de codes ci-dessous et lisez le paragraphe suivant.

In [ ]:
conda install geopandas
conda install --channel conda-forge geopandas

Néanmoins, pour traiter des données géographiques, il peut être pertinent d'installer une autre bibliothèque nommée PySAL, ce qui est notamment nécessaire dans un autre notebook. Or PySAL dépend justement de GeoPandas pour fonctionner. PySAL peut alors nécessiter une autre version que celle installée précédemment et ça peut poser problème. De plus, que ce soit GeoPandas et PySAL, ces bibliothèques reposent sur des bibliothèques Python, mais aussi sur des bibliothèques codées en C (autre langage de programmation) comme GEOS, GDAL, PROJ et ces bibliothèques ne sont pas si simples à installer et à faire fonctionner avec Python. Plus généralement, des difficultés apparaissent souvent dès que l'on commence à installer de nombreuses bibliothèques Python. Par conséquent, et par expérience, lorsque l'on connait les bibliothèques que l'on va être amené à utiliser, je conseille de commencer par installer les bibliothèques les plus spécialisées, c'est-à-dire celles qui reposent sur de nombreuses dépendances de telle sorte à ce qu'Anaconda traite dès le début cette problématique. Ici, je conseille donc d'installer PySAL en premier et l'installation de Geopandas se fera alors dans ce cadre. Il faut alors plutôt taper la ligne de code suivante pour installer GeoPandas :

In [ ]:
conda install --channel conda-forge pysal

En cas d'échec, il est possible d'utiliser PIP :

In [ ]:
pip install pysal

Dès lors, il est possible d'importer dans Spyder la bibliothèque GeoPandas et d'utiliser sa fonction read_file(), qui permet de charger de nombreux formats de fichiers géographiques (geojson, shp...). Combinée à sa fonction plot(), cela vous permettra de produire simplement votre première carte.

In [18]:
import geopandas as gpd
gdf = gpd.read_file('C:/Users/Serge/Desktop/Donnees/Departements.shp')
gdf.plot()
Out[18]:
<Axes: >
No description has been provided for this image

Différents aspects cartographiques sont personnalisables à l'aide des arguments. Par exemple, la gestion des couleurs passe bien entendu par l'argument color, mais aussi par l'argument column qui permet de créer des dégradés de couleurs en fonction des valeurs de cette colonne. Pour utiliser cet argument, il faut donc connaitre la table attributaire et les noms de colonnes. Pour cela, il est possible d'utiliser les mêmes fonctions que Pandas. En effet, read_file() crée un objet de type geodataframe qui ne diffère des dataframes Pandas que par l'existence d'une colonne geometry.

In [19]:
print(type(gdf))
<class 'geopandas.geodataframe.GeoDataFrame'>
In [20]:
gdf.head()
Out[20]:
ID_GEOFLA CODE_DEPT NOM_DEPT NOM_CHF CODE_REG NOM_REGION Pop_hommes Pop_femmes Pop_Etrang Pop_totale Naissances Deces Solde_nat Solde_mig Tx_Etrange Superfice geometry
0 1 01 AIN BOURG-EN-BRESSE 82 RHONE-ALPES 287336 294023 45109 581355 61000 37114 23886 41991 0.0776 5784.556 POLYGON ((919195.047 6541469.120, 918932.086 6...
1 2 02 AISNE LAON 22 PICARDIE 262006 276794 13447 538790 62669 48087 14582 -11105 0.0250 7419.598 POLYGON ((735604.502 6861427.402, 735235.539 6...
2 3 03 ALLIER MOULINS 83 AUVERGNE 164392 178416 9394 342807 30652 38838 -8186 6378 0.0274 7380.145 POLYGON ((753768.890 6537042.440, 753553.839 6...
3 4 04 ALPES-DE-HAUTE-PROVENCE DIGNE-LES-BAINS 93 PROVENCE-ALPES-COTE-D'AZUR 76580 81387 6388 157965 13741 13972 -231 18513 0.0404 6995.771 POLYGON ((992637.810 6305621.223, 992262.815 6...
4 5 05 HAUTES-ALPES GAP 93 PROVENCE-ALPES-COTE-D'AZUR 65494 68714 4192 134205 12882 10793 2089 10485 0.0312 5689.465 POLYGON ((1012912.430 6402903.638, 1012576.422...
In [21]:
print(gdf.columns)
Index(['ID_GEOFLA', 'CODE_DEPT', 'NOM_DEPT', 'NOM_CHF', 'CODE_REG',
       'NOM_REGION', 'Pop_hommes', 'Pop_femmes', 'Pop_Etrang', 'Pop_totale',
       'Naissances', 'Deces', 'Solde_nat', 'Solde_mig', 'Tx_Etrange',
       'Superfice', 'geometry'],
      dtype='object')
In [22]:
gdf.plot(color='red')
Out[22]:
<Axes: >
No description has been provided for this image
In [23]:
gdf.plot(column='Tx_Etrange', legend=True)
Out[23]:
<Axes: >
No description has been provided for this image

L'argument cmap permet de choisir son dégradé de couleurs.

In [24]:
gdf.plot(column='Tx_Etrange', cmap='OrRd', legend=True, legend_kwds={"label": "Tx Etranger", "orientation": "horizontal"})
Out[24]:
<Axes: >
No description has been provided for this image

Néanmoins, pour créer une carte en aplat de couleurs discrétisée, il faut utiliser la bibliothèque mapclassify. Cette bibliothèque mapclassify n'est pas installé par défaut avec GeoPandas. Elle dépend en fait de PySAL. C'est un argument supplémentaire pour installer PySAL.

In [25]:
import mapclassify
gdf.plot(column='Tx_Etrange', cmap='OrRd', scheme='Quantiles', k = 4, legend=True)
Out[25]:
<Axes: >
No description has been provided for this image

Enfin, l'argument column peut s'adapter à des variables qualitatives discrètes en associant l'argument cmap à une palette de couleurs adaptée à des valeurs discrètes : tab10 et tab20 par exemple.

In [26]:
gdf.plot(cmap='tab10')
gdf.plot(column='NOM_REGION', cmap='tab20')
Out[26]:
<Axes: >
No description has been provided for this image
No description has been provided for this image

Manipulations de la table attributaire¶

Pour manipuler le tableau géographique (la table attributaire), on pourra donc fonctionner avec les geodataframes comme avec les dataframes. Ainsi, pour récupérer les données des colonnes, les combiner ou les afficher dans un graphique, on pourra fonctionner comme cela.

In [27]:
gdf['Pop_hommes']
print(sum(gdf['Pop_hommes']))
gdf['Pop_hommes'] + gdf['Pop_femmes'] 
gdf[['NOM_DEPT','Pop_hommes','Pop_femmes']].head() 
30088079
Out[27]:
NOM_DEPT Pop_hommes Pop_femmes
0 AIN 287336 294023
1 AISNE 262006 276794
2 ALLIER 164392 178416
3 ALPES-DE-HAUTE-PROVENCE 76580 81387
4 HAUTES-ALPES 65494 68714
In [28]:
gdf.plot.scatter(x='Pop_hommes', y='Pop_femmes')
Out[28]:
<Axes: xlabel='Pop_hommes', ylabel='Pop_femmes'>
No description has been provided for this image

Si on souhaite utiliser les index, on récupère les lignes du tableau, c'est à dire les objets géographiques. Si on souhaite utiliser les index pour les lignes et les colonnes, on peut utiliser la fonction iloc().

In [29]:
gdf[0:3]
gdf[0:3]['Pop_hommes']
Out[29]:
0    287336
1    262006
2    164392
Name: Pop_hommes, dtype: int64
In [30]:
gdf.iloc[3:5, 6:8]
Out[30]:
Pop_hommes Pop_femmes
3 76580 81387
4 65494 68714

Pour récupérer un objet géographique, ou plus généralement faire une sélection attributaire, on pourra passer par les tests :

In [31]:
gdf[gdf['NOM_DEPT'] == 'PARIS']
Out[31]:
ID_GEOFLA CODE_DEPT NOM_DEPT NOM_CHF CODE_REG NOM_REGION Pop_hommes Pop_femmes Pop_Etrang Pop_totale Naissances Deces Solde_nat Solde_mig Tx_Etrange Superfice geometry
75 76 75 PARIS PARIS--1ER-ARRONDISSEMENT 11 ILE-DE-FRANCE 1042203 1169094 329922 2211297 284597 138230 146367 -60921 0.1492 105.365 POLYGON ((657172.410 6861181.210, 657135.406 6...
In [32]:
gdf[gdf['NOM_REGION'] == 'RHONE-ALPES']['NOM_DEPT']
Out[32]:
0              AIN
6          ARDECHE
24           DROME
38           ISERE
42           LOIRE
69           RHONE
73          SAVOIE
74    HAUTE-SAVOIE
Name: NOM_DEPT, dtype: object
In [33]:
gdf[gdf['NOM_REGION'] == 'RHONE-ALPES'].plot()
Out[33]:
<Axes: >
No description has been provided for this image
In [34]:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
gdf.plot(ax=ax, color='white', edgecolor='black')
gdf[gdf['NOM_REGION'] == 'RHONE-ALPES'].plot(ax=ax)
plt.show()
No description has been provided for this image

Bien entendu, les tests (et donc les sélections attributaires) peuvent s'effectuer sur des variables quantitatives :

In [35]:
gdf['Tx_Etrange'] > 0.1
gdf[gdf['Tx_Etrange'] > 0.1]
gdf[gdf['Tx_Etrange'] > 0.1]['NOM_DEPT']
Out[35]:
21               CREUSE
28         CORSE-DU-SUD
75                PARIS
92       HAUTS-DE-SEINE
93    SEINE-SAINT-DENIS
94         VAL-DE-MARNE
95           VAL-D'OISE
Name: NOM_DEPT, dtype: object

On peut aussi effectuer des tests plus complexes avec des connecteurs logiques ou des fonctions pratiques comme isin(). On peut ainsi reconstituer la région RHONE-ALPES-AUVERGNE.

In [36]:
rho_auv = gdf[gdf['NOM_REGION'].isin(['RHONE-ALPES','AUVERGNE'])]
rho_auv['NOM_DEPT']
Out[36]:
0              AIN
2           ALLIER
6          ARDECHE
14          CANTAL
24           DROME
38           ISERE
42           LOIRE
43     HAUTE-LOIRE
63     PUY-DE-DOME
69           RHONE
73          SAVOIE
74    HAUTE-SAVOIE
Name: NOM_DEPT, dtype: object
In [37]:
rho_auv2 = gdf[(gdf['NOM_REGION'] == 'RHONE-ALPES') | (gdf['NOM_REGION'] == 'AUVERGNE')]
rho_auv2['NOM_DEPT']
Out[37]:
0              AIN
2           ALLIER
6          ARDECHE
14          CANTAL
24           DROME
38           ISERE
42           LOIRE
43     HAUTE-LOIRE
63     PUY-DE-DOME
69           RHONE
73          SAVOIE
74    HAUTE-SAVOIE
Name: NOM_DEPT, dtype: object
In [38]:
txetrang_peup = gdf[(gdf['Tx_Etrange'] > 0.1) & (gdf['Pop_totale'] > 1000000)]
txetrang_peup['NOM_DEPT']
Out[38]:
75                PARIS
92       HAUTS-DE-SEINE
93    SEINE-SAINT-DENIS
94         VAL-DE-MARNE
95           VAL-D'OISE
Name: NOM_DEPT, dtype: object

La fonction groupby() permet d'effectuer des regroupements attributaires. On peut aussi utiliser la fonction value_counts() pour compter les objets géographique ayant des valeurs communes.

In [39]:
gr = gdf[['Pop_totale','NOM_REGION']].groupby(['NOM_REGION']).sum()
gr.head()
Out[39]:
Pop_totale
NOM_REGION
ALSACE 1837087
AQUITAINE 3177625
AUVERGNE 1341863
BASSE-NORMANDIE 1467425
BOURGOGNE 1638588
In [40]:
gdf['NOM_REGION'].value_counts().head()
Out[40]:
NOM_REGION
RHONE-ALPES                   8
MIDI-PYRENEES                 8
ILE-DE-FRANCE                 8
PROVENCE-ALPES-COTE-D'AZUR    6
CENTRE                        6
Name: count, dtype: int64

Enfin, une des manipulations attributaires les plus précieuses, la jointure attributaire permettant d'enrichir les tableaux d'information géographique, est possible avec la fonction merge(). Ici, on va joindre nos données du shapefile (gdf) avec celle de notre csv (df) à l'aide des champs CODE_DEPT et ID qui sont communs aux deux tableaux.

In [41]:
gdf2 = gdf.merge(df, left_on='CODE_DEPT',right_on='ID')
gdf2.head()
Out[41]:
ID_GEOFLA CODE_DEPT NOM_DEPT NOM_CHF CODE_REG NOM_REGION Pop_hommes Pop_femmes Pop_Etrang Pop_totale ... NOM Agriculteur Artisan Commercant Cadre Profession Intermediaire Employe Ouvrier Retraite Autres Actif
0 1 01 AIN BOURG-EN-BRESSE 82 RHONE-ALPES 287336 294023 45109 581355 ... Ain 4067 16745 36426 70663 77349 78998 30417 29910 344575
1 2 02 AISNE LAON 22 PICARDIE 262006 276794 13447 538790 ... Aisne 5201 11414 18629 48889 71578 78906 32063 42108 308788
2 3 03 ALLIER MOULINS 83 AUVERGNE 164392 178416 9394 342807 ... Allier 6159 9396 11842 31102 46196 42101 24218 21344 192358
3 4 04 ALPES-DE-HAUTE-PROVENCE DIGNE-LES-BAINS 93 PROVENCE-ALPES-COTE-D'AZUR 76580 81387 6388 157965 ... Alpes-de-Haute-Provence 2027 6387 6951 16140 20885 15560 10687 10278 88915
4 5 05 HAUTES-ALPES GAP 93 PROVENCE-ALPES-COTE-D'AZUR 65494 68714 4192 134205 ... Hautes-Alpes 2040 5286 5883 15687 19707 12415 8865 7222 77105

5 rows × 28 columns

Réaliser des traitements spatiaux avec GeoPandas¶

La bibliothèque GeoPandas va surtout être utile pour traiter de l'information géographique, car elle permet de réaliser des géotraitements et d'appeler des algorithmes permettant de calculer des superficies (area), des longueurs (length), des distances et des centroides (centroid).

Attention, ces fonctions requièrent des coordonnées planes.

In [42]:
a = gdf.area
print('Superficie ' + str(gdf['NOM_DEPT'][0]) + ' = ' + str(a[0] / 1000000) + ' km2')
l = gdf.length
print('Périmetre ' + str(gdf['NOM_DEPT'][0]) + ' = ' + str(l[0] / 1000) + ' km')
geom_ain = gdf[gdf['NOM_DEPT'] == 'AIN']['geometry']
geom_aisne = gdf[gdf['NOM_DEPT'] == 'AISNE']['geometry']
dist = geom_ain.distance(geom_aisne,align=False)
print('Distance Ain-Aisne ' + str(dist[0] / 1000) + ' km' )
b = gdf.centroid
b.plot()
Superficie AIN = 5773.959872775968 km2
Périmetre AIN = 477.70417195337177 km
Distance Ain-Aisne 280.9775357285964 km
Out[42]:
<Axes: >
No description has been provided for this image

Il est possible d'utiliser les centroides pour créer des cartes avec des symboles proportionnels.

In [44]:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
gdf.plot(ax=ax, color='white', edgecolor='black', linewidth=0.5)
sizes = gdf['Pop_totale'] / 100000
b.plot(ax=ax, marker='o', color='red', markersize=sizes)
plt.show()
No description has been provided for this image

Bien entendu, Geopandas permet de calculer des buffers, ici autour des centroides.

In [45]:
c = b.buffer(20000)
fig, ax = plt.subplots()
gdf.plot(ax=ax, color='white', edgecolor='grey', linewidth=0.5)
c.plot(ax=ax, color='grey',  edgecolor='black', linewidth=0.5)
b.plot(ax=ax, marker='o', color='blue', markersize=4)
plt.show()
No description has been provided for this image

A l'aide de la fonction overlay, il est possible de réaliser des intersections, des unions et des différences. Cette fonction nécessite deux geodataframes, or la fonction buffer() crée des GeoSeries. Il faut donc transformer les buffers en geodataframe avant de les intersecter aux départements.

In [46]:
c = b.buffer(40000)
cgdf = gpd.GeoDataFrame(c)
cgdf = cgdf.set_geometry(0)
In [47]:
cgdf['NOM'] = gdf['NOM_DEPT']
inter = gdf.overlay(cgdf, how='intersection')
inter.plot()
inter['NOM'].value_counts()
Out[47]:
NOM
YVELINES                10
VAL-D'OISE              10
ESSONNE                  9
VAL-DE-MARNE             9
SEINE-SAINT-DENIS        9
                        ..
HAUTE-CORSE              2
VENDEE                   2
PYRENEES-ATLANTIQUES     2
DORDOGNE                 2
LANDES                   1
Name: count, Length: 96, dtype: int64
No description has been provided for this image

Pour les regroupements, il faut utiliser la fonction dissolve() :

In [48]:
reg = gdf.dissolve(by='NOM_REGION') #Reroupement
reg.plot(edgecolor='white', linewidth=0.5)
Out[48]:
<Axes: >
No description has been provided for this image
In [49]:
reg.head()
Out[49]:
geometry ID_GEOFLA CODE_DEPT NOM_DEPT NOM_CHF CODE_REG Pop_hommes Pop_femmes Pop_Etrang Pop_totale Naissances Deces Solde_nat Solde_mig Tx_Etrange Superfice
NOM_REGION
ALSACE POLYGON ((1040403.562 6789657.178, 1040676.601... 68 67 BAS-RHIN STRASBOURG 42 531185 559832 78393 1091015 118888 73112 45776 19216 0.0719 4794.081
AQUITAINE MULTIPOLYGON (((427834.547 6198552.139, 427106... 23 24 DORDOGNE PERIGUEUX 72 196699 212696 11639 409388 33062 44924 -11862 32865 0.0284 9223.041
AUVERGNE POLYGON ((634691.847 6393269.982, 634356.813 6... 3 03 ALLIER MOULINS 83 164392 178416 9394 342807 30652 38838 -8186 6378 0.0274 7380.145
BASSE-NORMANDIE MULTIPOLYGON (((533473.967 6789000.545, 533191... 14 14 CALVADOS CAEN 25 326305 351904 12898 678206 74824 51028 23796 6111 0.0190 5604.691
BOURGOGNE POLYGON ((805986.882 6568662.419, 805127.846 6... 20 21 COTE-D'OR DIJON 26 251747 269866 10896 521608 54190 39748 14442 157 0.0209 8801.267

L'argument aggfunc permet de choisir des fonctions d'agrégation à appliquer à tous les champs.

In [50]:
gdf.dissolve(by='NOM_REGION', aggfunc='sum').head()
Out[50]:
geometry ID_GEOFLA CODE_DEPT NOM_DEPT NOM_CHF CODE_REG Pop_hommes Pop_femmes Pop_Etrang Pop_totale Naissances Deces Solde_nat Solde_mig Tx_Etrange Superfice
NOM_REGION
ALSACE POLYGON ((1040403.562 6789657.178, 1040676.601... 6869 6768 BAS-RHINHAUT-RHIN STRASBOURGCOLMAR 4242 897015 940078 139481 1837087 200406 125462 74944 28411 0.1538 8326.383
AQUITAINE MULTIPOLYGON (((427834.547 6198552.139, 427106... 2334414865 2433404764 DORDOGNEGIRONDELANDESLOT-ET-GARONNEPYRENEES-AT... PERIGUEUXBORDEAUXMONT-DE-MARSANAGENPAU 7272727272 1528017 1649629 122177 3177625 295132 272356 22776 245896 0.1879 41798.243
AUVERGNE POLYGON ((634691.847 6393269.982, 634356.813 6... 3154464 03154363 ALLIERCANTALHAUTE-LOIREPUY-DE-DOME MOULINSAURILLACLE PUY-EN-VELAYCLERMONT-FERRAND 83838383 649618 692256 43644 1341863 124443 130164 -5721 38928 0.1049 26174.396
BASSE-NORMANDIE MULTIPOLYGON (((533473.967 6789000.545, 533191... 145162 145061 CALVADOSMANCHEORNE CAENSAINT-LOALENCON 252525 710862 756580 27432 1467425 155150 122193 32957 12032 0.0588 17753.150
BOURGOGNE POLYGON ((805986.882 6568662.419, 805127.846 6... 20597290 21587189 COTE-D'ORNIEVRESAONE-ET-LOIREYONNE DIJONNEVERSMACONAUXERRE 26262626 793427 845175 54237 1638588 160843 155706 5137 23044 0.1314 31749.352
In [51]:
dep_pop = gdf[['NOM_REGION', 'geometry', 'Pop_totale']]
reg_pop = dep_pop.dissolve(by='NOM_REGION', aggfunc='sum')
reg_pop2 = dep_pop.dissolve(by='NOM_REGION', aggfunc={"Pop_totale": ["min", "max"]})
reg_popdef = gdf.dissolve(by='NOM_REGION', aggfunc={"Pop_hommes": "sum","Pop_femmes": "sum","Pop_totale": "sum",'NOM_REGION':"first"})
reg_pop.head()
Out[51]:
geometry Pop_totale
NOM_REGION
ALSACE POLYGON ((1040403.562 6789657.178, 1040676.601... 1837087
AQUITAINE MULTIPOLYGON (((427834.547 6198552.139, 427106... 3177625
AUVERGNE POLYGON ((634691.847 6393269.982, 634356.813 6... 1341863
BASSE-NORMANDIE MULTIPOLYGON (((533473.967 6789000.545, 533191... 1467425
BOURGOGNE POLYGON ((805986.882 6568662.419, 805127.846 6... 1638588
In [52]:
reg_pop2.head()
Out[52]:
geometry (Pop_totale, min) (Pop_totale, max)
NOM_REGION
ALSACE POLYGON ((1040403.562 6789657.178, 1040676.601... 746072 1091015
AQUITAINE MULTIPOLYGON (((427834.547 6198552.139, 427106... 326399 1421276
AUVERGNE POLYGON ((634691.847 6393269.982, 634356.813 6... 148737 628485
BASSE-NORMANDIE MULTIPOLYGON (((533473.967 6789000.545, 533191... 292282 678206
BOURGOGNE POLYGON ((805986.882 6568662.419, 805127.846 6... 220653 553968
In [53]:
reg_popdef.head()
Out[53]:
geometry Pop_hommes Pop_femmes Pop_totale NOM_REGION
NOM_REGION
ALSACE POLYGON ((1040403.562 6789657.178, 1040676.601... 897015 940078 1837087 ALSACE
AQUITAINE MULTIPOLYGON (((427834.547 6198552.139, 427106... 1528017 1649629 3177625 AQUITAINE
AUVERGNE POLYGON ((634691.847 6393269.982, 634356.813 6... 649618 692256 1341863 AUVERGNE
BASSE-NORMANDIE MULTIPOLYGON (((533473.967 6789000.545, 533191... 710862 756580 1467425 BASSE-NORMANDIE
BOURGOGNE POLYGON ((805986.882 6568662.419, 805127.846 6... 793427 845175 1638588 BOURGOGNE

Enfin, il est possible d'effectuer des jointures spatiales à l'aide de la fonction sjoin(). L'argument predicate permet de préciser la liaison. Ici, on va se servir de la région Ile-de-France pour récupérer les centroides des départements de la région Ile-de-France. Pour effectuer cette jointure, il faut deux geodataframes, or la fonction centroid() crée des GeoSeries. Il faut donc transformer les centroides en geodataframe avant de les intersecter aux départements de l'Ile-de-France.

In [54]:
idf = reg[reg.index=='ILE-DE-FRANCE']
In [55]:
cen = gpd.GeoDataFrame(b)
cen = cen.set_geometry(0)
In [56]:
centroid_idf = cen.sjoin(idf, predicate='intersects') 
fig, ax = plt.subplots()
idf.plot(ax=ax, color='white', edgecolor='black')
centroid_idf.plot(ax=ax, marker='o', markersize=20)
plt.show()
No description has been provided for this image

Bonus : première carte interactive avec Python¶

Dans cette partie, je vais exploiter le potentiel de MapClassify et de ipywidgets pour créer des cartes interactives dans Jupyter. Cette partie ne fonctionne pas avec Spyder.

On l'a vu plus haut, mapclassify permet de réaliser facilement des cartes thématiques en utilisant l'argument scheme dans un plot(). On peut alors chercher à faire varier les paramètres de ce plot() pour obtenir différentes représentations (notamment ce paramètre scheme pour appeler d'autres méthodes de discrétisation). Pour le faire dynamiquement, on peut utiliser la bibliothèque ipywidgets qui permet d'insérer dans les cellules Jupyter des widgets comme des listes déroulantes. Ensuite, il faut simplement faire communiquer ces widgets avec les paramètres de notre plot().

In [ ]:
import geopandas as gpd
import mapclassify as mc
import ipywidgets as widgets
from IPython.display import display, clear_output
import matplotlib.pyplot as plt

# Charger les données des départements français
gd = gpd.read_file('C:/Users/Serge/Desktop/Donnees/Departements.shp')

# Liste des méthodes de discrétisation disponibles
methods = ["Quantiles", "EqualInterval", "NaturalBreaks"]

# Création du widget pour choisir la méthode de discrétisation
dropdown = widgets.Dropdown(
    options=methods,
    value="Quantiles",
    description="Méthode :",
    style={'description_width': 'initial'}
)

# Zone de sortie pour afficher la carte
output = widgets.Output()

# Fonction pour mettre à jour la carte
def update_map(change):
    with output:
        clear_output(wait=True)  # Effacer l'ancienne carte
        ax = gd.plot(column='Tx_Etrange', cmap='OrRd', scheme=dropdown.value, k = 4, legend=True)
        ax.set_axis_off()
        plt.show()

# Lier la mise à jour au changement de sélection
dropdown.observe(update_map, names="value")

# Afficher les widgets et la carte initiale
display(dropdown,output)
update_map(None)

Ci-dessous, une vidéo pour visualiser le résultat. La méthode des seuils naturels est un peu longue. A noter, que ipywidgets doit être installé et bien souvent mis à jour : pip install --upgrade ipywidgets.

On peut alors faire varier l'aplat de couleurs, le nombre de classes...

< Page précédente | Sommaire | | Retour Site | Page suivante >