Tech - carte#

Links: notebook, html, python, slides, GitHub

Faire une carte, c’est toujours compliqué. C’est simple jusqu’à ce qu’on s’aperçoive qu’on doit récupérer la description des zones administratives d’un pays, fournies parfois dans des coordonnées autres que longitude et latitude. Quelques modules utiles :

  • cartopy : surcouche de matplotlib pour faire des dessins avec des coordonnées géographiques

  • bokeh : pour tracer des cartes interactives

  • pyproj : conversion entre systèmes de coordonnées

  • shapely : manipuler des polygones géographiques (union, intersection, …)

  • pyshp : lire ou écrire des polygones géographiques

  • geopandas : manipulation de dataframe avec des coordonnées géographiques

Quelques notebooks intéressants : * Tracer une carte en Python avec bokeh * Tracer une carte en Python * Données carroyées et OpenStreetMap * Carte de France avec les départements * Carte de France avec les départements (2)

from jyquickhelper import add_notebook_menu
add_notebook_menu()
%matplotlib inline

Exposé#

On télécharge des données hospitalières par départements.

Données COVID#

# https://www.data.gouv.fr/fr/datasets/donnees-hospitalieres-relatives-a-lepidemie-de-covid-19/
from pandas import read_csv
url = "https://www.data.gouv.fr/fr/datasets/r/63352e38-d353-4b54-bfd1-f1b3ee1cabd7"
covid = read_csv(url, sep=";")
covid.tail()
dep sexe jour hosp rea HospConv SSR_USLD autres rad dc
301945 976 0 2022-12-02 8 0 8.0 0.0 0.0 1703 163
301946 976 1 2022-12-02 5 0 5.0 0.0 0.0 709 100
301947 976 2 2022-12-02 3 0 3.0 0.0 0.0 969 61
301948 978 0 2022-12-02 0 0 0.0 0.0 0.0 0 0
301949 978 1 2022-12-02 0 0 0.0 0.0 0.0 0 0
last_day = covid.loc[covid.index[-1], "jour"]
last_day
'2022-12-02'
last_data = covid[covid.jour == last_day].groupby("dep").sum(numeric_only=True)
last_data.shape
(102, 8)
last_data.describe()
sexe hosp rea HospConv SSR_USLD autres rad dc
count 102.000000 102.000000 102.000000 102.000000 102.000000 102.000000 102.000000 102.000000
mean 2.980392 386.686275 21.754902 231.264706 121.470588 12.196078 15482.431373 2534.039216
std 0.198030 413.069001 29.729619 238.054654 156.744397 17.687070 15273.986786 2444.945989
min 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 3.000000 130.250000 4.000000 89.000000 24.000000 0.000000 5949.500000 932.000000
50% 3.000000 246.500000 12.000000 153.000000 73.500000 4.000000 10183.000000 1644.000000
75% 3.000000 448.000000 25.500000 277.250000 155.000000 18.000000 19457.000000 3084.000000
max 3.000000 2203.000000 162.000000 1430.000000 1055.000000 83.000000 88785.000000 12515.000000
last_data.head()
sexe hosp rea HospConv SSR_USLD autres rad dc
dep
01 3 201 6 102.0 89.0 4.0 9021 1625
02 3 252 16 134.0 92.0 10.0 15490 3099
03 3 116 6 98.0 10.0 2.0 10824 1772
04 3 227 2 102.0 123.0 0.0 4820 748
05 3 98 0 24.0 74.0 0.0 5646 787
last_data.tail()
sexe hosp rea HospConv SSR_USLD autres rad dc
dep
972 3 105 6 83.0 16.0 0.0 9000 2142
973 3 46 10 34.0 2.0 0.0 12343 802
974 3 200 8 47.0 145.0 0.0 16726 1882
976 3 16 0 16.0 0.0 0.0 3381 324
978 1 0 0 0.0 0.0 0.0 0 0

Données départements#

On récupère ensuite la définition géographique des départements.

