|
|
Cette page partage et commente des petits tests utiles pour comprendre et corriger les mésaventures pour réaliser des zooms sur une partie d'une figure déjà réalisée, dans le cas d'une "heatmap" (champs de valeurs, avec matshow) ou d'un "plot", i.e. une courbe simple y=f(x).
|
|
|
|
|
|
### **Objectif de cette page ** :
|
|
|
:one: décrire les méthodes et pièges rencontrés en réalisant deux cartes successives : une avec le champ entier, l'autre étant une restriction à une zone.
|
|
|
:two: proposer un rappel théorique sur les fonctions utilisées, sur des exemples simples (un jeu de données minimal et la méthode de base plot), mais avec des cas particuliers pouvant créer des surprises (notamment quand l'un des axes porte des valeurs négatives).
|
|
|
|
|
|
|
|
|
**Notions manipulées** : **matshow** pour tracer les matrices et ses arguments, notamment **extent** ; réglage de la zone de tracé d'une figure avec plt.xlim et plt.ylim, et leurs équivalents en programmation plus objet : **ax.set_xlim** et **ax.set_ylim**, différences avec **ax.set_xbound** et **ax.set_ybound**
|
|
|
|
|
|
\:working: ## en construction : ces éléments seront peut-être reversés à d'autres pages après réorganisation
|
|
|
|
|
|
## Exemple d'une carte à l'échelle de la France, à restreindre à un bassin
|
|
|
On présente d'abord un exemple "en situation", avec une carte à l'échelle de la France que l'on souhaite restreindre à un sous-domaine, avec la méthode **matshow**.
|
|
|
|
|
|
### **Mise en contexte** : exploration d'une série de fichiers de champs, que l'on parcourt pour créer et enregistrer des cartes sur la totalité de l'emprise puis des "zooms" sur une partie
|
|
|
|
|
|
### **Démarche suivie** : restreindre une figure à une zone prédéfinie ne devrait a priori pas poser de problème... mais en pratique il y a quelques peaux de bananes tapies dans l'ombre. On vous recommande ici de comparer, en phase de débugage, votre figure restreinte par deux ou trois méthodes :
|
|
|
:map: le plus "sûr", qui sollicite l'utilisateur et servira de référence : tracer tout le graphique en mode interactif et zoomer 'à la main' ;
|
|
|
:map: méthode similaire mais automatisée : tracer tout le graphique et zoomer dans le code avec **set_xlim** et **set_ylim**. On peut avoir des ennuis si on n'écrit pas les arguments dans "le bon ordre" ; il est intéressant de connaître aussi **set_xbound** et **set_ybound**, que l'on présente dans la 2e partie, avec un jeu de données minimal et la méthode de base plot.
|
|
|
:map: la méthode la plus élégante mais qui présente quelques petits pièges : restreindre la matrice à la zone d'intérêt avant le tracé par _slicing_, et préciser les coordonnées de la zone restreinte avec **extent**
|
|
|
|
|
|
|
|
|
### Tracé sur tout le domaine puis restriction : méthodes 1, avec intervention de l'utilisateur, et 2, avec set_xlim et set_ylim :
|
|
|
|
|
|
#### dans les deux cas :
|
|
|
* lecture des données et tracé d'une carte, en l'espèce avec matshow en spécifiant les coordonnées de la boîte englobante avec extent
|
|
|
|
|
|
#### dans la méthode qui fait intervenir l'utilisateur :
|
|
|
* le code affiche la carte en mode interactif, et l'utilisateur effectue les manips pour zoomer et enregistrer s'il le souhaite
|
|
|
|
|
|
#### dans la méthode automatisée :
|
|
|
* on n'affiche pas la carte mais on enregistre la figure avec savefig(nom_du_ficher.png) ; dans notre cas, il y a une boucle sur les pas de temps et le nom est construit sur le modèle champ_France_heure_minute
|
|
|
* on modifie l'étendue de la vue en passant à set_xlim et set_ylim les coordonnées de la zone d'étude
|
|
|
* on enregistre une seconde fois avec savefig(champ_zone_restreinte_heure_minute.png)
|
|
|
|
|
|
Comportement :
|
|
|
| Domaine complet | carte interactive zoomée à la main | carte restreinte par set_xlim et set_ylim |
|
|
|
|-----------------|------------------------------------|-------------------------------------------|
|
|
|
| ![crs_netcdf_Matrice_Accumulated_precipitation__mm_cumul_france_1536 1km 5min_202010030005](uploads/9b3bab0b4ae1e917de36c3b58e9b7757/crs_netcdf_Matrice_Accumulated_precipitation__mm_cumul_france_1536-1km-5min_202010030005.png) | ![verif_mat_5min_dezoome](uploads/1c0e402cab6311064c1762e9a9492a0b/verif_mat_5min_dezoome.png) | ![ZI_precipitation_mm__00h05](uploads/c20fa5eb0cfd6c3932564472fbf552f5/ZI_precipitation_mm__00h05.png) |
|
|
|
|
|
|
```python
|
|
|
from matplotlib import pyplot as plt
|
|
|
|
|
|
# principe du code, en version simplifiée
|
|
|
# les données sont dans la matrice np_carte
|
|
|
fig0, ax0 = plt.subplots()
|
|
|
# carte sur domaine complet, défini par une liste ou un tuple de 4 coordonnées
|
|
|
extent_total = [gauche_bbox, droite_bbox, bas_bbox, haut_bbox]
|
|
|
ax0.matshow(np_carte, extent= extent_total )
|
|
|
nom_fichier_a_ecrire = self.rep_enreg + '/domaine_entier_' + self.nom_fic + ".png"
|
|
|
plt.savefig(nom_fichier_a_ecrire)
|
|
|
|
|
|
# restriction : on redimensionne avec set_xlim et set_ylim. on ne touche à rien d'autre
|
|
|
gauche, droite, bas, haut = xmin, xmin + width_m, ymin, ymin + height_m
|
|
|
ax0.set_xlim(gauche, droite)
|
|
|
ax0.set_ylim(bas, haut)
|
|
|
|
|
|
nom_fichier_a_ecrire_zoom = self.rep_enreg + '/ZOOM_' + self.nom_fic + ".png"
|
|
|
print("Ecriture de : ", nom_fichier_a_ecrire_zoom)
|
|
|
|
|
|
plt.close(fig0)
|
|
|
```
|
|
|
|
|
|
### Méthode 3 : on trace uniquement sur le sous-domaine
|
|
|
|
|
|
Au lieu de zoomer sur une carte existante, on crée une 2e carte indépendante sur le domaine ZI défini à la fois par les indices min et max en ligne et colonne et par 4 valeurs de coordonnées à fournir à l'argument extent.
|
|
|
|
|
|
```python
|
|
|
# slicing de la matrice
|
|
|
np_extrait_carte = np_carte[rang_col_min:rang_col_max, rang_lig_min:rang_lig_max]
|
|
|
cax = ax0.matshow(np_extrait_carte, extent=extent_ZI)
|
|
|
```
|
|
|
|
|
|
- ⚠ premier piège : les rangs des matrices numpy commencent à :zero: ; attention à ne pas passer de "numéro de colonne" au lieu de rang
|
|
|
- ⚠ deuxième piège :le chiffre de fin de slicing, comme pour les listes, est le rang du premier élément **exclu**...
|
|
|
- ⚠ troisième piège : quand je fais le slicing, je dois vérifier quel est le sens d'écriture "comme sur papier" de ma matrice, je ne peux pas forcément me fier à ce que je vois, puisque le couple "extent/origin" peut intervertir le sens de tracé... voir plus bas et dans l'atelier D_carte
|
|
|
- ⚠ et un dernier avertissement : si cet extrait de domaine est tracé avec d'autres courbes qui servent de fond de plan et qui dépassent le sous-domaine, il faut quand même restreindre la figure avec **set_xlim** et/ou **set_ylim** ; ce sera notre cas ici si on veut quand même tracer les frontières de la France, puisque nos bassins d'intérêt sont proches d'une frontière !
|
|
|
|
|
|
Donc, supposons que je connaisse les limites de mon rectangle par les NUMEROS des premières lignes et colonnes et des dernières INCLUSES, notées `**num**_col_min, **num**_col_max, **num**_lig_min, **num**_lig_max]`,je devrais effectuer le slicing suivant :
|
|
|
|
|
|
```python
|
|
|
# le rang de début correspond au premier numéro de colonne (ou de ligne) ' rang = num moins 1 '
|
|
|
# le rang de fin correspond à la première valeur exclue : donc après avoir enlevé 1 pour avoir le rang, j'ajoute 1 si mon numéro de colonne appartient encore à mon domaine...
|
|
|
np_extrait_carte = np_carte[num_col_min - 1:num_col_max, num_lig_min - 1:num_lig_max]
|
|
|
```
|
|
|
|
|
|
Attention au cas où le tracé est inversé par rapport à x ou à y (effet miroir obtenu via **origin** et **extent** ou même **set_xlim**...) : le slicing se fait sur la matrice de départ.
|
|
|
|
|
|
### Conclusion "partielle et subjective"
|
|
|
|
|
|
Travailler sur toute la carte puis la restreindre est une bonne idée si on a besoin des 2 informations, et surtout on peut vérifier que le domaine restreint est bien placé. Pour certains usages, il est peut-être plus pertinent de ne tracer que ce qui est utile, en réduisant la matrice de départ par un "slicing" et en la traçant en précisant avec extent les limites de ce rectangle.
|
|
|
|
|
|
Dans la méthode de restriction qui utilise set_xlim et set_ylim, il suffit de bien faire attention à l'ordre des valeurs : avec set_ylim, le premier argument sera compris comme la valeur de l'ordonnée "en bas de l'axe" : si on inverse les valeurs de "haut" et "bas" on renverse l'axe.
|
|
|
|
|
|
Le plus efficace est de réaliser un slicing avant tout tracé, mais on multiplie le risque d'erreur : il faut bien faire le slicing pour extraire la bonne partie de la matrice, comme détaillé plus haut, et évidemment bien recalculer les coordonnées à passer à l'argument extent. Il est peut-être utile de vérifier ce que l'on fait en appliquant en parallèle les premières méthodes, au moins transitoirement, pour comparer (phase de débugage).
|
|
|
|
|
|
Voici un exemple où le slicing est correct, mais les valeurs passées à extent sont erronées (erreur ici provoquée au départ par une variable mal nommée, ymin n'était pas la plus petite valeur mais la plus grande - on va dire la plus "petite en valeur absolue").\
|
|
|
Suite à cette erreur, on va donc placer "le bon extrait de matrice" à "une mauvaise position", ce qui a créé un décalage étrange. Ici, l'anomalie a été facilement repérée grâce à la présence des frontières de la France, en noir, qui servent de repère, et grâce à la comparaison avec les cartes obtenues en parallèle par la première méthode (édition d'une seconde carte en zoomant à partir de la carte initiale). !
|
|
|
|
|
|
![crs_netcdf_ZI_Accumulated_precipitation_mm___cumul_france_1536 1km 5min_202010030005](uploads/d9abfebd12a59f24a9eb53f98f68f6f0/crs_netcdf_ZI_Accumulated_precipitation_mm___cumul_france_1536-1km-5min_202010030005.png) _carte obtenue avec le bon slicing de matrice mais des coordonnées erronées passées à extent_
|
|
|
|
|
|
## Tests de **set_ylim** sur un jeu de données minimaliste
|
|
|
|
|
|
On va maintenant réaliser un graphique simplissime, la seule "difficulté" étant que les valeurs de l'axe des y sont négatives. On établit une carte avec 4 points, puis on redimensionne soit avec plt.xlim et plt.ylim soit avec ax.set_xlim et ax.set_ylim si on a nommé l'Axes.
|
|
|
|
|
|
```python
|
|
|
# on définit 4 points, par un vecteur des abscisses et un vecteur des ordonnées
|
|
|
x = [500, 1000, 1500, 2000]
|
|
|
y= [-500,-1000,-1500,-2000]
|
|
|
# on trace les points avec des marqueurs seuls
|
|
|
plt.plot(x,y, marker='o', ls='None')
|
|
|
plt.show()
|
|
|
# deuxième carte, restreinte
|
|
|
plt.xlim(750,1750)
|
|
|
plt.ylim(-750,-1750)
|
|
|
plt.show()
|
|
|
```
|
|
|
| figure avec l'extension par défaut | figure restreinte avec set_xlim et set_ylim |
|
|
|
|------------------------------------|---------------------------------------------|
|
|
|
| ![cas_simple_y_negatifs](uploads/4b617c3dc262d060180de87a4a4e3c88/cas_simple_y_negatifs.png) | ![cas_simple_apres_set_xlim](uploads/b83d69f83f24777f563799ccc698ca6f/cas_simple_apres_set_xlim.png) |
|
|
|
|
|
|
(Mauvaise) surprise : l'axe des y a été inversé, la valeur -750 est "en bas" et -1750 "en haut", ce qui renverse la figure. En fait, quand on utilise ylim, on passe la valeur du BAS puis la valeur du HAUT, d'où l'inversion (voir la doc !).
|
|
|
|
|
|
Première solution : affecter la valeur du BAS voulue, puis la valeur du HAUT
|
|
|
|
|
|
```python
|
|
|
# deuxième carte, restreinte
|
|
|
plt.xlim(750,1750)
|
|
|
plt.ylim(-1750,-750) # -1750 sera en BAS et - 750 en HAUT
|
|
|
plt.show()
|
|
|
```
|
|
|
|
|
|
Deuxième solution : utiliser set_ybound Il n'existe pas de fonction plt.xbound, on doit donc préciser sur quel ax on travaille pour appliquer la méthode set_ybound : on utilise plt.gca() qui va retourner l'axe courant (gca = get current Axes)
|
|
|
|
|
|
```python
|
|
|
# deuxième carte, restreinte
|
|
|
plt.gca().set_xbound(750,1750)
|
|
|
plt.gca().set_ybound(-750,-1750) # -1750 sera en BAS et - 750 en HAUT
|
|
|
plt.show()
|
|
|
```
|
|
|
|
|
|
>
|
|
|
|
|
|
### Méthode Axes.set_xbound(lower=None, upper=None)
|
|
|
|
|
|
```plaintext
|
|
|
Set the lower and upper numerical bounds of the x-axis.
|
|
|
|
|
|
This method will honor axes inversion regardless of parameter order. It will not change the autoscaling setting (get_autoscalex_on()).
|
|
|
Parameters:
|
|
|
|
|
|
lower, upperfloat or None
|
|
|
``` |
|
|
\ No newline at end of file |