Matplotlib : dessiner entre les sous-graphiques

Matplotlib : dessiner entre les sous-graphiques

Table des Matières

La semaine dernière, je préparais un rapport d’analyse de données avec Jupyter, Pandas et Matplotlib (pour ne citer que quelques briques de ce fabuleux framework). Une des figures contenait deux sous-graphiques : le second étant un agrandissement d’une région du premier. Pour bien illustrer cela et, au passage, montrer au fanclub MATLAB à quel point ils sont restés “so 90s”, j’ai décidé de tracer une flèche entre les deux sous-graphiques.

Figure Matplotlib montrant deux sous-graphiques avec une flèche traversant les deux

Hélas, cette idée simple m’a conduit à trois heures de recherche intense sur Internet, entre essais, erreurs, et même quelques plantages. J’étais à deux doigts de m’incliner devant la technologie du siècle dernier… Mais pas question !

Artistes et transformations

Si vous voulez juste vous vanter du résultat final, passez cette section. Je vais juste expliquer vite-fait ce qui se passe sous le capot. Alors, figures, axes, abscisses, c’est quoi tout ça ? Je ne saurais trouver une meilleure explication que le tutoriel sur les “artistes” de Matplotlib, mais je vais essayer de résumer. Avec Matplotlib, vous dessinez sur un Canvas en utilisant un Renderer. Les objets capables d’utiliser un renderer pour dessiner sur le canvas s’appellent des “artistes”.

Il existe deux types d’artistes : les primitives et les conteneurs. Les primitives représentent les objets graphiques standards que nous voulons dessiner sur le canvas : Line2D, Rectangle, Text, AxesImage, etc. Les conteneurs sont des endroits pour les y mettre (Axis, Axes et Figure).

On en déduit qu’il nous faut créer une primitive, à savoir un Patch, et plus précisément dans notre cas un FancyArrowPatch, qu’on ajoutera à la figure. Facile.

Mais il y a un autre aspect à considérer. Comme nous ajoutons un patch à la figure, on peut s’attendre à ce qu’il soit dessiné en utilisant les coordonnées de la figure. Ce n’est pas très simple quand il s’agit de bien placer la flèche. Pour résoudre cela, il faut comprendre comment fonctionnent les transformations. Là encore, il existe un excellent tutoriel sur les transformations, et je ne saurais mieux expliquer. Lorsque vous traitez des coordonnées dans Matplotlib, il faut savoir à quel système de coordonnées elles se rapportent. Il y a 4 systèmes de coordonnées :

CoordonnéesObjet de transformationDescription
dataax.transDataCoordonnées des données utilisateur, contrôlées par xlim et ylim
axesax.transAxesCoordonnées des axes ; (0,0) correspond au coin inférieur gauche, (1,1) au coin supérieur droit.
figurefig.transFigureCoordonnées de la figure ; (0,0) pour le coin inférieur gauche, (1,1) pour le coin supérieur droit.
displayNoneCoordonnées en pixels de l’écran ; (0,0) pour le coin inférieur gauche, (largeur, hauteur) pour le coin supérieur droit.

Le système de référence est display, et les objets de transformation décrivent comment transformer des coordonnées vers ce système de référence. Nous savons où positionner la flèche dans les données (c’est à dire ses coordonnées dans le système data), mais pas où cela se place sur la figure complète. En appliquant une transformation via l’objet de transformation des données, nous obtenons les coordonnées en pixels. Une étape. Ensuite, il suffit d’inverser la transformation en utilisant l’objet de transformation de la figure pour convertir depuis les coordonnées pixels au système de coordonnées de la figure.

Dernier point, il faut indiquer explicitement le système de coordonnées utilisé lors de la création du patch avec le paramètre transform.

Montre-moi le code

OK, sortez vos données, configurez le graphique ! Dans l’exemple ci-dessous, j’utilise un DataFrame Pandas, mais cela fonctionnerait tout aussi bien avec des tableaux NumPy.

import matplotlib.pyplot as plt
import matplotlib.patches

fig, axes = plt.subplots(2)
# Affiche la mesure complète pour comparaison sur l'axe 0
testdata.plot(ax=axes[0], x="mdfTime", y="psiDt")
# Place un petit rectangle pour marquer la zone agrandie
axes[0].add_patch(matplotlib.patches.Rectangle((200., -4.), 50., 6., transform=axes[0].transData, alpha=0.3, color="g"))
# Agrandissement de la mesure sur l'axe 1
testdata[int(200./0.02):int(250./0.02)].plot(ax=axes[1], x="mdfTime", y="psiDt")

Et enfin, la partie vraiment intéressante :

# Créer la flèche
# 1. Obtenir les objets de transformation pour les axes et la figure
ax0tr = axes[0].transData # Axe 0 -> Affichage
ax1tr = axes[1].transData # Axe 1 -> Affichage
figtr = fig.transFigure.inverted() # Affichage -> Figure
# 2. Transformer le point de départ de la flèche de l'axe 0 aux coordonnées de la figure
ptB = figtr.transform(ax0tr.transform((225., -10.)))
# 3. Transformer le point final de la flèche de l'axe 1 aux coordonnées de la figure
ptE = figtr.transform(ax1tr.transform((225., 1.)))
# 4. Créer le patch
arrow = matplotlib.patches.FancyArrowPatch(
    ptB, ptE, transform=fig.transFigure,  # Place la flèche dans le système de coord. de la figure
    fc="g", connectionstyle="arc3,rad=0.2", arrowstyle='simple', alpha=0.3,
    mutation_scale=40.
)
# 5. Ajouter le patch à la liste des objets à dessiner sur la figure
fig.patches.append(arrow)

Figure Matplotlib montrant deux sous-graphiques avec une flèche traversant les deux

Si vous travaillez avec des lignes, le processus est similaire, mais vous devez ajouter votre objet personnalisé à la propriété lines de la figure au lieu de patches. Jettez un œil à cette question sur StackOverflow.

Articles Similaires

Le Rotrics DexArm est une addition intrigante et originale dans le monde de l’impression 3D et de la découpe laser. Contrairement aux découpeuses laser traditionnelles, c’est un bras robotique de petite taille à 4 axes avec une grande modularité, lui permettant de passer facilement de la gravure laser à l’impression 3D, le tracé et même la préhension d’objets.

Lire la suite

Utiliser GNOME Keyring sur XFCE sous Manjaro n’est pas compliqué, mais la documentation de ArchLinux n’est pas adaptée et peut vous laisser vous gratter la tête pendant des heures. Allez, ne perdez plus de cheveux, je vous aide.

Lire la suite

Manjaro est une excellente façon d’obtenir un bureau Linux complet et fonctionnel en quelques minutes. Son installateur graphique, ses choix par défaut judicieux et sa sélection de paquets bien pensée en font une très bonne option pour les débutants comme pour les utilisateurs avancés. Cependant, avec le temps, j’ai constaté que les dépôts de Manjaro accusaient un certain retard par rapport à ceux d’Arch, et que – du moins pour la version XFCE – la stabilité n’était pas au rendez-vous. Je voulais conserver mon environnement de travail tel quel, mais passer à un système plus simple et plus stable à maintenir : Arch Linux pur.

Lire la suite