import geopandas
# dernier lien de la page (format shapefiles)
url = "https://www.data.gouv.fr/fr/datasets/r/90b9341a-e1f7-4d75-a73c-bbc010c7feeb"
geo = geopandas.read_file(url)
geo.tail()
code nom geometry
91 91 Essonne POLYGON ((2.22656 48.77610, 2.22866 48.77451, ...
92 92 Hauts-de-Seine POLYGON ((2.29097 48.95097, 2.29162 48.95077, ...
93 93 Seine-Saint-Denis POLYGON ((2.55306 49.00982, 2.55814 49.01201, ...
94 94 Val-de-Marne POLYGON ((2.33190 48.81701, 2.33371 48.81677, ...
95 95 Val-d'Oise POLYGON ((2.59052 49.07965, 2.59013 49.07786, ...

Il faudrait aussi fusionner avec la population de chaque département. Ce sera pour une autre fois.

Carte#

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, figsize=(5, 4))
geo.plot(ax=ax, color='white', edgecolor='black');
../_images/2020_carte_16_0.png

On enlève tous les départements à trois chiffres. Les départements outre-mer sont présents ou non selon le jeu de données téléchargé.

codes = [_ for _ in set(geo.code) if len(_) < 3]
metropole = geo[geo.code.isin(codes)]
metropole.tail()
code nom geometry
91 91 Essonne POLYGON ((2.22656 48.77610, 2.22866 48.77451, ...
92 92 Hauts-de-Seine POLYGON ((2.29097 48.95097, 2.29162 48.95077, ...
93 93 Seine-Saint-Denis POLYGON ((2.55306 49.00982, 2.55814 49.01201, ...
94 94 Val-de-Marne POLYGON ((2.33190 48.81701, 2.33371 48.81677, ...
95 95 Val-d'Oise POLYGON ((2.59052 49.07965, 2.59013 49.07786, ...
fig, ax = plt.subplots(1, 1, figsize=(5, 4))
metropole.plot(ax=ax, color='white', edgecolor='black')
ax.set_title("%s départements" % metropole.shape[0]);
../_images/2020_carte_19_0.png

Carte COVID#

merged = last_data.reset_index(drop=False).merge(metropole, left_on="dep", right_on="code")
merged.shape
(96, 12)
merged.tail()
dep sexe hosp rea HospConv SSR_USLD autres rad dc code nom geometry
91 91 3 715 40 337.0 308.0 30.0 33284 5225 91 Essonne POLYGON ((2.22656 48.77610, 2.22866 48.77451, ...
92 92 3 1509 123 730.0 603.0 53.0 47450 7156 92 Hauts-de-Seine POLYGON ((2.29097 48.95097, 2.29162 48.95077, ...
93 93 3 1921 91 714.0 1055.0 61.0 40699 6297 93 Seine-Saint-Denis POLYGON ((2.55306 49.00982, 2.55814 49.01201, ...
94 94 3 1196 48 685.0 400.0 63.0 45694 7898 94 Val-de-Marne POLYGON ((2.33190 48.81701, 2.33371 48.81677, ...
95 95 3 726 18 440.0 252.0 16.0 31208 5478 95 Val-d'Oise POLYGON ((2.59052 49.07965, 2.59013 49.07786, ...
fig, ax = plt.subplots(1, 1, figsize=(5, 4))
merged.hist('rea', bins=20, ax=ax)
ax.set_title("Distribution rea");
../_images/2020_carte_23_0.png

Les régions les plus peuplées ont sans doute la plus grande capacité hospitalière. Il faudrait diviser par cette capacité pour avoir une carte qui ait un peu plus de sens. Comme l’idée est ici de simplement tracer la carte, on ne calculera pas de ratio.

merged.sort_values('rea').tail()
dep sexe hosp rea HospConv SSR_USLD autres rad dc code nom geometry
12 13 3 1551 98 864.0 579.0 10.0 88785 11852 13 Bouches-du-Rhône MULTIPOLYGON (((5.39670 43.17300, 5.39314 43.1...
33 33 3 970 102 702.0 152.0 14.0 27382 3831 33 Gironde MULTIPOLYGON (((-1.23902 44.59278, -1.23583 44...
59 59 3 1723 110 1075.0 466.0 72.0 70645 11328 59 Nord MULTIPOLYGON (((3.04040 50.15971, 3.04599 50.1...
92 92 3 1509 123 730.0 603.0 53.0 47450 7156 92 Hauts-de-Seine POLYGON ((2.29097 48.95097, 2.29162 48.95077, ...
75 75 3 2203 162 1430.0 593.0 18.0 73440 12515 75 Paris POLYGON ((2.33190 48.81701, 2.33247 48.81825, ...
geomerged = geopandas.GeoDataFrame(merged)
from mpl_toolkits.axes_grid1 import make_axes_locatable

fig, ax = plt.subplots(1, 1)

# ligne à ajouter pour avoir une légende ajustée à la taille du graphe
cax = make_axes_locatable(ax).append_axes("right", size="5%", pad=0.1)

geomerged.plot(column="rea", ax=ax, edgecolor='black', legend=True, cax=cax)
ax.set_title("Réanimations pour les %d départements" % metropole.shape[0]);
../_images/2020_carte_27_0.png

La création de carte a toujours été plus ou moins compliqué. Les premiers notebooks que j’ai créés sur le sujet étaient beaucoup plus complexe. geopandas a simplifié les choses. Son développement a commencé entre 2013 et a bien évolué depuis. Et j’ai dû passer quelques heures à récupérer les contours des départements il y a cinq ans.

On peut également récupérer la capacité maximale de chaque département en regardant sur le passé.

capacite = covid.groupby(["jour", "dep"]).sum().groupby("dep").max()
capacite.head()
sexe hosp rea HospConv SSR_USLD autres rad dc
dep
01 3 904 78 287.0 187.0 36.0 9021 1625
02 3 688 125 392.0 250.0 18.0 15490 3099
03 3 558 58 394.0 91.0 12.0 10824 1772
04 3 381 30 136.0 184.0 26.0 4820 748
05 3 380 50 134.0 178.0 20.0 5646 787
capa_merged = merged.merge(capacite, left_on="dep", right_on="dep")
capa_merged["occupation"] = capa_merged["rea_x"] / capa_merged["rea_y"]
capa_merged.head(n=2).T
0 1
dep 01 02
sexe_x 3 3
hosp_x 201 252
rea_x 6 16
HospConv_x 102.0 134.0
SSR_USLD_x 89.0 92.0
autres_x 4.0 10.0
rad_x 9021 15490
dc_x 1625 3099
code 01 02
nom Ain Aisne
geometry POLYGON ((4.78021 46.17668, 4.78024 46.18905, ... POLYGON ((3.17296 50.01131, 3.17382 50.01186, ...
sexe_y 3 3
hosp_y 904 688
rea_y 78 125
HospConv_y 287.0 392.0
SSR_USLD_y 187.0 250.0
autres_y 36.0 18.0
rad_y 9021 15490
dc_y 1625 3099
occupation 0.076923 0.128
geocapa = geopandas.GeoDataFrame(capa_merged)
fig, ax = plt.subplots(1, 1)

# ligne à ajouter pour avoir une légende ajustée à la taille du graphe
cax = make_axes_locatable(ax).append_axes("right", size="5%", pad=0.1)

geocapa.plot(column="occupation", ax=ax, edgecolor='black', legend=True, cax=cax)
ax.set_title("Occupations en réanimations pour les %d départements" % metropole.shape[0]);
../_images/2020_carte_33_0.png