|
|
# "Widgets" directement avec matplotlib
|
|
|
|
|
|
Cette session "G" a été ajoutée pour permettre de manipuler les widgets sur un exemple simple, sans avoir à lire un fichier ni utiliser les rééchantillonnages (une difficulté à la fois...).
|
|
|
On en profitera quand même pour ajouter une manipulation de variable globale, pour pouvoir changer la valeur d'une variable dans une fonction. Cela est à utiliser avec précaution toutefois...
|
|
|
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...). On signale ici qu'il existe aussi une bibliothèque iPython pour ajouter des widgets à un notebook Jupyter (non testé ici).
|
|
|
|
|
|
|
|
|
Dans cet exemple, on cherche à montrer comment se comporte une formule paramétrique si on change les paramètres. L'utilisateur pourra modifier les valeurs de ces paramètres par des curseurs (widget Slider) et changer la définition de l'axe des x (en fréquence ou en période de retour = 1/f) avec une case à cocher (CheckButtons).
|
|
|
|
|
|
## Les fonctions paramétrées et leurs paramètres
|
|
|
|
|
|
### Les fonctions paramétrées
|
|
|
Pour représenter de manière empirique les fréquences des points d'un échantillon, il existe plusieurs formules : de Hazen, de Tchegodayev...
|
|
|
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(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.
|
|
|
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)]
|
|
|
print("Paramétré par curseur ", plotting_positions_param) # à mettre en commentaire
|
|
|
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 !
|
|
|
|
|
|
``` python
|
|
|
def T_emp_parametre_avec_defaut_Tchego(n_annees, a=0.3, b=0.4):
|
|
|
|
|
|
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
|
|
|
Les valeurs *a*, *b* et *N* vont être transmises des curseurs aux fonctions puis aux graphiques ; on peut les passer comme arguments mais on préfèrera ici les déclarer comme variables globales dans les fonctions.
|
|
|
C'est sans doute à discuter.
|
|
|
Le critère retenu ici est que le slider qui sert à modifier b va déclencher un recalcul du vecteur des fréquences (ou T) qui nécessite de connaître a et b aussi.
|
|
|
|
|
|
On donc définir *a*, *b* et *N* dès le départ, avec une valeur par défaut.
|
|
|
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 ! On le trie d'emblée, ce qui permet d'utiliser 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).
|
|
|
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
|
|
|
a = 0.5
|
|
|
b = 0.5
|
|
|
N = 10
|
|
|
echantillon = sorted(range(1, N+1), reverse=True)
|
|
|
en_periode_de_retour = True
|
|
|
``` python
|
|
|
|
|
|
|
|
|
## 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 grille et le tracé des deux premières courbes
|
|
|
|
|
|
``` 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'")
|
|
|
|
|
|
fig_pp.suptitle(f"les valeurs importent peu, seul leur rang est utilisé ! ")
|
|
|
|
|
|
courbe_estim_Tchego, = ax.plot(T_emp_Tchego(N), echantillon, color="red", alpha=0.7, linewidth=3, solid_capstyle='round',
|
|
|
zorder=2, label= "Tchegodaiev", marker="o" , ls="--", markersize=7)
|
|
|
courbe_estim_param, = ax.plot(T_emp_parametre(N, a, b), echantillon, color='blue', alpha=0.7, linewidth=5, solid_capstyle='round',
|
|
|
zorder=10, label = "paramétrée", marker="x" , ls=':', markersize=7)
|
|
|
|
|
|
# pour faire tourner ce code seul, après avoir défini a, b et N
|
|
|
ax.legend(title="formules", bbox_to_anchor=(0.85, 0.15), loc='lower right')
|
|
|
# plt.show()
|
|
|
|
|
|
```
|
|
|
|
|
|
### définition des widgets et liaison avec une fonction
|
|
|
|
|
|
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)
|
|
|
|
|
|
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*
|
|
|
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)
|
|
|
|
|
|
|
|
|
``` 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)
|
|
|
#for pos in ['right', 'top', 'bottom', 'left']:
|
|
|
# ax_s.spines[pos].set_visible(False)
|
|
|
|
|
|
# a
|
|
|
slider_a = Slider(
|
|
|
ax_slide_a, "paramètre a ", valmin=0.01, valmax = 0.99, valfmt='%0.2f', valinit=0.5, color="green")
|
|
|
|
|
|
slider_a.on_changed(update_a)
|
|
|
|
|
|
# b
|
|
|
slider_b = Slider(
|
|
|
ax_slide_b, "paramètre b ", valmin=0.01, valmax = 0.99, valfmt='%0.2f', valinit=0.5, color="blue")
|
|
|
|
|
|
slider_b.on_changed(update_b)
|
|
|
|
|
|
# N
|
|
|
valeurs_possibles_b = np.linspace(0, 100, 1)
|
|
|
slider_n = Slider(
|
|
|
ax_slide_n, "paramètre N ", valmin=5, valmax = 200, valfmt='%0.0f', valinit=N, valstep=valeurs_possibles_b, color="purple")
|
|
|
|
|
|
slider_n.on_changed(update_N)
|
|
|
|
|
|
# freq
|
|
|
valeurs_possibles_b = np.linspace(0, 100, 1)
|
|
|
chb_enT = CheckButtons(ax_chb, [ "En période de retour (années)"], actives=[0])
|
|
|
|
|
|
chb_enT.on_clicked(switch_freq)
|
|
|
|
|
|
``` |