Introduction
Matplotlib est une bibliothèque populaire de visualisation de données qui fournit une large gamme d'outils pour créer des visualisations en Python. L'une des fonctionnalités intéressantes de Matplotlib est la capacité d'ajouter un curseur de repère croisé à un graphique. Dans ce laboratoire, vous allez apprendre à ajouter un curseur de repère croisé à un graphique Matplotlib.
Conseils sur la VM
Une fois le démarrage de la VM terminé, cliquez dans le coin supérieur gauche pour basculer vers l'onglet Notebook pour accéder à Jupyter Notebook pour la pratique.
Parfois, vous devrez peut-être attendre quelques secondes pour que Jupyter Notebook ait fini de charger. La validation des opérations ne peut pas être automatisée en raison des limitations de Jupyter Notebook.
Si vous rencontrez des problèmes pendant l'apprentissage, n'hésitez pas à demander à Labby. Donnez votre feedback après la session, et nous résoudrons rapidement le problème pour vous.
Curseur simple
La première étape consiste à ajouter un curseur simple à un graphique Matplotlib. Ce curseur affichera les valeurs x et y de la position actuelle de la souris.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backend_bases import MouseEvent
class Cursor:
"""
Un curseur de repère croisé.
"""
def __init__(self, ax):
self.ax = ax
self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
## emplacement du texte en coordonnées d'axes
self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
def set_cross_hair_visible(self, visible):
need_redraw = self.horizontal_line.get_visible()!= visible
self.horizontal_line.set_visible(visible)
self.vertical_line.set_visible(visible)
self.text.set_visible(visible)
return need_redraw
def on_mouse_move(self, event):
if not event.inaxes:
need_redraw = self.set_cross_hair_visible(False)
if need_redraw:
self.ax.figure.canvas.draw()
else:
self.set_cross_hair_visible(True)
x, y = event.xdata, event.ydata
## mettre à jour les positions des lignes
self.horizontal_line.set_ydata([y])
self.vertical_line.set_xdata([x])
self.text.set_text(f'x={x:1.2f}, y={y:1.2f}')
self.ax.figure.canvas.draw()
x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)
fig, ax = plt.subplots()
ax.set_title('Curseur simple')
ax.plot(x, y, 'o')
cursor = Cursor(ax)
fig.canvas.mpl_connect('motion_notify_event', cursor.on_mouse_move)
plt.show()
Utilisation de la mémorisation pour un curseur plus rapide
Le curseur créé dans l'étape précédente est un peu lent car il redessine la figure à chaque mouvement de souris. Dans cette étape, nous allons créer un curseur qui utilise la mémorisation pour une représentation plus rapide.
class BlittedCursor:
"""
Un curseur de repère croisé utilisant la mémorisation pour une redessin plus rapide.
"""
def __init__(self, ax):
self.ax = ax
self.background = None
self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
## emplacement du texte en coordonnées d'axes
self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
self._creating_background = False
ax.figure.canvas.mpl_connect('draw_event', self.on_draw)
def on_draw(self, event):
self.create_new_background()
def set_cross_hair_visible(self, visible):
need_redraw = self.horizontal_line.get_visible()!= visible
self.horizontal_line.set_visible(visible)
self.vertical_line.set_visible(visible)
self.text.set_visible(visible)
return need_redraw
def create_new_background(self):
if self._creating_background:
## ignorer les appels déclenchés à l'intérieur de cette fonction
return
self._creating_background = True
self.set_cross_hair_visible(False)
self.ax.figure.canvas.draw()
self.background = self.ax.figure.canvas.copy_from_bbox(self.ax.bbox)
self.set_cross_hair_visible(True)
self._creating_background = False
def on_mouse_move(self, event):
if self.background is None:
self.create_new_background()
if not event.inaxes:
need_redraw = self.set_cross_hair_visible(False)
if need_redraw:
self.ax.figure.canvas.restore_region(self.background)
self.ax.figure.canvas.blit(self.ax.bbox)
else:
self.set_cross_hair_visible(True)
## mettre à jour les positions des lignes
x, y = event.xdata, event.ydata
self.horizontal_line.set_ydata([y])
self.vertical_line.set_xdata([x])
self.text.set_text(f'x={x:1.2f}, y={y:1.2f}')
self.ax.figure.canvas.restore_region(self.background)
self.ax.draw_artist(self.horizontal_line)
self.ax.draw_artist(self.vertical_line)
self.ax.draw_artist(self.text)
self.ax.figure.canvas.blit(self.ax.bbox)
x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)
fig, ax = plt.subplots()
ax.set_title('Curseur mémorisé')
ax.plot(x, y, 'o')
blitted_cursor = BlittedCursor(ax)
fig.canvas.mpl_connect('motion_notify_event', blitted_cursor.on_mouse_move)
plt.show()
Captage sur les points de données
Le curseur créé dans l'étape précédente est toujours un peu lent car il met à jour la position du curseur à chaque mouvement de souris. Dans cette étape, nous allons créer un curseur qui s'accroche aux points de données d'un objet Line2D.
class SnappingCursor:
"""
Un curseur de repère croisé qui s'accroche au point de données d'une ligne, qui est
le plus proche de la position *x* du curseur.
Pour simplifier, cela suppose que les valeurs *x* des données sont triées.
"""
def __init__(self, ax, line):
self.ax = ax
self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
self.x, self.y = line.get_data()
self._last_index = None
## emplacement du texte en coordonnées d'axes
self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
def set_cross_hair_visible(self, visible):
need_redraw = self.horizontal_line.get_visible()!= visible
self.horizontal_line.set_visible(visible)
self.vertical_line.set_visible(visible)
self.text.set_visible(visible)
return need_redraw
def on_mouse_move(self, event):
if not event.inaxes:
self._last_index = None
need_redraw = self.set_cross_hair_visible(False)
if need_redraw:
self.ax.figure.canvas.draw()
else:
self.set_cross_hair_visible(True)
x, y = event.xdata, event.ydata
index = min(np.searchsorted(self.x, x), len(self.x) - 1)
if index == self._last_index:
return ## toujours sur le même point de données. Rien à faire.
self._last_index = index
x = self.x[index]
y = self.y[index]
## mettre à jour les positions des lignes
self.horizontal_line.set_ydata([y])
self.vertical_line.set_xdata([x])
self.text.set_text(f'x={x:1.2f}, y={y:1.2f}')
self.ax.figure.canvas.draw()
x = np.arange(0, 1, 0.01)
y = np.sin(2 * 2 * np.pi * x)
fig, ax = plt.subplots()
ax.set_title('Curseur de captage')
line, = ax.plot(x, y, 'o')
snap_cursor = SnappingCursor(ax, line)
fig.canvas.mpl_connect('motion_notify_event', snap_cursor.on_mouse_move)
plt.show()
Sommaire
Dans ce laboratoire, vous avez appris à ajouter un curseur de repère croisé à un graphique Matplotlib. Vous avez créé un curseur simple, un curseur qui utilise la mémorisation pour une représentation plus rapide et un curseur qui s'accroche aux points de données d'un objet Line2D. Ces curseurs peuvent être utiles pour explorer les données et extraire des informations des visualisations.