Introducción
Matplotlib es una popular biblioteca de visualización de datos que ofrece una amplia variedad de herramientas para crear visualizaciones en Python. Una de las características interesantes de Matplotlib es la capacidad de agregar un cursor de cruz a una gráfica. En este laboratorio, aprenderá cómo agregar un cursor de cruz a una gráfica de Matplotlib.
Consejos sobre la VM
Una vez finalizada la inicialización de la VM, haga clic en la esquina superior izquierda para cambiar a la pestaña Cuaderno y acceder a Jupyter Notebook para practicar.
A veces, es posible que tenga que esperar unos segundos a que Jupyter Notebook termine de cargarse. La validación de operaciones no se puede automatizar debido a las limitaciones de Jupyter Notebook.
Si tiene problemas durante el aprendizaje, no dude en preguntar a Labby. Deje sus comentarios después de la sesión y lo resolveremos rápidamente para usted.
Cursor simple
El primer paso es agregar un cursor simple a una gráfica de Matplotlib. Este cursor mostrará los valores de x e y de la ubicación actual del mouse.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backend_bases import MouseEvent
class Cursor:
"""
Un cursor de cruz.
"""
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='--')
## ubicación del texto en coordenadas de los ejes
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
## actualizar las posiciones de la línea
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('Cursor simple')
ax.plot(x, y, 'o')
cursor = Cursor(ax)
fig.canvas.mpl_connect('motion_notify_event', cursor.on_mouse_move)
plt.show()
Cursado más rápido usando el bliteado
El cursor creado en el paso anterior es un poco lento porque vuelve a dibujar la figura en cada movimiento del mouse. En este paso, crearemos un cursor que utilice el bliteado para una representación más rápida.
class BlittedCursor:
"""
Un cursor de cruz que utiliza el bliteado para un redibujado más rápido.
"""
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='--')
## ubicación del texto en coordenadas de los ejes
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:
## descartar llamadas desencadenadas desde dentro de esta función
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)
## actualizar las posiciones de la línea
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('Cursor con bliteado')
ax.plot(x, y, 'o')
blitted_cursor = BlittedCursor(ax)
fig.canvas.mpl_connect('motion_notify_event', blitted_cursor.on_mouse_move)
plt.show()
Ajuste automático a los puntos de datos
El cursor creado en el paso anterior sigue siendo un poco lento porque actualiza la posición del cursor en cada movimiento del mouse. En este paso, crearemos un cursor que se ajuste automáticamente a los puntos de datos de un objeto Line2D.
class SnappingCursor:
"""
Un cursor de cruz que se ajusta automáticamente al punto de datos de una línea, que es
el más cercano a la posición *x* del cursor.
Por simplicidad, se asume que los valores de *x* de los datos están ordenados.
"""
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
## ubicación del texto en coordenadas de los ejes
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 ## todavía en el mismo punto de datos. Nada que hacer.
self._last_index = index
x = self.x[index]
y = self.y[index]
## actualizar las posiciones de la línea
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('Cursor de ajuste automático')
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()
Resumen
En este laboratorio, aprendió cómo agregar un cursor de cruz a una gráfica de Matplotlib. Creó un cursor simple, un cursor que utiliza el bliteado para una representación más rápida y un cursor que se ajusta automáticamente a los puntos de datos de un objeto Line2D. Estos cursores pueden ser útiles para explorar datos y obtener información valiosa a partir de visualizaciones.