|
|
# "Widgets" directement avec matplotlib
|
|
|
# "Widgets" directement avec matplotlib
|
|
|
|
|
|
Cette session "G" a été ajoutée pour permettre de manipuler les **widgets de Matplotlib** sur un exemple plus simple que les étapes "E" et "F", en particulier sans avoir à lire un fichier ni utiliser pandas et ses rééchantillonnages (une difficulté à la fois...).
|
|
|
Les widgets sont des objets permettant d'interagir avec la figure, il faut pour cela les associer avec des fonctions qui, par exemple, recalculent des valeurs en fonction de nouveaux paramètres et retracent les courbes :
|
|
|
- :abc: textbox : espace de saisie d'un texte
|
|
|
Cette session "G" a été ajoutée pour permettre de manipuler les **widgets de Matplotlib** sur un exemple plus simple que les étapes "E" et "F", en particulier sans avoir à lire un fichier ni utiliser pandas et ses rééchantillonnages (une difficulté à la fois...). Les widgets sont des objets permettant d'interagir avec la figure, il faut pour cela les associer avec des fonctions qui, par exemple, recalculent des valeurs en fonction de nouveaux paramètres et retracent les courbes :
|
|
|
|
|
|
- :abc: textbox : espace de saisie d'un texte
|
|
|
- :arrow_backward: :arrow_forward: "Sliders" (un peu relookés dans la version 3.5 :new:)
|
|
|
- :ballot_box_with_check: cases à cocher : Checkbuttons
|
|
|
- :radio_button: choix d'une option : Radiobuttons
|
... | ... | @@ -13,89 +13,84 @@ On commence par un exemple simple avec Textbox, et pour la suite on va commenter |
|
|
A noter : on peut aussi ajouter des outils :tools: à la barre d'outil de matplotlib, ou reconfigurer mes outils existants.
|
|
|
|
|
|
## Exemple simple : "textbox" et mise à jour d'une courbe
|
|
|
Dans cet exemple, on définit une figure avec deux vignettes : **ax** va accueillir le graphique tandis que **ax_tbox** , en dessous, va servir à placer la textbox.
|
|
|
La textbox a :
|
|
|
|
|
|
Dans cet exemple, on définit une figure avec deux vignettes : **ax** va accueillir le graphique tandis que **ax_tbox** , en dessous, va servir à placer la textbox. La textbox a :
|
|
|
|
|
|
- une représentation graphique, que l'on place sur une des vignettes du graphique
|
|
|
- des possibilités d'interactions : l'utilisateur peut modifier le texte (clic + saisie au clavier) ou simplement survoler l'emplacement
|
|
|
- des possibilités d'interactions : l'utilisateur peut modifier le texte (clic + saisie au clavier) ou simplement survoler l'emplacement
|
|
|
- des événements associés (ici "on_change" et "on_submit", voir la doc)
|
|
|
|
|
|
On définit deux courbes fixes avec **plot**, une sinusoïde et un point isolé placé sur cette sinusoïde (cette 2e courbe est tout à fait optionnelle). Il ne serait pas nécessaire de les nommer, puisqu'on n'intervient plus dessus.
|
|
|
Ici, on a défini une couleur de fond et une couleur "en cas de survol" : quand on passe la souris, la couleur chance
|
|
|
L'événement "une valeur a été modifiée dans la textbox" est associé à une fonction, appelée update. Cette fonction va tout simplement lire la valeur dans la textbox (argument "val" de la fonction ), transformée en entier positif, et tirer au hasard autant de valeurs aléatoires entre un et 6, et placer les points (x, sin(x)) pour tous les points x de cette série.
|
|
|
### le graphique : trois courbes donc une sera modifiable
|
|
|
|
|
|
On définit deux courbes fixes avec **plot**, une sinusoïde et un point isolé placé sur cette sinusoïde (cette 2e courbe est tout à fait optionnelle). Il ne serait pas nécessaire de les nommer, puisqu'on n'intervient plus dessus. On va définir une 3e courbe, avec scatter, que l'on souhaite modifier au fil du temps. On a nommé cette courbe scat.
|
|
|
|
|
|
### définition de la textbox
|
|
|
|
|
|
On place une textbox dans la vignette ax_tbox. On n'a pas besoin de la nommer car elle est définie dans le programme principal, le "garbage collector" ne va donc pas l'expulser. , on a défini une couleur de fond et une couleur "en cas de survol" : quand on passe la souris au-dessus ("hover"), la couleur changera.
|
|
|
|
|
|
### définition de la fonction associée à un événement ("callback")
|
|
|
|
|
|
L'événement **on_submit**="une valeur a été modifiée dans la textbox" est associé à une fonction, appelée update, qu'il faut à présent définir\*\*. Cette fonction va tout simplement lire la valeur dans la textbox (argument "val" de la fonction ), transformée en entier positif, et tirer au hasard autant de valeurs aléatoires entre un et 6, et placer les points (x, sin(x)) pour tous les points x de cette série. Au lieu de définir une nouvelle courbe à chaque fois, on va modifier scat, grâce à la méthode **set_offsets** qui est analogue à la méthode **set_data** pour plot. On en profite aussi pour modifier une liste d'étiquettes que l'on va placer sur le graphique grâce à la méhtode **annotate**, qu'il est utile de connaître. On verra donc :
|
|
|
|
|
|
- les points, représentés par des carrés ; pour montrer comment changer les couleurs dans scatter, on a décidé que le 2e de chaque série, dans l'ordre de tirage, sera rouge et les autres orange ; pour en savoir plus, la [page dédiée à scatter](focus_scatter) est beaucoup plus détaillée ;
|
|
|
- les annotations, qui correspondent à l'abscisse mise en forme grâce à une f-string (valeur précédée de # et avec 2 chiffres après la virgule)
|
|
|
|
|
|
## Widgets intégrés à des applications
|
|
|
|
|
|
On s'appuie pour les explications sur :
|
|
|
- Le code ProbaCruesMaxAn_SurNannees.py : utilisation du widget **"slider"** pour changer les paramètres d'une fonction très simple, recalculer les résultats et mettre à jour deux courbes. Ces courbes sont tracées avec **plot**, ce qui n'est pas la meilleure solution pour des calculs discrets : dans la version ProbaCruesMaxAn_SurNannees_stairs.py on utilise "step" en combinaison avec "fill_between" pour avoir un rendu en escalier. Mais une difficulté à la fois...[pour voir la version définitive](https://gitlab.irstea.fr/hydrotools_demosandprocessing/probability-of-having-k-floods-in-n-years)
|
|
|
- Chegodaiev.py combine plusieurs types de courbes (plot, vlines) et utilise deux **sliders** et une **checkbox** (widget CheckButtons) ; GenerateurCruesMaxAnnuelles.py s'appuie sur ce code qu'il développe pour faire passer un message plus complexe. Il manipule d'autres types de courbes (scatter, stem). On y teste aussi, en doublon des widget **boutons**, des **outils** ajoutés à la barre d'outils (portant du texte ou une icône). Enfin, une version plus aboutie, qui n'utilise plus de boutons mais seulement des outils barre d'outils de matplotlib, a été déposée dans un projet public, avec traduction en anglais (en cours...) sous le nom Sample2Gumbel, qui lui-même sert de base à Sampo2Gumbel2Damages...
|
|
|
|
|
|
illustrations ; [fenêtre matplotlib de GenerateurCruesMaxAnnuels avec widgets et, en doublons des widgets boutons, des outils ajoutés à la barre d'outils](https://gitlab.irstea.fr/christine.poulard/atelier-matplotlib/-/blob/master/AtelierG_widgetsEtBoutonsToolbar.png)
|
|
|
- Le code ProbaCruesMaxAn_SurNannees.py : utilisation du widget **"slider"** pour changer les paramètres d'une fonction très simple, recalculer les résultats et mettre à jour deux courbes. Ces courbes sont tracées avec **plot**, ce qui n'est pas la meilleure solution pour des calculs discrets : dans la version ProbaCruesMaxAn_SurNannees_stairs.py on utilise "step" en combinaison avec "fill_between" pour avoir un rendu en escalier. Mais une difficulté à la fois...[pour voir la version définitive](https://gitlab.irstea.fr/hydrotools_demosandprocessing/probability-of-having-k-floods-in-n-years)
|
|
|
- Chegodaiev.py combine plusieurs types de courbes (plot, vlines) et utilise deux **sliders** et une **checkbox** (widget CheckButtons) ; GenerateurCruesMaxAnnuelles.py s'appuie sur ce code qu'il développe pour faire passer un message plus complexe. Il manipule d'autres types de courbes (scatter, stem). On y teste aussi, en doublon des widget **boutons**, des **outils** ajoutés à la barre d'outils (portant du texte ou une icône). Enfin, une version plus aboutie, qui n'utilise plus de boutons mais seulement des outils barre d'outils de matplotlib, a été déposée dans un projet public, avec traduction en anglais (en cours...) sous le nom Sample2Gumbel, qui lui-même sert de base à Sampo2Gumbel2Damages...
|
|
|
|
|
|
illustrations ; [fenêtre matplotlib de GenerateurCruesMaxAnnuels avec widgets et, en doublons des widgets boutons, des outils ajoutés à la barre d'outils](https://gitlab.irstea.fr/christine.poulard/atelier-matplotlib/-/blob/master/AtelierG_widgetsEtBoutonsToolbar.png)
|
|
|
|
|
|
![Sample2Gumbel screenshot](/images/DixAnsAvecCentennaleEtQuinquennale.png)
|
|
|
|
|
|
|
|
|
Au passage, on manipulera la notion de "portée des variables". Certaines fonctions utilisent le mot-clef "global" pour pouvoir changer la valeur d'une variable du corps du programme dans une fonction. Cela est à utiliser avec précaution toutefois...
|
|
|
|
|
|
### à propos des widgets de matplotlib,
|
|
|
Il s'agit bien des widgets de matplotlib, ce qui peut rendre service en phase de test sans assurer un rendu parfait... mais en s'épargnant le recours à une bibliothèque d'interface (Tkinter, PyQT ou PySide...).
|
|
|
Il existe des bibliothèques tierces avec des améliorations de ces widgets, signalée [dans la rubrique Third Party Packages de MatPlotLib](https://matplotlib.org/stable/thirdpartypackages/index.html ). Pour les curseurs, on trouve ainsi: mplcursors (interactive data cursors for Matplotlib), MplDataCursor ( toolkit to provide interactive "data cursors" (clickable annotation boxes) et mpl_interactions (easy to create interactive plots controlled by sliders and other widgets), non encore testées.
|
|
|
|
|
|
Il s'agit bien des widgets de matplotlib, ce qui peut rendre service en phase de test sans assurer un rendu parfait... mais en s'épargnant le recours à une bibliothèque d'interface (Tkinter, PyQT ou PySide...). Il existe des bibliothèques tierces avec des améliorations de ces widgets, signalée [dans la rubrique Third Party Packages de MatPlotLib](https://matplotlib.org/stable/thirdpartypackages/index.html). Pour les curseurs, on trouve ainsi: mplcursors (interactive data cursors for Matplotlib), MplDataCursor ( toolkit to provide interactive "data cursors" (clickable annotation boxes) et mpl_interactions (easy to create interactive plots controlled by sliders and other widgets), non encore testées.
|
|
|
|
|
|
On signale ici qu'il existe aussi une bibliothèque iPython pour ajouter des widgets à un notebook Jupyter (non testé ici).
|
|
|
|
|
|
:warning: :recycle: erreur courante : si vous définissez des widgets à l'intérieur d'une fonction, ils vont fonctionner un moment, jusqu'à ce que le "garbage collector" (ramasse-miette en français) considère, une fois la fonction terminée, que ces objets ne sont plus référencés même si la fenêtre matplotlib est toujours ouverte avec les widgets dessus ! Il va donc les supprimer. Ils seront toujours tracés, mais inactifs (non-responsive en anglais, si vous cherchez des compléments sur les forums). Il faut donc veiller à ce que leur nom soit déclaré au-dehors de la fonction, soit en le définissant comme retour de fonction, soit en créant un attribut d'un objet mon_objet_qui_existe_dans_le_main.slider = mon_slider_dans_la_fonction. Le même problème existe pour les images intégrées dans une interface, il faut absolument leur donner un nom pour que l'objet correspondant ne soit pas jeté par le GC.
|
|
|
|
|
|
:warning: :recycle: erreur courante : si vous définissez des widgets à l'intérieur d'une fonction, ils vont fonctionner un moment, jusqu'à ce que le "garbage collector" (ramasse-miette en français) considère, une fois la fonction terminée, que ces objets ne sont plus référencés même si la fenêtre matplotlib est toujours ouverte avec les widgets dessus ! Il va donc les supprimer. Ils seront toujours tracés, mais inactifs (non-responsive en anglais, si vous cherchez des compléments sur les forums). Il faut donc veiller à ce que leur nom soit déclaré au-dehors de la fonction, soit en le définissant comme retour de fonction, soit en créant un attribut d'un objet
|
|
|
mon_objet_qui_existe_dans_le_main.slider = mon_slider_dans_la_fonction.
|
|
|
Le même problème existe pour les images intégrées dans une interface, il faut absolument leur donner un nom pour que l'objet correspondant ne soit pas jeté par le GC.
|
|
|
### à propos des codes
|
|
|
|
|
|
### à propos des codes
|
|
|
On présente ici surtout deux codes, vous pouvez choisir de lire celui qui vous parle le plus. On a laissé deux versions de chaque code, une simple et une un peu mieux finie mais du coup un peu plus complexe.
|
|
|
|
|
|
**ProbaCruesMaxAn_SurNannees.py** : c'est le code le plus simple, qui traite d'une seule fonction qui tient en une ligne et qui dépend de deux paramètres, N (nombre d'années) et T (période de retour, inverse d'une fréquence). Sa courbe est tracée avec "plot" ; on représente les probas par des points, sans ligne pour les relier, car ce sont des probas discrètes :
|
|
|
La figure est munie de deux sliders qui font varier N et T, et qui activent tous les deux la même fonction de mise à jour du tracé : recalcul et remplacement des vecteurs x et y de la courbe (méthode set_data). Le titre change aussi.
|
|
|
Les "sliders" sont intéressants pour faire varier "en direct", et créer presque des animations en direct, mais il est difficile de viser une valeur en particulier. Le widget "textBox" serait meilleur pour saisir des valeurs.
|
|
|
***ProbaCruesMaxAn_SurNannees_stairs** est une autre version qui propose en plus un graphique en escalier, intéressant car on doit jouer avec les Locator et Formatter pour que l'étiquette se place au milieu de chaque marche d'escalier.
|
|
|
|
|
|
**Chegodaiev.py** : code simple pour illustrer la notion de fréquence empirique, avec la formule de Tchégodaiev et une formule plus générique paramétrée par a et b, dont les valeurs sont modifiables avec des "sliders". Une case à cocher permet de changer l'abscisse de fréquence en période de retour.
|
|
|
Il est utilisable, mais il reste quelques maladresses, dans les définitions des xlim par exemple ou le choix des méthodes de tracé pour Tchgodaiev (les vlines allant de 0 à la valeur max ne sont pas forcément le meilleur choix, et surtout ils sont bien plus compliqués à mettre à jour !). Comme il a aussi une vocation d'exercice, on a utilisé le widget "checkbutton" même si le rendu est catastrophique dans ce cas de figure... Le "radiobouton" aussi est très mal représenté, avec un ovale très aplati au lieu d'un cercle. On peut penser qu'un simple bouton serait mieux.
|
|
|
**ProbaCruesMaxAn_SurNannees.py** : c'est le code le plus simple, qui traite d'une seule fonction qui tient en une ligne et qui dépend de deux paramètres, N (nombre d'années) et T (période de retour, inverse d'une fréquence). Sa courbe est tracée avec "plot" ; on représente les probas par des points, sans ligne pour les relier, car ce sont des probas discrètes : La figure est munie de deux sliders qui font varier N et T, et qui activent tous les deux la même fonction de mise à jour du tracé : recalcul et remplacement des vecteurs x et y de la courbe (méthode set_data). Le titre change aussi. Les "sliders" sont intéressants pour faire varier "en direct", et créer presque des animations en direct, mais il est difficile de viser une valeur en particulier. Le widget "textBox" serait meilleur pour saisir des valeurs. \***ProbaCruesMaxAn_SurNannees_stairs** est une autre version qui propose en plus un graphique en escalier, intéressant car on doit jouer avec les Locator et Formatter pour que l'étiquette se place au milieu de chaque marche d'escalier.
|
|
|
|
|
|
<details><summary>Plus d'infos sur la customisation des checkbuttons</summary>
|
|
|
Il est possible d'aller modifier la taille de la case à cocher, qui est un objet Rectangle du module matplotlib.patches, avec par exemple la méthode set_bounds(left, bottom, width, height). J'ai essayé, c'est plutôt facile de trouver l'attribut rectangles de la case à cocher et de trouver des dimensions qui vont bien, surtout avec une seule case à cocher.
|
|
|
Par contre, si la case est cochée les lignes qui sont tracées restent définies en fonction de la case définie par défaut, il faudrait donc également modifier les propriétés des Line2D en conséquence, ce que je n'ai pas testé. La liste des lignes est dans l'attribut lines et pour chaque ligne on peut modifier les coordonnées avec set_data(self, *args, avec *args(2, N) array ou 2 1D arrays
|
|
|
</details>
|
|
|
**Chegodaiev.py** : code simple pour illustrer la notion de fréquence empirique, avec la formule de Tchégodaiev et une formule plus générique paramétrée par a et b, dont les valeurs sont modifiables avec des "sliders". Une case à cocher permet de changer l'abscisse de fréquence en période de retour. Il est utilisable, mais il reste quelques maladresses, dans les définitions des xlim par exemple ou le choix des méthodes de tracé pour Tchgodaiev (les vlines allant de 0 à la valeur max ne sont pas forcément le meilleur choix, et surtout ils sont bien plus compliqués à mettre à jour !). Comme il a aussi une vocation d'exercice, on a utilisé le widget "checkbutton" même si le rendu est catastrophique dans ce cas de figure... Le "radiobouton" aussi est très mal représenté, avec un ovale très aplati au lieu d'un cercle. On peut penser qu'un simple bouton serait mieux.
|
|
|
|
|
|
**GenerateurCruesMaxAnnuelles.py** : code non commenté ici mais il est ajouté au dépôt pour information.
|
|
|
C'est une étape intermédiaire entre Chegodaiev.py (dont il corrige quelques maladresses) et un code qui commence à être diffusable, **Sample2Gumbel**. Comme **Sample2Gumbel** est appelé à évoluer, on a préféré citer ici l'étape intermédiaire sur laquelle on ne reviendra pas.
|
|
|
Il s'agit de créer une chronique d'observation de max annuels suivant une loi connue de Gumbel, de 10 ans en 10 ans, afin d'observer la variabilité des crues (ou des pluies d'ailleurs). On compare sur une autre vignette la loi ayant servi à la constitution de l'échantillon avec la relation Q(T) déduite des observations: plus on étend la période d'observation, plus on va converger vers la 'vraie' distribution (que l'on ne connaît pas dans le monde réel...). Un bouton "remise à zéro" efface la chronique et repart d'une série de 10 ans, ce qui permet aussi de voir que quel que soit le tirage de 10 ans les positions empiriques sont identiques (tant que a et b restent inchangées)...
|
|
|
La version plus aboutie, [Sample2Gumbel](https://gitlab.irstea.fr/christine.poulard/hydrotools-_-demo-and-processing-tools), est mise en ligne dans un groupe public "démos et outils". Elle dispose de plus de fonctionnalités, déclenchées par des icônes ajoutées à la toolbar plutôt que par les "boutons" de matplotlib, bien pratiques mais toujours pas très reconnaissables en tant que boutons.
|
|
|
Plus d'infos sur la customisation des checkbuttons Il est possible d'aller modifier la taille de la case à cocher, qui est un objet Rectangle du module matplotlib.patches, avec par exemple la méthode set_bounds(left, bottom, width, height). J'ai essayé, c'est plutôt facile de trouver l'attribut rectangles de la case à cocher et de trouver des dimensions qui vont bien, surtout avec une seule case à cocher. Par contre, si la case est cochée les lignes qui sont tracées restent définies en fonction de la case définie par défaut, il faudrait donc également modifier les propriétés des Line2D en conséquence, ce que je n'ai pas testé. La liste des lignes est dans l'attribut lines et pour chaque ligne on peut modifier les coordonnées avec set_data(self, \*args, avec \*args(2, N) array ou 2 1D arrays
|
|
|
|
|
|
**GenerateurCruesMaxAnnuelles.py** : code non commenté ici mais il est ajouté au dépôt pour information. C'est une étape intermédiaire entre Chegodaiev.py (dont il corrige quelques maladresses) et un code qui commence à être diffusable, **Sample2Gumbel**. Comme **Sample2Gumbel** est appelé à évoluer, on a préféré citer ici l'étape intermédiaire sur laquelle on ne reviendra pas. Il s'agit de créer une chronique d'observation de max annuels suivant une loi connue de Gumbel, de 10 ans en 10 ans, afin d'observer la variabilité des crues (ou des pluies d'ailleurs). On compare sur une autre vignette la loi ayant servi à la constitution de l'échantillon avec la relation Q(T) déduite des observations: plus on étend la période d'observation, plus on va converger vers la 'vraie' distribution (que l'on ne connaît pas dans le monde réel...). Un bouton "remise à zéro" efface la chronique et repart d'une série de 10 ans, ce qui permet aussi de voir que quel que soit le tirage de 10 ans les positions empiriques sont identiques (tant que a et b restent inchangées)... La version plus aboutie, [Sample2Gumbel](https://gitlab.irstea.fr/christine.poulard/hydrotools-_-demo-and-processing-tools), est mise en ligne dans un groupe public "démos et outils". Elle dispose de plus de fonctionnalités, déclenchées par des icônes ajoutées à la toolbar plutôt que par les "boutons" de matplotlib, bien pratiques mais toujours pas très reconnaissables en tant que boutons.
|
|
|
|
|
|
# CODE **ProbaCruesMaxAn_SurNannees.py**
|
|
|
Il s'agit de probabilités discrètes, calculées sur des valeurs **k** entières, avec deux paramètres **N** et **T** que l'on souhaite faire varier.
|
|
|
On peut donc représenter ces probabilités en fonction de k soit par **plot** avec des markers non reliés, soit par une fonction de type "step". On va présenter ici le premier cas car les courbes tracées avec **plot** sont plus faciles à modifier, et on présentera à la fin une variante avec une graphique en escalier.
|
|
|
|
|
|
Il s'agit de probabilités discrètes, calculées sur des valeurs **k** entières, avec deux paramètres **N** et **T** que l'on souhaite faire varier. On peut donc représenter ces probabilités en fonction de k soit par **plot** avec des markers non reliés, soit par une fonction de type "step". On va présenter ici le premier cas car les courbes tracées avec **plot** sont plus faciles à modifier, et on présentera à la fin une variante avec une graphique en escalier.
|
|
|
|
|
|
## 1. La fonction et ses paramètres
|
|
|
On rappelle que la période de retour **T** est l’inverse de **f**, la fréquence au dépassement : **T= 1/f**.
|
|
|
On raisonne avec les crues maximales annuelles, on ne retient de chaque année que la plus forte occurrence de crue. Une crue centennale est dépassée en moyenne une fois tous les 100 ans et a donc une chance sur 100 d’être dépassée une année donnée : T=100 ans et f=1/100.
|
|
|
|
|
|
Si on illustre par un arbre des possibles, le nombre de réalisations pour lesquelles on a exactement **k** crues supérieures au niveau voulu est égal au nombre de façons de sélectionner **k** positions parmi **N**, soit **C(N,k)**. Pour ces **C(N,k)** réalisations, on a **k** fois un tirage de probabilité **f** et le reste du temps, soit **(N-k)** tirages, un tirage de probabilité complémentaire **(1-f)**.
|
|
|
On rappelle que la période de retour **T** est l’inverse de **f**, la fréquence au dépassement : **T= 1/f**. On raisonne avec les crues maximales annuelles, on ne retient de chaque année que la plus forte occurrence de crue. Une crue centennale est dépassée en moyenne une fois tous les 100 ans et a donc une chance sur 100 d’être dépassée une année donnée : T=100 ans et f=1/100.
|
|
|
|
|
|
Si on illustre par un arbre des possibles, le nombre de réalisations pour lesquelles on a exactement **k** crues supérieures au niveau voulu est égal au nombre de façons de sélectionner **k** positions parmi **N**, soit **C(N,k)**. Pour ces **C(N,k)** réalisations, on a **k** fois un tirage de probabilité **f** et le reste du temps, soit **(N-k)** tirages, un tirage de probabilité complémentaire **(1-f)**.
|
|
|
|
|
|
La probabilité d’avoir sur **N** années données exactement **k** crues qui dépassent le niveau de la crue de période de retour T=1/f se calcule donc par la formule : **C(N,k).f^k.(1-f)^(N-k).**
|
|
|
La probabilité d’avoir sur **N** années données exactement **k** crues qui dépassent le niveau de la crue de période de retour T=1/f se calcule donc par la formule : **C(N,k).f^k.(1-f)^(N-k).**
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
from scipy.special import comb
|
|
|
def proba_crue(n, k, f):
|
|
|
""" Probabilité d’avoir exactement k crues de fréquence f en n années : C(n,k)*f^k*(1-f)^(N-k)
|
|
|
"""
|
|
|
return comb(n, k)*(f**k)*((1-f)**(n-k))
|
|
|
```
|
|
|
On représente la probabilité ainsi calculée en fonction de k, pour N et T fixés.
|
|
|
A chaque fois que l'on a une probabilité pour k, on peut aussi calculer par cumul la probabilité d'avoir entre 0 et k crues de période de retour supérieure à N (courbe orange sur la copie d’écran suivante).
|
|
|
Il n'est pas utile d'explorer tous les k de 0 à N : quand la probabilité cumulée dépasse un seuil, ici 0.999, on arrête la boucle sur k. .
|
|
|
|
|
|
``` python
|
|
|
On représente la probabilité ainsi calculée en fonction de k, pour N et T fixés. A chaque fois que l'on a une probabilité pour k, on peut aussi calculer par cumul la probabilité d'avoir entre 0 et k crues de période de retour supérieure à N (courbe orange sur la copie d’écran suivante). Il n'est pas utile d'explorer tous les k de 0 à N : quand la probabilité cumulée dépasse un seuil, ici 0.999, on arrête la boucle sur k. .
|
|
|
|
|
|
```python
|
|
|
def calcul_courbes(n,f):
|
|
|
|
|
|
cumul_au_plus = 0
|
... | ... | @@ -110,27 +105,31 @@ def calcul_courbes(n,f): |
|
|
k+=1
|
|
|
|
|
|
return k-1, liste_probas_exactement, liste_probas_auplus
|
|
|
```
|
|
|
```
|
|
|
|
|
|
On définit **N** et **T** dès le départ, avec une valeur par défaut.
|
|
|
|
|
|
On calcule les probabilités avec ces valeurs par défaut, et on recueille les résultats dans 3 variables qui seront utilisées pour le tracé.
|
|
|
``` python
|
|
|
|
|
|
```python
|
|
|
k, liste_probas_exactement, liste_probas_auplus = calcul_courbes(n, 1 / T)
|
|
|
```
|
|
|
```
|
|
|
|
|
|
Deux **sliders** vont permettre ensuite de modifier **N** et **T** ; on va relancer les calculs et tracer des résultats dès qu'ils seront modifiés. Ici, on a choisi de répercuter la modification aux vraibles N et T définies dans le corps du programme. Ce n'est pas forcément la meilleure façon de faire, mais pour un petit code avec donc peu de variables cela ne pose pas de problème (pas d'ambiguité possible) et cela va nous permettre de manipuler la notion de portée des variables.
|
|
|
|
|
|
## 2. La figure
|
|
|
|
|
|
## 2. La figure
|
|
|
La figure est divisée en une grille de 1 colonne et 4 lignes, dont une ligne plus haute pour le graphique, les ratios sont précisés avec l'argument gridspec_kw, par mots-clés (KeyWord) : à la clé 'height_ratios' on associe un vecteur avec les proportions . Deux autres lignes vont recevoir un curseur, pour éviter les superpositions de texte on a défini entre le graphique et les sliders une ligne "vide"
|
|
|
La figure est divisée en une grille de 1 colonne et 4 lignes, dont une ligne plus haute pour le graphique, les ratios sont précisés avec l'argument gridspec_kw, par mots-clés (KeyWord) : à la clé 'height_ratios' on associe un vecteur avec les proportions . Deux autres lignes vont recevoir un curseur, pour éviter les superpositions de texte on a défini entre le graphique et les sliders une ligne "vide"
|
|
|
|
|
|
### définition des vignettes
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
fig, (ax, ax_espace, ax_sn, ax_sT) = plt.subplots(nrows=4, ncols=1, gridspec_kw={'height_ratios':[6,1, 1,1]}, sharex=False)
|
|
|
```
|
|
|
Ensuite, il faut enlever les traits matérialisant les vignettes et rendre la vignette "intercalaire" transparente, afin qu'elle ne masque pas ce qui débord des autres axes.
|
|
|
``` python
|
|
|
```
|
|
|
|
|
|
Ensuite, il faut enlever les traits matérialisant les vignettes et rendre la vignette "intercalaire" transparente, afin qu'elle ne masque pas ce qui débord des autres axes.
|
|
|
|
|
|
```python
|
|
|
for ax_s in [ax_espace, ax_sn, ax_sT]:
|
|
|
ax_s.xaxis.set_visible(False)
|
|
|
ax_s.yaxis.set_visible(False)
|
... | ... | @@ -142,32 +141,28 @@ for pos in ['right', 'top', 'bottom', 'left']: |
|
|
|
|
|
(...)
|
|
|
plt.show()
|
|
|
```
|
|
|
```
|
|
|
|
|
|
### première vignette, la courbe
|
|
|
|
|
|
En haut, on a donc une vignette "ax" dans laquelle on va tracer deux courbes avec "plot", avec les résultats qui viennent d'être calculés. On nomme le premier objet retourné par la fonction "plot", puisqu'on aura besoin d'agir sur lui : on remplacera les données (vecteurs des x et vecteurs des y) après chaque action sur les sliders.
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
courbe_exactement, = ax.plot(range(p + 1), liste_probas_exactement, label="exactement k crues",ls='None', marker='o', markersize=10))
|
|
|
courbe_cumul, = ax.plot(range(p + 1), liste_probas_auplus, label="k crues ou moins de k",ls='None', marker='o', markersize=10)).
|
|
|
```
|
|
|
```
|
|
|
|
|
|
### définition des sliders et liaison avec une fonction par widget de "mise à jour de la courbe"
|
|
|
|
|
|
Sous la vignette "ax" il y a une vignette intercalaire "ax_espace" dans laquelle on n'ajoute rien, puis les vignettes des deux sliders.
|
|
|
Les arguments utilisés ici pour les sliders sont :
|
|
|
|
|
|
nom_du_slider = Slider(
|
|
|
ax_ou_le_slider_est_installe, "titre", valmin=valeur_mini, valmax = valeur_max, valstep=incrément, valfmt=format précédé de % : '%0.2f', valinit=valeur de départ, color="nom_de_couleur")
|
|
|
Sous la vignette "ax" il y a une vignette intercalaire "ax_espace" dans laquelle on n'ajoute rien, puis les vignettes des deux sliders. Les arguments utilisés ici pour les sliders sont :
|
|
|
|
|
|
nom_du_slider = Slider( ax_ou_le_slider_est_installe, "titre", valmin=valeur_mini, valmax = valeur_max, valstep=incrément, valfmt=format précédé de % : '%0.2f', valinit=valeur de départ, color="nom_de_couleur")
|
|
|
|
|
|
Comme on veut des entiers, on définit pour valstep un incrément égal à 1, et comme la valeur prise sera quand même un flottant on l'affiche avec zéro chiffre après la virgule.
|
|
|
Comme on veut des entiers, on définit pour valstep un incrément égal à 1, et comme la valeur prise sera quand même un flottant on l'affiche avec zéro chiffre après la virgule.
|
|
|
|
|
|
On définit enfin la fonction qui sera appelée si on change le curseur du slider :
|
|
|
nom_du_slider.on_changed(fonction_appellée)
|
|
|
On définit enfin la fonction qui sera appelée si on change le curseur du slider : nom_du_slider.on_changed(fonction_appellée)
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
# nombre d'années d'observation
|
|
|
slider_n = Slider(
|
|
|
ax_sn, "nombre d'années d'observation ", valmin=1, valmax = 1000, valfmt='%0.0f', valinit=n, color="green")
|
... | ... | @@ -180,25 +175,21 @@ slider_T = Slider( |
|
|
ax_sT, "période de retour T ", 2, 1000, valinit=T, valstep=1, valfmt='%0.0f', color="blue")
|
|
|
|
|
|
slider_T.on_changed(update_graphique)
|
|
|
```
|
|
|
```
|
|
|
|
|
|
### définition de la fonction commune associée aux deux widgets
|
|
|
|
|
|
On a associé la même fonction, update_graphique, aux deux sliders car finalement ils vont tous les deux déclencher la même suite d'actions : recalcul et retracé.
|
|
|
|
|
|
Le slider_N (respectivement slider_T) récupère une valeur numérique et la transmet en tant qu'argument *val* à la fonction qui lui a été liée.
|
|
|
Les fonctions liées à un widget ne peuvent prendre qu'un argument, imposé par le type du widget. Pour un slider, l'argument ne peut être que la valeur du curseur, val par convention.
|
|
|
Le slider_N (respectivement slider_T) récupère une valeur numérique et la transmet en tant qu'argument _val_ à la fonction qui lui a été liée. Les fonctions liées à un widget ne peuvent prendre qu'un argument, imposé par le type du widget. Pour un slider, l'argument ne peut être que la valeur du curseur, val par convention.
|
|
|
|
|
|
Cependant, on appelle cette fonction soit depuis slider_N soit depuis slider_T : il n'est pas possible d'utiliser l'argument passé val car dans la fonction appelée on ne sait pas quel est le curseur qui a été activé, cela peut être slider_N ou slider_T.
|
|
|
Heureusement, on peut appeler directement la valeur de chaque curseur en tant qu'attribut ([ source utilisée ](http://www.math.buffalo.edu/~badzioch/MTH337/PT/PT-matplotlib_slider/PT-matplotlib_slider.html)):
|
|
|
Cependant, on appelle cette fonction soit depuis slider_N soit depuis slider_T : il n'est pas possible d'utiliser l'argument passé val car dans la fonction appelée on ne sait pas quel est le curseur qui a été activé, cela peut être slider_N ou slider_T. Heureusement, on peut appeler directement la valeur de chaque curseur en tant qu'attribut ([ source utilisée ](http://www.math.buffalo.edu/\~badzioch/MTH337/PT/PT-matplotlib_slider/PT-matplotlib_slider.html)):
|
|
|
|
|
|
n = slider_N.val
|
|
|
T = slider_T.val
|
|
|
n = slider_N.val T = slider_T.val
|
|
|
|
|
|
Enfin, on a choisi que les sliders modifient la valeur de N et de T pour tout le code. Pour cela, on précise dans la fonction que l'on travaille avec les deux variables N et T au niveau global, avec l'instruction
|
|
|
**global** N, T
|
|
|
Enfin, on a choisi que les sliders modifient la valeur de N et de T pour tout le code. Pour cela, on précise dans la fonction que l'on travaille avec les deux variables N et T au niveau global, avec l'instruction **global** N, T
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
def update_graphique(val):
|
|
|
global n, T
|
|
|
n = int(slider_n.val) # on précise que ce sont des entiers pour l'utilisation dans C(n,p)
|
... | ... | @@ -210,27 +201,27 @@ def update_graphique(val): |
|
|
courbe_cumul.set_data(range(k + 1), liste_probas_auplus)
|
|
|
ax.set_xlim(0, k+1)
|
|
|
fig.canvas.draw_idle()
|
|
|
```
|
|
|
```
|
|
|
|
|
|
Le recalcul ne présente pas de difficulté ; on modifie ensuite les données des courbes avec set_data, ce qui revient à leur passer le nouveau vecteur des x (entiers de 0 à p) et des y (probabilités pour p ou cumulées jusque p) . On modifie aussi le titre et l'étendue de l'axe des x.
|
|
|
fig.canvas.draw_idle() force les objets modifiés à se retracer.
|
|
|
Le recalcul ne présente pas de difficulté ; on modifie ensuite les données des courbes avec set_data, ce qui revient à leur passer le nouveau vecteur des x (entiers de 0 à p) et des y (probabilités pour p ou cumulées jusque p) . On modifie aussi le titre et l'étendue de l'axe des x. fig.canvas.draw_idle() force les objets modifiés à se retracer.
|
|
|
|
|
|
:warning: les objets tracés avec **plot** sont très faciles à mettre à jour avec **set_data**. Ce n'est malheureusement pas le cas de tous les types de graphiques. Dans certains cas, il sera même peut-être plus simple d'effacer la courbe (on doit donc avoir la permission de le faire, par exemple avec global) et de la retracer.
|
|
|
|
|
|
#### remarque sur la portée des variables
|
|
|
#### remarque sur la portée des variables
|
|
|
|
|
|
Ni **fig** ni **ax** ni les courbes n'ont été passés en argument, pourtant ils sont connus depuis l'intérieur de la fonction. Comme c'est un petit code, on sait que l'on va manipuler les bons objets, il n'y a pas d'ambiguité. Dans la fonction, on ne pourrait pas écraser l'un ou l'autre objet, par contre si **on applique sur eux une méthode** (suptitle, set_data, set_xlim...) la modification de leurs attributs correspondant est répercutée.
|
|
|
|
|
|
## Variante avec une courbe "en escalier"
|
|
|
## Variante avec une courbe "en escalier"
|
|
|
|
|
|
On a réécrit un code avec la possibilité de représenter la courbe soit avec **plot** soit avec un escalier en utilisant **hlines** et **fillbetween**.
|
|
|
|
|
|
[exemple de graphique en escalier](https://gitlab.irstea.fr/christine.poulard/atelier-matplotlib/-/blob/master/ScE_-_Hydrologie_-_D%C3%A9mo_probas_crue_sur_N_ann%C3%A9es.png)
|
|
|
Pour passer d'une représentation à l'autre, il faut changer la valeur du booléen TRACE_PAR_PLOT (on rappelle que les valeurs du booléen sont False et True, avec une majuscule).
|
|
|
[exemple de graphique en escalier](https://gitlab.irstea.fr/christine.poulard/atelier-matplotlib/-/blob/master/ScE_-_Hydrologie_-_D%C3%A9mo_probas_crue_sur_N_ann%C3%A9es.png) Pour passer d'une représentation à l'autre, il faut changer la valeur du booléen TRACE_PAR_PLOT (on rappelle que les valeurs du booléen sont False et True, avec une majuscule).
|
|
|
|
|
|
### fonction "stair_plot"
|
|
|
|
|
|
En attendant que la fonction **stairs** soit implémentée dans la version stable, on a repris une solution proposée sur un forum
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
|
|
|
def stair_plot(x,y, couleur):
|
|
|
x = [*x, x[-1]] # duplicate last to draw last line
|
... | ... | @@ -238,17 +229,13 @@ def stair_plot(x,y, couleur): |
|
|
# fill under line : y with duplicated last value to draw last step as well ; hide edge
|
|
|
ax.fill_between(x, [*y, y[-1]],
|
|
|
step='post', facecolor=couleur, edgecolor=None, alpha=0.5)
|
|
|
|
|
|
```
|
|
|
```
|
|
|
|
|
|
### premier tracé
|
|
|
On distingue donc les deux cas.
|
|
|
Or remarque que dans le cas d'un **plot** on nomme les objets renvoyés car on va agir dessus.
|
|
|
Dans le cas d'un tracé en marches d'escalier, on va simplement effacer et retracer, donc on ne nomme pas.
|
|
|
Au passage, on en profite pour choisir quelle fonction update_graphique sera liée aux Sliders : update_graphique_plot est équivalente à la fonction de la version simple, tandis que update_graphique_stairs traite le cas d'un graphique en escalier.
|
|
|
|
|
|
On distingue donc les deux cas. Or remarque que dans le cas d'un **plot** on nomme les objets renvoyés car on va agir dessus. Dans le cas d'un tracé en marches d'escalier, on va simplement effacer et retracer, donc on ne nomme pas. Au passage, on en profite pour choisir quelle fonction update_graphique sera liée aux Sliders : update_graphique_plot est équivalente à la fonction de la version simple, tandis que update_graphique_stairs traite le cas d'un graphique en escalier.
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
if TRACE_PAR_PLOT:
|
|
|
courbe_exactement, = ax.plot(range(k + 1), liste_probas_exactement, label="exactement k crues",ls='None', marker='o', markersize=10)
|
|
|
courbe_cumul, = ax.plot(range(k + 1), liste_probas_auplus, label="k crues ou moins de k", ls='None', marker='o', markersize=10)
|
... | ... | @@ -257,18 +244,17 @@ else: |
|
|
stair_plot(range(p + 1),liste_probas_exactement, couleur="blue")
|
|
|
stair_plot(range(p + 1),liste_probas_auplus, couleur="sienna")
|
|
|
update_graphique = update_graphique_stairs
|
|
|
```
|
|
|
|
|
|
```
|
|
|
### fontion update, cas graphique_stairs
|
|
|
|
|
|
Les calculs sont les mêmes, mais au lieu de modifier une courbe on efface le contenu de la vignette avec "clear" et on retrace.
|
|
|
Plus intéressant : on veut que les étiquettes de k soient au milieu des classes. Pour cela, on utilise les Locator et Formattor :
|
|
|
Les calculs sont les mêmes, mais au lieu de modifier une courbe on efface le contenu de la vignette avec "clear" et on retrace. Plus intéressant : on veut que les étiquettes de k soient au milieu des classes. Pour cela, on utilise les Locator et Formattor :
|
|
|
|
|
|
- si p> 15 on place les ticks de 5 en 5 en commençant par 0.5 et les ticks secondaires de 1 en 1 depuis 0.5
|
|
|
- si p<= 15 on place les ticks de 1 en 1 en commençant par 0.5 et on enlève les ticks secondaires
|
|
|
- évidemment, on ne veut pas que les étiquettes soient "0.5, 1,5..." ; donc on les formate au format entier.
|
|
|
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
def update_graphique_stairs(val):
|
|
|
global n, T
|
|
|
n = int(slider_n.val)
|
... | ... | @@ -290,24 +276,23 @@ def update_graphique_stairs(val): |
|
|
ax.xaxis.set_minor_locator(plt.NullLocator())
|
|
|
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter("%d"))
|
|
|
fig.canvas.draw_idle()
|
|
|
```
|
|
|
|
|
|
```
|
|
|
|
|
|
# CODE **Chegodaiev.py**
|
|
|
|
|
|
## 1. Les fonctions paramétrées utilisées et leurs paramètres
|
|
|
|
|
|
Pour représenter de manière empirique les fréquences des points d'un échantillon, il existe plusieurs formules : de Hazen, de Tchegodayev (ou Chegodaiev)... Elles ne varient en fait que par deux paramètres, a et b.
|
|
|
|
|
|
### Les fonctions paramétrées
|
|
|
|
|
|
L'expression générale de la fréquence empirique d'une observation s'exprime uniquement en fonction de son rang si l'on classe les N observations par ordre décroissant, :
|
|
|
freq_depassement(obs) = ( rang(obs) - a ) / ( N + b )
|
|
|
L'expression générale de la fréquence empirique d'une observation s'exprime uniquement en fonction de son rang si l'on classe les N observations par ordre décroissant, : freq_depassement(obs) = ( rang(obs) - a ) / ( N + b )
|
|
|
|
|
|
On vérifie que pour les rangs petits proches de un on tend vers une fréquence nulle, pour des rangs proches de N on tend vers une fréquence de 1 en restant en dessous, et entre deux observations la différence de fréquence est constante, égale à 1 / (N+b) donc légèrement inférieure à 1/N.
|
|
|
|
|
|
On va raisonner dans la suite soit en fréquence soit en période de retour, notée T, si on suppose que l'échantillon correspond à des maxima annuels (une valeur par an), liées par T= 1/f si f = fréquence au dépassement = 1 - fréquence au non dépassement.
|
|
|
La fonction pour calculer les périodes de retour d'un échantillon donnée prend en argument N et les paramètres a et b ; elle s'écrit donc :
|
|
|
``` python
|
|
|
On va raisonner dans la suite soit en fréquence soit en période de retour, notée T, si on suppose que l'échantillon correspond à des maxima annuels (une valeur par an), liées par T= 1/f si f = fréquence au dépassement = 1 - fréquence au non dépassement. La fonction pour calculer les périodes de retour d'un échantillon donnée prend en argument N et les paramètres a et b ; elle s'écrit donc :
|
|
|
|
|
|
```python
|
|
|
def T_emp_parametre(n_annees, a , b):
|
|
|
|
|
|
plotting_positions_param = [((n_annees + b) / ((indice + 1) - a)) for indice in range(n_annees)]
|
... | ... | @@ -318,22 +303,20 @@ def T_emp_parametre(n_annees, a , b): |
|
|
print(f"Périodes de retour empiriques, a={a:.1f}, b={b:.1f}, N={N} : ", " / ".join([f"{freq:.2f}" for freq in plotting_positions_param]))
|
|
|
|
|
|
return plotting_positions_param
|
|
|
```
|
|
|
|
|
|
```
|
|
|
On remarque que indice + 1 = rang, car rang va de 1 à N tandis que indice va de 0 à N-1.
|
|
|
|
|
|
Pour la formulation de Tchegodayev, a = 0,3 et b = 0,4
|
|
|
On peut écrire une fonction spécifique (exemple du code fourni) ou faire des appels à la fonction ci-dessus en précisant les paramètres. On peut aussi donner à la fonction des valeurs par défaut qui correspondent à la formulation de Tchegodayev ; c'est sans doute une solution plus élégante !
|
|
|
Pour la formulation de Tchegodayev, a = 0,3 et b = 0,4 On peut écrire une fonction spécifique (exemple du code fourni) ou faire des appels à la fonction ci-dessus en précisant les paramètres. On peut aussi donner à la fonction des valeurs par défaut qui correspondent à la formulation de Tchegodayev ; c'est sans doute une solution plus élégante !
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
def T_emp_parametre_avec_defaut_Tchego(n_annees, a=0.3, b=0.4):
|
|
|
# fonction non inclue dans le code
|
|
|
|
|
|
plotting_positions_param = [((n_annees + b) / ((indice + 1) - a)) for indice in range(n_annees)]
|
|
|
print("Paramétré par curseur ", plotting_positions_param) # à mettre en commentaire
|
|
|
return plotting_positions_param
|
|
|
|
|
|
```
|
|
|
```
|
|
|
|
|
|
### Les variables du programme principal
|
|
|
|
... | ... | @@ -341,19 +324,17 @@ On a ici une difficulté ; les sliders vont permettre de modifier a, b et N ; le |
|
|
|
|
|
#### initialisations
|
|
|
|
|
|
On définit *a*, *b* et *N* dès le départ, avec une valeur par défaut.
|
|
|
Il faut également un échantillon de N valeurs. Dans cette version, on insiste sur le fait que la fréquence empirique d'une observation s'exprime uniquement en fonction de son rang ; on propose de créer un *échantillon* de N valeurs de 1 à N... ce qui correspondra de fait aux rangs !
|
|
|
Une autre solution, plus radicale, serait de décider que echantillon=[1]*N mais tous les graphiques seraient alors horizontaux et plus difficiles à lire... mais c'est une option quand même.
|
|
|
On définit _a_, _b_ et _N_ dès le départ, avec une valeur par défaut. Il faut également un échantillon de N valeurs. Dans cette version, on insiste sur le fait que la fréquence empirique d'une observation s'exprime uniquement en fonction de son rang ; on propose de créer un _échantillon_ de N valeurs de 1 à N... ce qui correspondra de fait aux rangs ! Une autre solution, plus radicale, serait de décider que echantillon=\[1\]\*N mais tous les graphiques seraient alors horizontaux et plus difficiles à lire... mais c'est une option quand même.
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
ech = [1]*10
|
|
|
ech
|
|
|
Out[7]: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
|
|
```
|
|
|
Enfin, plus en rapport avec l'objet initial de ce code, on peut tirer un échantillon au sort selon une loi de Gumbel. C'est ce que l'on utilise dans GenerateurCruesMaxAnnuelles.py.
|
|
|
Le code comprend ces 3 fonctions, et une ligne de code indique quelle fonction on assigne à methode_echantillonnage ; ici methode_echantillonnage = echantillon_uniforme. En quelque sorte, methode_echantillonnage est un alias de la fonction choisie, et c'est cet alias qui est utilisé ensuite dans le code. Ainsi, pour changer de fonction, il suffit de changer une seule ligne.
|
|
|
```
|
|
|
|
|
|
``` python
|
|
|
Enfin, plus en rapport avec l'objet initial de ce code, on peut tirer un échantillon au sort selon une loi de Gumbel. C'est ce que l'on utilise dans GenerateurCruesMaxAnnuelles.py. Le code comprend ces 3 fonctions, et une ligne de code indique quelle fonction on assigne à methode_echantillonnage ; ici methode_echantillonnage = echantillon_uniforme. En quelque sorte, methode_echantillonnage est un alias de la fonction choisie, et c'est cet alias qui est utilisé ensuite dans le code. Ainsi, pour changer de fonction, il suffit de changer une seule ligne.
|
|
|
|
|
|
```python
|
|
|
|
|
|
def echantillon_uniforme(nb):
|
|
|
return sorted(range(1, nb+1), reverse=True)
|
... | ... | @@ -367,14 +348,11 @@ def echantillon_gumbel(nb): |
|
|
x0 = 196
|
|
|
gr = 102
|
|
|
return sorted(gumbel(loc=x0, scale=gr, size=nb))
|
|
|
```
|
|
|
```
|
|
|
|
|
|
On remarque que l'on trie l'échantillon dès la création, avec la fonction **sorted** avec son attribut reverse ; elle ne change pas le conteneur qui lui est passé mais renvoie un conteneur avec les valeurs triées. La méthode **sort** trie aussi des conteneurs mais l'emploi est différent ; ma_liste.sort()) trie ma_liste _en place_ (elle change le conteneur et ne retourne rien).
|
|
|
On définit ensuite les paramètres de Tchégodaiev en une instructions grâce aux propriétés des tuples : **a_Tchego**, **b_Tchego** = 0.3 , 0.4""
|
|
|
Ensuite on définit **a** et **b** = en les initialisant avec les valeurs de Tchégodaiev, et on définit plus arbitraitrement **N** pour le premier tracé.
|
|
|
Le booléen **en_periode_de_retour** permettra de gérer l'affichage sur l'axe des x, en fréquence ou converti en période de retour.
|
|
|
On remarque que l'on trie l'échantillon dès la création, avec la fonction **sorted** avec son attribut reverse ; elle ne change pas le conteneur qui lui est passé mais renvoie un conteneur avec les valeurs triées. La méthode **sort** trie aussi des conteneurs mais l'emploi est différent ; ma_liste.sort()) trie ma_liste _en place_ (elle change le conteneur et ne retourne rien). On définit ensuite les paramètres de Tchégodaiev en une instructions grâce aux propriétés des tuples : **a_Tchego**, **b_Tchego** = 0.3 , 0.4"" Ensuite on définit **a** et **b** = en les initialisant avec les valeurs de Tchégodaiev, et on définit plus arbitraitrement **N** pour le premier tracé. Le booléen **en_periode_de_retour** permettra de gérer l'affichage sur l'axe des x, en fréquence ou converti en période de retour.
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
a_Tchego, b_Tchego = 0.3 , 0.4 # affectation de plusieurs valeurs simultanées par tuples
|
|
|
# initialisation des paramètres de départ (modifiables par curseur)
|
|
|
a = a_Tchego
|
... | ... | @@ -387,32 +365,27 @@ en_periode_de_retour = True |
|
|
|
|
|
#### notion de portée des variables dans une fonction, mot-clé "global"
|
|
|
|
|
|
Depuis les fonctions, les valeurs de *a*, *b* et *N* sont connues : si on ne définit pas une variable, le code va chercher si elle n'est pas définie au niveau supérieur.
|
|
|
Cependant, on a quand même défini ces variables explicitement comme **arguments des fonctions**, d'autant plus qu'un des buts du code est de montrer que les fréquences ou périodes de retour calculées ne dépendent que du rang de l'observation et pas de la fonction.
|
|
|
Depuis les fonctions, les valeurs de _a_, _b_ et _N_ sont connues : si on ne définit pas une variable, le code va chercher si elle n'est pas définie au niveau supérieur. Cependant, on a quand même défini ces variables explicitement comme **arguments des fonctions**, d'autant plus qu'un des buts du code est de montrer que les fréquences ou périodes de retour calculées ne dépendent que du rang de l'observation et pas de la fonction.
|
|
|
|
|
|
Des curseurs vont permettre de modifier les valeurs de _a_, _b_ et _N_ ; ces modifications devront être transmises à des fonctions pour recalculer et retracer le graphique.\
|
|
|
Il y a deux façons de répercuter les modifications de a, b et N dans tout le programme (en fait, sans doute plus...):
|
|
|
|
|
|
Des curseurs vont permettre de modifier les valeurs de *a*, *b* et *N* ; ces modifications devront être transmises à des fonctions pour recalculer et retracer le graphique.
|
|
|
Il y a deux façons de répercuter les modifications de a, b et N dans tout le programme (en fait, sans doute plus...):
|
|
|
- on va déclarer dans les fonctions activées par les sliders qu'on travaille sur a, b ou N "global", c'est à dire que si on les modifie dans la fonction la modification sera répercutée dans la variable au niveau du programme principal (ce qui n'est pas le cas sinon).
|
|
|
- on pourrait travailler dans le code avec l'attribut "val" des sliders, à condition que ces sliders soient bien connus du programme principal, ce qui est le cas ici car ils sont définis dans le corps du programme .
|
|
|
|
|
|
|
|
|
C'est sans doute à discuter : quelle méthode est la plus claire ? la plus sûre ?
|
|
|
|
|
|
|
|
|
## 2. La figure
|
|
|
La figure est divisée en une grille de 1 colonne et composée de 5 lignes, dont une ligne plus haute pour le graphique. 3 des autres lignes vont recevoir un curseur, et la dernière une case à cocher. On va donc définir le nom des espaces pour le tracé ("Axes"), en fonction de leur rôle.
|
|
|
On a spécifié une marge de 15% en haut pour permettre l'affichage d'un titre de figure et d'un titre de la vignette du haut, ax. On mettra à jour les valeurs des paramètres dans le titre de ax pour vérifier que les signaux se transmettent bien.
|
|
|
|
|
|
|
|
|
La figure est divisée en une grille de 1 colonne et composée de 5 lignes, dont une ligne plus haute pour le graphique. 3 des autres lignes vont recevoir un curseur, et la dernière une case à cocher. On va donc définir le nom des espaces pour le tracé ("Axes"), en fonction de leur rôle. On a spécifié une marge de 15% en haut pour permettre l'affichage d'un titre de figure et d'un titre de la vignette du haut, ax. On mettra à jour les valeurs des paramètres dans le titre de ax pour vérifier que les signaux se transmettent bien.
|
|
|
|
|
|
### code définissant la grille et le tracé des deux premières courbes
|
|
|
On remarque que l'on a nommé le *premier objet renvoyé par la fonction plot*, respectivement _courbe_estim_Tchego_ et _courbe_estim_param_, afin de pouvoir appliquer des méthodes sur ces objets, à savoir modifier le vecteur des x et le vecteur des y donnant les coordonnées des points. Comme _plot_ renvoie plusieurs objets, on ajoute une virgule après le nom donné à la variable qui nous intéresse, ce qui est équivalent à :
|
|
|
nom_du_premier_objet_du_tuple*,* reste_du_tuple_qui_ne_m_interesse_pas = ax.plot(mes_arguments)
|
|
|
|
|
|
La représentation des T (ou f) empiriques de Tchégodaiev se fera par des verticales, avec la fonction vlines. Dans l'e'autre code, on a choisi plutôt "stem" qui combine une ligne et un marker. La méthode "stem" n'était pas très performante il y a peu (trop de points => erreur mémoire) mais elle a été améliorée, donc il était intéressant de la tester. Autre différence, ici on modifie vlines avec une fonction update_vlines trouvée dans un forum au lieu d'effacer et de refaire. Cette fonction update_vlines pose problème pour utiliser des lignes de hauteur différentes (ymax = array), donc finalement effacer et refaire serait plus judicieux.
|
|
|
On remarque que l'on a nommé le _premier objet renvoyé par la fonction plot_, respectivement _courbe_estim_Tchego_ et _courbe_estim_param_, afin de pouvoir appliquer des méthodes sur ces objets, à savoir modifier le vecteur des x et le vecteur des y donnant les coordonnées des points. Comme _plot_ renvoie plusieurs objets, on ajoute une virgule après le nom donné à la variable qui nous intéresse, ce qui est équivalent à : nom_du_premier_objet_du_tuple\*,\* reste_du_tuple_qui_ne_m_interesse_pas = ax.plot(mes_arguments)
|
|
|
|
|
|
La représentation des T (ou f) empiriques de Tchégodaiev se fera par des verticales, avec la fonction vlines. Dans l'e'autre code, on a choisi plutôt "stem" qui combine une ligne et un marker. La méthode "stem" n'était pas très performante il y a peu (trop de points => erreur mémoire) mais elle a été améliorée, donc il était intéressant de la tester. Autre différence, ici on modifie vlines avec une fonction update_vlines trouvée dans un forum au lieu d'effacer et de refaire. Cette fonction update_vlines pose problème pour utiliser des lignes de hauteur différentes (ymax = array), donc finalement effacer et refaire serait plus judicieux.
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
fig_pp, (ax, ax_slide_a, ax_slide_b, ax_slide_n, ax_chb) = plt.subplots(nrows=5, ncols=1, gridspec_kw={'height_ratios': [10, 1, 1, 1,2]})
|
|
|
plt.subplots_adjust(wspace=1, hspace=0.5,left=0.1,top=0.85,right=0.9,bottom=0.1)
|
|
|
fig_pp.canvas.set_window_title("Démo périodes de retour 'empiriques'")
|
... | ... | @@ -432,38 +405,29 @@ courbe_estim_param, = ax.plot(T_emp_parametre(N, a, b), echantillon, color='blu |
|
|
# pour faire tourner ce code seul, après avoir défini a, b et N, on ajouterait la légende et plt.show()
|
|
|
# ax.legend(title="formules", bbox_to_anchor=(1.05, 0.10), loc='lower right')
|
|
|
# plt.show()
|
|
|
|
|
|
```
|
|
|
|
|
|
### remarque sur la prise en compte des actions des widgets après show()
|
|
|
|
|
|
Après le **plt.show()**, la figure est tracée ; cependant, les actions sur les différents widgets vont la modifier. Après chaque action qui modifie la figure, par appel d'une méthode sur l'un de ses objets, il faudra donc appeler une méthode qui met à jour si l'on veut que cela soit pris en compte:
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
fig_pp.canvas.draw_idle()
|
|
|
```
|
|
|
```
|
|
|
|
|
|
### définition des widgets et liaison avec une fonction par widget
|
|
|
|
|
|
Entre la définition de la figure et de ax et le show(), on définit ce qui se passe dans les 4 autres lignes de la grille, où l'on va placer les sliders.
|
|
|
On va d'abord enlever le cadre des vignettes du bas, avec ax.xaxis/yaxis.set_visible(False)
|
|
|
Ensuite on définit les widgets, les arguments utilisés ici sont :
|
|
|
*pour les sliders*
|
|
|
nom_du_slider = Slider(
|
|
|
ax_ou_le_slider_est_installe, "titre", valmin=valeur_mini, valmax = valeur_max, valfmt=format précédé de % : '%0.2f', valinit=valeur de départ, color="nom_de_couleur")
|
|
|
Entre la définition de la figure et de ax et le show(), on définit ce qui se passe dans les 4 autres lignes de la grille, où l'on va placer les sliders. On va d'abord enlever le cadre des vignettes du bas, avec ax.xaxis/yaxis.set_visible(False) Ensuite on définit les widgets, les arguments utilisés ici sont : _pour les sliders_ nom_du_slider = Slider( ax_ou_le_slider_est_installe, "titre", valmin=valeur_mini, valmax = valeur_max, valfmt=format précédé de % : '%0.2f', valinit=valeur de départ, color="nom_de_couleur")
|
|
|
|
|
|
On définit la fonction qui sera appelée si on change le curseur du slider :
|
|
|
nom_du_slider.on_changed(fonction_appellée)
|
|
|
On définit la fonction qui sera appelée si on change le curseur du slider : nom_du_slider.on_changed(fonction_appellée)
|
|
|
|
|
|
Pour le slider qui gouverne N, il y a deux spécificités : comme on veut un entier, on définit comme arguments supplémentaires valstep, la liste des valeurs permises (on transforme la saisie continue en saisie discrète), et comme ce sont des flottants on définit un affichage avec zéro chiffre après la virgule. Il existe un autre exemple dans le projet ST2shape,
|
|
|
|
|
|
*pour la checkbox*
|
|
|
nom_du_checkbox= CheckButtons(ax_ou_le_checkbox_est_installe, [ liste des titres des items"], actives= [liste des entiers = indices des checkbox activées au départ]).
|
|
|
_pour la checkbox_ nom_du_checkbox= CheckButtons(ax_ou_le_checkbox_est_installe, \[ liste des titres des items"\], actives= \[liste des entiers = indices des checkbox activées au départ\]).
|
|
|
|
|
|
On définit la fonction qui sera appelée si on clique sur la case à cocher :
|
|
|
nom_du_checkbox.on_clicked(fonction_appellée)
|
|
|
On définit la fonction qui sera appelée si on clique sur la case à cocher : nom_du_checkbox.on_clicked(fonction_appellée)
|
|
|
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
for ax_s in [ax_slide_a, ax_slide_b, ax_slide_n, ax_chb]:
|
|
|
ax_s.xaxis.set_visible(False)
|
|
|
ax_s.yaxis.set_visible(False)
|
... | ... | @@ -494,41 +458,33 @@ slider_n.on_changed(update_N) |
|
|
|
|
|
chb_enT = CheckButtons(ax_chb, [ "Période de retour (années) ; sinon fréquence (de 0 à 1)"], [False])
|
|
|
chb_enT.on_clicked(switch_freq)
|
|
|
|
|
|
```
|
|
|
|
|
|
Si on exécute le code, on voit déjà la courbe initiale et les widgets mais ils ne sont pas actifs. On remarque que la case à cocher du checkbutton est en fait un très vilain rectangle aplati...
|
|
|
|
|
|
### définition des fonctions associées aux widgets, version une par widget
|
|
|
|
|
|
Les fonctions liées à un widget ne peuvent prendre qu'un argument, imposé par le type du widget. Pour un slider, l'argument ne peut être que la valeur du curseur.
|
|
|
De plus, on relie le widget à la fonction, donc sans parenthèses, on ne peut pas passer des arguments au moment de l'association !
|
|
|
Les fonctions liées à un widget ne peuvent prendre qu'un argument, imposé par le type du widget. Pour un slider, l'argument ne peut être que la valeur du curseur. De plus, on relie le widget à la fonction, donc sans parenthèses, on ne peut pas passer des arguments au moment de l'association !
|
|
|
|
|
|
Si j'écrivais mon_widget.on_changed(ma_fonction(a=0.3, b= 1.4)) j'associerais le widget à ce que retourne la fonction avec a=0.3 et b=0.4, ce qui a peu de chances d'être une fonction...
|
|
|
|
|
|
Dans un premier temps, on va se débrouiller en définissant une fonction par widget, et en utilisant les variables globales dans les fonctions (mot-clef *global*)
|
|
|
Dans un premier temps, on va se débrouiller en définissant une fonction par widget, et en utilisant les variables globales dans les fonctions (mot-clef _global_)
|
|
|
|
|
|
*pour les sliders*
|
|
|
Le slider_a (respectivement slider_b) récupère une valeur de a (resp. b) et la transmet en tant qu'argument *val* à la fonction qui lui a été liée, ici respectivement update_a et update_b.
|
|
|
_pour les sliders_ Le slider_a (respectivement slider_b) récupère une valeur de a (resp. b) et la transmet en tant qu'argument _val_ à la fonction qui lui a été liée, ici respectivement update_a et update_b.
|
|
|
|
|
|
Si on change a ou b, il suffit de retracer la fonction paramétrée par a et b, celle de Tchegodayev ne change pas. On va passer par des fonctions intermédiaires retracer_parametree et retracer_Tchego que l'on décrira dans la paragraphe suivant.
|
|
|
|
|
|
Pour que l'on puisse changer la valeur de *a* pour tout le code, et pas seulement en tant qu'argument pour retracer_parametree(echantillon, a, b), on a choisi de préciser dans la fonction que l'on travaille avec la variable de niveau global:
|
|
|
global a # respectivement global b
|
|
|
Ainsi, quand on écrit ensuite a = val, on remplace donc la valeur pour tout le corps du programme ("main").
|
|
|
Pour que l'on puisse changer la valeur de _a_ pour tout le code, et pas seulement en tant qu'argument pour retracer_parametree(echantillon, a, b), on a choisi de préciser dans la fonction que l'on travaille avec la variable de niveau global: global a # respectivement global b Ainsi, quand on écrit ensuite a = val, on remplace donc la valeur pour tout le corps du programme ("main").
|
|
|
|
|
|
Si on change N, l'échantillon change et il faut retracer les deux courbes, et redimensionner l'axe des y.
|
|
|
Dans cette fonction, on remarque qu'il n'était pas nécessaire de passer *ax* en argument, on travaille l'objet ax du main et on va lui appliquer des méthodes, ce que l'on peut faire même dans une fonction - c'est remplacer complètement un objet avec une assignation ( = ) qui n'a qu'une portée locale.
|
|
|
Si on suit la même logique, on n'a pas besoin non plus de préciser a, b et même N comme arguments des fonctions T_emp_parametre et T_emp_Tchego puisque la fonction va aller chercher ces variables "manquantes" dans le code principal. Cependant, il est toujours intéressant de voir quels sont les arguments, surtout si l'on voulait en faire une utilisation pédagogique.
|
|
|
Si on change N, l'échantillon change et il faut retracer les deux courbes, et redimensionner l'axe des y. Dans cette fonction, on remarque qu'il n'était pas nécessaire de passer _ax_ en argument, on travaille l'objet ax du main et on va lui appliquer des méthodes, ce que l'on peut faire même dans une fonction - c'est remplacer complètement un objet avec une assignation ( = ) qui n'a qu'une portée locale. Si on suit la même logique, on n'a pas besoin non plus de préciser a, b et même N comme arguments des fonctions T_emp_parametre et T_emp_Tchego puisque la fonction va aller chercher ces variables "manquantes" dans le code principal. Cependant, il est toujours intéressant de voir quels sont les arguments, surtout si l'on voulait en faire une utilisation pédagogique.
|
|
|
|
|
|
Pour le slider, il existe un autre exemple dans le projet ST2shape, avec comme arguments supplémentaires la liste des valeurs permises (on transforme la saisie continue en saisie discrète).
|
|
|
|
|
|
*pour la checkbox*
|
|
|
Une action sur la checkbox fait passer le booléen en_periode_de_retour de True à False ou de False à True. Cela peut s'écrire avec des tests (*if ma_checkbox == True:* ou même *if ma_checkbox :* puis (*if ma_checkbox == False:* ou même *if not ma_checkbox :* ou bien plus simplement en disant que la valeur est à présent le contraire de la valeur initiale : mon_booleen = *not* mon_booleen.
|
|
|
Dans le code GenerateurCruesMaxAnnuelles.py on utilise un bouton à la place de cette checkbox, car son rendu est tout à fait raté... La checkbox permet de voir si l'option est cochée ou pas ; pour avoir la même information sur un bouton il suffit de changer le texte à chaque fois qu'on l'active (T => f ou f => T) ; dans les deux cas il est important de changer aussi le titre de l'axe des x.
|
|
|
_pour la checkbox_ Une action sur la checkbox fait passer le booléen en_periode_de_retour de True à False ou de False à True. Cela peut s'écrire avec des tests (_if ma_checkbox == True:_ ou même _if ma_checkbox :_ puis (_if ma_checkbox == False:_ ou même _if not ma_checkbox :_ ou bien plus simplement en disant que la valeur est à présent le contraire de la valeur initiale : mon_booleen = _not_ mon_booleen. Dans le code GenerateurCruesMaxAnnuelles.py on utilise un bouton à la place de cette checkbox, car son rendu est tout à fait raté... La checkbox permet de voir si l'option est cochée ou pas ; pour avoir la même information sur un bouton il suffit de changer le texte à chaque fois qu'on l'active (T => f ou f => T) ; dans les deux cas il est important de changer aussi le titre de l'axe des x.
|
|
|
|
|
|
On retrace les fonctions, dans lesquelles la valeur de en_periode_de_retour sera prise en compte pour le calcul.
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
def update_a(val):
|
|
|
global a
|
|
|
a = val
|
... | ... | @@ -558,44 +514,38 @@ def switch_freq(label): |
|
|
#ax.set_ylim(bottom=0, top=N * 1.1) #améliorable ; ; définir xlim aussi
|
|
|
|
|
|
fig_pp.canvas.draw_idle()
|
|
|
```
|
|
|
```
|
|
|
|
|
|
### autre manière de définir des fonctions associées aux widgets, avec une fonction commune pour a et b ?
|
|
|
[ source utilisée ](http://www.math.buffalo.edu/~badzioch/MTH337/PT/PT-matplotlib_slider/PT-matplotlib_slider.html)
|
|
|
|
|
|
Les fonctions update_a et update_b sont très proches, et pourraient partager une même fonction liée, comme dans le programme ProbaCrueMaxAn_SurNannees.
|
|
|
On rappelle que l'on peut récupérer la valeur d'un slider, car elle est aussi un argument : *mon_slider.val*
|
|
|
[source utilisée](http://www.math.buffalo.edu/\~badzioch/MTH337/PT/PT-matplotlib_slider/PT-matplotlib_slider.html)
|
|
|
|
|
|
``` python
|
|
|
Les fonctions update_a et update_b sont très proches, et pourraient partager une même fonction liée, comme dans le programme ProbaCrueMaxAn_SurNannees. On rappelle que l'on peut récupérer la valeur d'un slider, car elle est aussi un argument : _mon_slider.val_
|
|
|
|
|
|
```python
|
|
|
def update_a_ou_b(val):
|
|
|
# solution avec les variables globales
|
|
|
global a,b
|
|
|
a = slider_a.val
|
|
|
b= slider_b.val
|
|
|
retracer_parametree(echantillon, a, b)
|
|
|
```
|
|
|
|
|
|
```
|
|
|
On peut aussi imaginer se débarrasser des variables globales : on pourrait utiliser slider_a.val et slider_b.val, puisqu'ils ne sont modifiés que par les curseurs.
|
|
|
Il faudrait vérifier ce qui se passe pour le premier tracé : les sliders devraient avoir été définis avant.
|
|
|
|
|
|
On peut aussi imaginer se débarrasser des variables globales : on pourrait utiliser slider_a.val et slider_b.val, puisqu'ils ne sont modifiés que par les curseurs. Il faudrait vérifier ce qui se passe pour le premier tracé : les sliders devraient avoir été définis avant.
|
|
|
|
|
|
### définition des fonctions pour retracer les courbes, appelées par les fonctions associées aux widgets
|
|
|
|
|
|
On a vu au-dessus que changer a ou b amène à retracer une seule des courbes, la même dans les deux cas, tandis que changer N ou le booleen en_periode_de_retour demande de retracer les deux.
|
|
|
Pour factoriser le code, on va donc définir séparément une fonction pour retracer la courbe paramétrée et une autre pour la courbe d'après Tchegodayev.
|
|
|
On a vu au-dessus que changer a ou b amène à retracer une seule des courbes, la même dans les deux cas, tandis que changer N ou le booleen en_periode_de_retour demande de retracer les deux. Pour factoriser le code, on va donc définir séparément une fonction pour retracer la courbe paramétrée et une autre pour la courbe d'après Tchegodayev.
|
|
|
|
|
|
Pour une courbe simple, avec la méthode *plot*, il est facile de redéfinir les données, vecteurs x et y, associées aux courbes, avec la méthode set_data.
|
|
|
Pour une courbe de type *vlines* c'est plus compliqué : il s'agit d'une collection de lignes. Heureusement, on a trouvé sur un forum une solution toute prête, avec la fonction update_vlines qui utiise set_segments.
|
|
|
Pour une courbe simple, avec la méthode _plot_, il est facile de redéfinir les données, vecteurs x et y, associées aux courbes, avec la méthode set_data. Pour une courbe de type _vlines_ c'est plus compliqué : il s'agit d'une collection de lignes. Heureusement, on a trouvé sur un forum une solution toute prête, avec la fonction update_vlines qui utiise set_segments.
|
|
|
|
|
|
Dans chaque cas, on va définir différemment le vecteur des x selon la valeur de en_periode_de_retour, soit le vecteur de périodes de retour renvoyé par T_emp_Tchego(len(echantillon), soit le vecteur qui contient l'inverse de chaque élément.
|
|
|
|
|
|
Comme on n'appelle jamais retracer_Tchego sans appeler ensuite retracer_parametree, on ne va écrire les instructions de modification du titre (ax.set_title) et des limites de l'axe des x (ax.set_xlim) et enfin fig_pp.canvas.draw_idle().
|
|
|
|
|
|
Pour le titre, on utilise les *f_string* : f précède la chaîne de caractère pour annoncer qu'il faudra interpréter les variables formattées placées entre accolades à l'intérieur, {nom_de_variable:code_format}.
|
|
|
f"a={a:.2f}" signifie que l'on va écrire "a=" suivi de la valeur de a, comme flottant (f) avec deux chiffres après la virgule.
|
|
|
|
|
|
Pour le titre, on utilise les _f_string_ : f précède la chaîne de caractère pour annoncer qu'il faudra interpréter les variables formattées placées entre accolades à l'intérieur, {nom_de_variable:code_format}. f"a={a:.2f}" signifie que l'on va écrire "a=" suivi de la valeur de a, comme flottant (f) avec deux chiffres après la virgule.
|
|
|
|
|
|
``` python
|
|
|
```python
|
|
|
|
|
|
def update_vlines(courbe_en_vlines, x, ymin=None, ymax=None):
|
|
|
# mise à jour de courbes de type vlines
|
... | ... | @@ -642,7 +592,6 @@ def retracer_parametree(echantillon, a, b): |
|
|
#ax.set_xbounds(lower=min(T_emp), upper=max(T_emp))
|
|
|
|
|
|
fig_pp.canvas.draw_idle()
|
|
|
```
|
|
|
```
|
|
|
|
|
|
Le code Chegodaiev.py est fourni, n'hésitez pas à le modifier à votre sauce, éventuellement à tester d'autres widgets, et à me faire remonter vos remarques.
|
|
|
Pour un autre exemple, intégré à un projet bâti autour d'une interface Tkinter, voir ST2Shape. |
|
|
\ No newline at end of file |
|
|
Le code Chegodaiev.py est fourni, n'hésitez pas à le modifier à votre sauce, éventuellement à tester d'autres widgets, et à me faire remonter vos remarques. Pour un autre exemple, intégré à un projet bâti autour d'une interface Tkinter, voir ST2Shape.![Sinus_modifiable](uploads/aa1f7671490ea6eecddd2a91bda8b21c/Sinus_modifiable.JPG) |
|
|
\ No newline at end of file |