visualiser les données d’alerte propluvia

Dans ce snack, nous allons regarder comment :

  • préparer des données qui ne sont pas d’une qualité vertigineuse
  • produire un graphique exploratoire

Le but est de voir comment aller au plus simple de la donnée jusqu’à la visualisation.

ingrédients

On commence par importer pandas. Rien de bien surprenant jusque là.

import pandas as pd

On importe les données légèrement brute de propluvia tout en préparant le typage des colonnes qui nous intéressent en les transformant en date ou en catégorie.

propluvia = (
    pd
    .read_csv('https://raw.githubusercontent.com/taniki/propluvia/main/propluvia.csv')
    .assign(
        Date_debut = lambda d: pd.to_datetime(d.Date_debut, errors='coerce'),
        Date_fin = lambda d: pd.to_datetime(d.Date_fin, errors='coerce'),
        NIVEAU= lambda d: d.NIVEAU.astype('category'),
        Region= lambda d: d.Region.astype('category'),
        Departement= lambda d: d.Departement.astype('category'),
    )
    .drop_duplicates()
)

propluvia
Code_ZA Libelle_ZA NIVEAU Date_debut Date_fin Duree Surface AC_Concerne Numero_AR Region Bassin Departement Type_de_zone Durée
0 11_77_02 Nappe Champigny Est Crise renforcée 2010-05-01 2011-02-28 303.0 550.51 NaN 2010/DDEA/SEPR/195 Île-de-France SEINE-NORMANDIE Seine-et-Marne Souterraine NaN
1 11_77_03 Nappe Champigny Ouest Crise renforcée 2010-05-01 2011-02-28 303.0 1718.08 NaN 2010/DDEA/SEPR/195 Île-de-France SEINE-NORMANDIE Seine-et-Marne Souterraine NaN
2 11_91_01 Bassin de l'Yerres Absence de restriction 2010-05-05 2011-03-01 300.0 47.49 NaN 2010-DDEA?SE?132 Île-de-France SEINE-NORMANDIE Essonne Superficielle NaN
3 11_91_01 Nappe Champigny Ouest Crise renforcée 2010-05-05 2011-03-01 300.0 76.94 NaN 2010-DDEA?SE?132 Île-de-France SEINE-NORMANDIE Essonne Souterraine NaN
4 11_91_02 Bassin de l'Ecole Absence de restriction 2010-05-05 2011-03-01 300.0 117.62 NaN 2010-DDEA?SE?132 Île-de-France SEINE-NORMANDIE Essonne Superficielle NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
42964 MP 14 Nappe des Autises Alerte 2022-05-09 2022-05-15 6.0 260.97 NaN AP-22-DDTM85-303 Pays de la Loire LOIRE-BRETAGNE Vendée Souterraine NaN
42965 MP 14 Nappe des Autises Alerte 2022-06-17 2022-07-01 14.0 260.97 NaN 22-DDTM85-409 Pays de la Loire LOIRE-BRETAGNE Vendée Souterraine NaN
42966 MP 14 Nappe des Autises Alerte 2022-05-20 2022-06-07 18.0 260.97 NaN AP-22-DDTM85-336 Pays de la Loire LOIRE-BRETAGNE Vendée Souterraine NaN
42967 MP 14 Nappe des Autises Vigilance 2022-07-08 2022-07-22 14.0 260.97 NaN 22-DDTM85-452 Pays de la Loire LOIRE-BRETAGNE Vendée Souterraine NaN
42968 MP 14 Nappe des Autises Vigilance 2022-07-22 2022-10-31 101.0 260.97 NaN 22-DDTM85-504 Pays de la Loire LOIRE-BRETAGNE Vendée Souterraine NaN

42857 rows × 14 columns

niveaux d’alerte

On goute ensuite aux données. Ici, je regarde le nombre d’arrêté par niveau d’alerte. Le Serie.to_frame() est surtout esthétique, Serie.value_counts() renvoyant ici une série qui serait représenté sous forme de texte et non de tableau sans cela.

