Skip to content
GitLab
Projects Groups Topics Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Register
  • Sign in
  • PYTHON - Atelier MatPlotLib PYTHON - Atelier MatPlotLib
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributor statistics
    • Graph
    • Compare revisions
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Service Desk
    • Milestones
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Terraform modules
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar

La forge institutionnelle d'INRAE étant en production depuis le 10 juin 2025, nous vous recommandons d'y créer tous vos nouveaux projets.

  • Poulard Christine
  • PYTHON - Atelier MatPlotLibPYTHON - Atelier MatPlotLib
  • Wiki
  • Atelier_E_longues_series

Atelier_E_longues_series · Changes

Page history
Update Atelier_E_longues_series authored Apr 21, 2021 by Poulard Christine's avatar Poulard Christine
Show whitespace changes
Inline Side-by-side
Atelier_E_longues_series.md 0 → 100644
View page @ df92607c
# "Chroniques volumineuses", c'est-à-dire très longues et/ou à pas de temps très fin
(en construction ; attention, confusion entre dixièmes de mm et centièmes, vérif en cours...)
Cette session "E" est la première de deux séances consacrées au problème des "chroniques volumineuses", c'est-à-dire très longues et/ou à pas de temps très fin : * comment interpréter des chroniques qui sont "écrasées" par le tracé : rééchantillonnage, intérêt et pièges.
La session suivante, "F", proposera des pistes pour faciliter le tracé : prétraitements avant tracé, graphe avec deux vignettes, la chronique complète et une chronique zoomable, avec mention sur le premier d'un rectangle correspondant à l'étendue du graphique zoomé.
On va travailler ici avec une longue série, avec des illustrations utilisant un fichier binaire de 10 ans de données à 5 minutes de résolution. Vous pourrez travailler avec vos propres fichiers ou le fichier de débit journalier utilisé précédemment ("B"), mais les réflexions de cette session sont d'autant plus pertinentes que le fichier est "volumineux".
Pour permettre d'utiliser n'importe quel type de fichier, on va donc distinguer la fonction de lecture, qui retournera une liste de x et une liste de y (ou bien un objet pandas), et la fonction pour le tracé qui prendra en entrée une liste de x et une liste de y (ou bien un objet pandas).
## Complément : lecture d'un fichier binaire
Il n'est pas indispensable de travailler avec un fichier binaire, mais on va commenter le code ci-dessous au moins pour information.
Pour lire un fichier binaire, il faut impérativement connaître sa structure : son *nombre de valeurs*, le *type* exact des données et éventuellement le *code* correspondant aux lacunes.
Ici, on a des valeurs qui correspondent à des cumuls de lames d'eau toutes les 5 minutes, en _dixièmes_ de mm, depuis le 1er juillet 2006 inclus.
On remarque que l'on a créé aussi une variable PDT_OBS_EN_MIN, le pas de temps d'observation en minutes. Il est recommandé de définir des variables explicites plutôt que d'écrire simplement "5" quand nécessaire pour deux raisons :
- les calculs sont plus lisibles : quand on utilise directement des chiffres, ce sont comme des chiffres tombés du ciel (on les appelle _"magic number"_): cela peut être compliqué de retrouver à quoi cela correspond, même si avec un peu d'habitude on devinera ce que signifie 365 ou 288...
- il est plus facile de modifier, pour adapter par exemple la routine à la lecture de données pluviométriques qui, elles, sont à 6 minutes
Ensuite, on crée un vecteur de dates ; un "timeindex" pandas serait plus intelligent, on pourra vérifier en séance si effectivement il occupe la même place en mémoire ou pas.
Pour la lecture, on utilise np.fromfile(_nom_du_fichier_binaire_, dtype=_type de données_, count=_nombre de données_).
Ici les valeurs sont des entiers non signés dtype=np.uint16 codant des intensités en _dixièmes_ de mm, et il y en a autant que de dates dans la liste si on a bien travaillé...
On remarque un test de type "try/except" : en cas d'erreur le code ne plante pas mais des messages d'erreur s'affichent dans le terminal.
Une fois les données dans la vecteur "numpy.array" uint16_valeurs, on va créer à partir de ces éléments un conteneur de type *numpy array*, dont les éléments sont tous de même type, ce qui permet le recours à une bibliothèque de fonctions codées en C. On va effectuer deux traitements :
- passer les valeurs en millimètres si ce ne sont pas des lacunes
- leur affecter np.nan si ce sont des lacunes.
On a écrit une boucle pour faire ce traitement de manière explicite, mais il est possible d'écrire la même chose en une ligne en utilisant numpy (dès que je le retrouve dans mes codes... ).
Une spécificité de numpy est que les valeurs nan, "not a number", sont rattachés au type *float* : un vecteur de valeurs entières comportant des "nan" devra donc être de type float...
```python
# LIRE UN FICHIER BINAIRE
def lire_lame_deau_radar_5min(nom_fichier_binaire):
# données correspondant au fichier binaire (elles doivent être fournies avec le fichier)
PDT_OBS_EN_MIN = 5 # pas de temps d'observation en minutes
DIVISEUR = 10
CODE_LACUNE_BINAIRE = 65535 # => -999.99 (lacune)
nb_of_days = 3653
pas_de_temps = 288 # nb de pas de temps dans une journée
len_of_tvect = nb_of_days * pas_de_temps # 1052064
recordSize = len_of_tvect * 2 # parce que ce sont des entiers codés sur 2 bytes
# création de la liste de dates
date_premiere_valeur = datetime(2006, 7, 1, 0, 5)
liste_dates = [date_premiere_valeur + i * timedelta(minutes=PDT_OBS_EN_MIN) for i in
range(len_of_tvect)] # PDT_OBS_EN_MIN = 5 minutes
if Path.is_file(Path(nom_fichier_binaire)):
print(f"nom_chemin_binaire1 : {nom_fichier_binaire} ")
# lecture des valeurs
uint16_valeurs = None
with open(nom_fichier_binaire, 'rb') as fic_binaire:
try:
uint16_valeurs = np.fromfile(fic_binaire, dtype=np.uint16, count=int(len(liste_dates)))
statut_lecture = "OK"
# ATTENTION MAUVAISE PRATIQUE, PEP8 : jamais d'exception sans en préciser le type
except:
statut_lecture = "erreur" # on pourrait sortir de la fonction avec return None
# VERIF
print("Ensemble des valeurs lues avant traitement (lacunes, diviseur...) : ", set(uint16_valeurs))
if statut_lecture == "OK":
# Traitement des lacunes (il y a plus court comme syntaxe, avec un masque)
nb_lacunes = 0
for valeur in uint16_valeurs:
if int(valeur) == int(CODE_LACUNE_BINAIRE):
nb_lacunes += 1
print("Nombre de lacunes = ", nb_lacunes)
# on travaille avec des LAMES D'EAU en centième de mm, à passer en mm
np_valeurs = np.array(
[entier / DIVISEUR if entier != int(CODE_LACUNE_BINAIRE)
else np.nan for entier in uint16_valeurs])
# ici tracé dans la foulée de la lecture
temps = datetime.now() - debut
# tracé avec plot
plt.plot(liste_dates, np_valeurs)
plt.title("Fichier binaire "+ os.path.basename(nom_fichier_binaire) + "\n sans précaution pour les étiquettes de dates" )
plt.show()
print("temps écoulé, step:", temps)
return liste_dates, np_valeurs
```
## Tracé d'une longue chronique
Pour le tracé on peut se contenter de plt.plot(liste_dates, np_valeurs) ou utiliser step en vérifiant que les créneaux sont sur le bon l'intervalle: la première valeur doit correspondre aux cinq premières minutes du 1er juillet 2006.
Cependant, si on fait varier le niveau de zoom, on constate que la gestion des étiquettes de date est hasardeuse.
On propose donc de construire une fonction séparée (c'est toujours mieux), quitte à l'appeler depuis la fonction de lecture, et de tester des instructions très pratiques qui permettent de retrouver le rendu de pandas, et même de customiser les étiquettes (on n'ira pas jusque là) : *mdates.AutoDateLocator* et *mdates.ConciseDateFormatter*.
```python
# Tracer une longue série, applicable à toutes données (lues dans Hydro2, dans un binaire ou tout autre format)
import matplotlib.dates as mdates
def tracer_proprement_une_longue_serie(dates, valeurs, info_chroniques="lames d'eau radar, mm en 5 min"):
""" démonstration de "concise formatter"
"""
locator = mdates.AutoDateLocator(minticks=3, maxticks=12)
formatter = mdates.ConciseDateFormatter(locator)
# VERIF
if len(dates) != len(valeurs):
print(f"Les 2 vecteurs n'ont pas la même longueur : {len(dates)} dates et {len(valeurs)} valeurs")
return
# ça permet de quitter la fonction ; ce n'est pas la meilleure manière de faire (plutôt un else...)
print("Nombre de points : ", len(dates))
# tracé
debut = datetime.now()
plt.ion()
fig_cf, ax_t = plt.subplots()
fig_cf.suptitle("Tracé d'une longue chronique, soin apporté aux étiquettes de dates \n" + info_chroniques)
ax_t.set_xlabel("dates, format date", fontsize=14)
ax_t.set_ylabel("info_valeurs", fontsize=14)
ax_t.xaxis.set_major_locator(locator)
ax_t.xaxis.set_major_formatter(formatter)
ax_t.step(dates, valeurs)
temps = datetime.now() - debut
print("tracer_proprement_une_longue_serie, temps écoulé, step:", temps)
fig_cf.canvas.draw()
fig_cf.savefig("test.png")
plt.ioff()
plt.show()
# on ne retourne rien
```
## Comment interpréter cette chronique ?
Est-ce que l'on se rend compte de la répartition des volumes au cours de l'année ?
(à suivre)
\ No newline at end of file
Clone repository
  • AtelierB1_Graphiques_simples
  • AtelierB2_Lire_un_fichier
  • AtelierC
  • Atelier_D_carte
  • Atelier_D_carte_a_partir_de_fichiers_binaires
  • Atelier_E_longues_series
  • Atelier_G_widgets
  • Atelier_clic_afficher
  • Atelier_clics
  • Cartes focus sur le redimensionnement
  • GUI avec QT
  • La doc avec Sphinx
  • Lexique
  • Point Théorie Subplots
  • Pour les contributeurs
View All Pages