propluvia.NIVEAU.value_counts().to_frame()
NIVEAU
Alerte 10854
Vigilance 10161
Crise 10088
Alerte renforcée 9504
Crise renforcée 851
Arrêt des prélèvements non prioritaires 593
Crise modérée 550
Absence de restriction 149
Modification du régime hydraulique 28

Par soucis de concision, on ne va garder que les 4 principaux niveaux.

niveaux = propluvia.NIVEAU.value_counts().index.tolist()[:4]

évolution de la surface des niveaux d’alerte

Afin de décomposer mon code, je prépare une petite fonction qui me donne la surface et le nombre d’arrêté par niveau d’alerte pour une date donnée.

def agg_day(date):
    return (
        propluvia
        [(propluvia.Date_debut <= date) * (propluvia.Date_fin >= date)]
        .groupby('NIVEAU')
        .agg({
            'Surface': 'sum',
            'Numero_AR': 'count'
        })
        .reset_index()
        .assign(
            date = date,
        )
        #.drop(columns=propluvia.columns)
    )

agg_day('2022-06-15')
NIVEAU Surface Numero_AR date
0 Absence de restriction 0.00 0 2022-06-15
1 Alerte 87259.51 199 2022-06-15
2 Alerte renforcée 24435.77 73 2022-06-15
3 Arrêt des prélèvements non prioritaires 0.00 0 2022-06-15
4 Crise 6779.20 38 2022-06-15
5 Crise modérée 0.00 0 2022-06-15
6 Crise renforcée 0.00 0 2022-06-15
7 Modification du régime hydraulique 0.00 0 2022-06-15
8 Vigilance 241842.46 414 2022-06-15

La sélection est faite avec [] par force d’habitude mais pour des soucis de lisibilité, il est souvent préférable d’utiliser DataFrame.query()

Pour aboutir à la visualisation, il y a deux étapes importantes :

  • la transformation en table de pivot afin que chaque ligne du graphique corresponde à une colonne. Ici comme, on cherche à avoir une ligne par niveau d’alerte.
  • l’utilisation de DataFrame.plot.line() afin de séparer chaque niveau d’alerte avec le paramètre subplots et la normalisation de l’axe des ordonnées avec sharey.
import matplotlib.pyplot as plt

(
    pd
    .concat([ agg_day(date) for date in pd.date_range(propluvia.Date_debut.min(),propluvia.Date_fin.max())])
    .query('NIVEAU.isin(@niveaux)')
    .pivot_table(
        index='date',
        columns='NIVEAU',
        values='Surface'
    )
    .plot
        .line(
            sharey=True,
            subplots=True,
            figsize=(15,len(niveaux)*4),
            ylabel='surface (km2)',
        )
)

plt.savefig('graphics/propluvia-niveaux.png', facecolor='white', transparent=False)

(
    propluvia
    .query('NIVEAU.isin(@niveaux)')
    .pivot_table(
        index='Region',
        columns='NIVEAU',
        values='Surface',
        aggfunc='sum'
    )
    [niveaux]
)
NIVEAU Alerte Vigilance Crise Alerte renforcée
Region
Auvergne-Rhône-Alpes 876865.23 1490289.19 205485.61 510990.78
Bourgogne-Franche-Comté 541889.09 304067.78 284922.00 302802.50
Bretagne 291416.45 293329.83 20642.11 54078.45
Centre-Val de Loire 372786.63 175472.81 515529.67 340807.71
Corse 43961.59 125953.05 0.00 23081.19
Grand-Est 439493.13 127040.61 8631.22 273625.34
Hauts-de-France 150273.87 284564.16 5690.04 32149.72
Normandie 97692.49 267389.62 15486.78 30342.10
Nouvelle-Aquitaine 918336.50 451461.35 920951.43 840590.52
Occitanie 671419.42 798545.95 453210.48 517558.06
Pays de la Loire 529736.71 948949.48 506058.29 534155.64
Provence-Alpes-Côte d'Azur 172623.75 446274.37 23164.92 109890.72
Île-de-France 63412.13 82448.24 21813.49 24460.52

après

Voilà, je suis allé au plus court et il y a possibilité d’aller plus loin :