Matplotlib - 视窗限制



Viewlims视窗限制 指的是沿 x 轴和 y 轴在绘图中显示的数据范围。视窗限制对于交互式数据可视化非常有用,因为它允许用户动态调整数据的显示。

Matplotlib 提供了各种方法和工具来交互式地设置和更新视窗限制,使用户能够有效地探索和分析数据。一般情况下,在 matplotlib 绘图中,视窗限制会根据正在绘制的数据自动确定。

本教程将逐步实现如何在 Matplotlib 绘图中使用视窗限制创建交互式缩放功能,以创建与用户交互相对应的动态可视化。

创建交互式缩放绘图

创建一个类,当我们放大时重新生成分形集(曼德布罗集是数学中著名的分形),允许我们观察越来越多的细节。此外,我们将在左侧面板中显示一个框,以显示我们缩放到的区域。

此类定义了compute_image方法,根据提供的边界计算曼德布罗集。而axes_update方法根据当前视窗限制更新绘图。它确保每当视窗限制更改时,都会重新计算和重绘曼德布罗集。

class InteractiveFractal:

   def __init__(self, h=500, w=500, niter=50, radius=2., power=2):
      self.height = h
      self.width = w
      self.niter = niter
      self.radius = radius
      self.power = power

   def compute_image(self, xstart, xend, ystart, yend):
      self.x = np.linspace(xstart, xend, self.width)
      self.y = np.linspace(ystart, yend, self.height).reshape(-1, 1)
      c = self.x + 1.0j * self.y
      threshold_time = np.zeros((self.height, self.width))
      z = np.zeros(threshold_time.shape, dtype=complex)
      mask = np.ones(threshold_time.shape, dtype=bool)
      for i in range(self.niter):
         z[mask] = z[mask]**self.power + c[mask]
         mask = (np.abs(z) < self.radius)
         threshold_time += mask
      return threshold_time

   def axes_update(self, ax):
      ax.set_autoscale_on(False) 
        
      self.width, self.height = \
         np.round(ax.patch.get_window_extent().size).astype(int)
        
      vl = ax.viewLim
      extent = vl.x0, vl.x1, vl.y0, vl.y1

      im = ax.images[-1]
      im.set_data(self.compute_image(*extent))
      im.set_extent(extent)
      ax.figure.canvas.draw_idle()

更新视窗限制

我们将创建一个类UpdateRectangle来创建一个矩形,该矩形代表我们在曼德布罗集中缩放到的区域。此类扩展了Rectangle类,并用于在视窗限制更改时更新表示缩放区域的矩形。当我们在左侧面板放大时,矩形将更新其形状以匹配坐标轴的边界。

class UpdateRectangle(Rectangle):
   def __call__(self, ax):
      self.set_bounds(*ax.viewLim.bounds)
      ax.figure.canvas.draw_idle()

连接回调函数

回调函数连接到第二个子图 (ax2) 的xlim_changedylim_changed 事件。每当视窗限制更改时,这些回调函数都会触发矩形和曼德布罗集的更新。

# Connect for changing the view limits
ax2.callbacks.connect('xlim_changed', rect)
ax2.callbacks.connect('ylim_changed', rect)

ax2.callbacks.connect('xlim_changed', md.ax_update)
ax2.callbacks.connect('ylim_changed', md.ax_update)

以下是完整代码

曼德布罗集最初绘制在两个子图 (ax1 和 ax2) 中。表示缩放区域的矩形添加到 ax1,并且回调函数连接到 ax2 以处理视窗限制更改。

示例

import matplotlib.pyplot as plt
import numpy as np

from matplotlib.patches import Rectangle

class UpdateRectangle(Rectangle):
   def __call__(self, ax):
      self.set_bounds(*ax.viewLim.bounds)
      ax.figure.canvas.draw_idle()

class InteractiveFractal:

   def __init__(self, h=500, w=500, niter=50, radius=2., power=2):
      self.height = h
      self.width = w
      self.niter = niter
      self.radius = radius
      self.power = power

   def compute_image(self, xstart, xend, ystart, yend):
      self.x = np.linspace(xstart, xend, self.width)
      self.y = np.linspace(ystart, yend, self.height).reshape(-1, 1)
      c = self.x + 1.0j * self.y
      threshold_time = np.zeros((self.height, self.width))
      z = np.zeros(threshold_time.shape, dtype=complex)
      mask = np.ones(threshold_time.shape, dtype=bool)
      for i in range(self.niter):
         z[mask] = z[mask]**self.power + c[mask]
         mask = (np.abs(z) < self.radius)
         threshold_time += mask
      return threshold_time

   def axes_update(self, ax):
      ax.set_autoscale_on(False) 
        
      self.width, self.height = \
         np.round(ax.patch.get_window_extent().size).astype(int)
        
      vl = ax.viewLim
      extent = vl.x0, vl.x1, vl.y0, vl.y1

      im = ax.images[-1]
      im.set_data(self.compute_image(*extent))
      im.set_extent(extent)
      ax.figure.canvas.draw_idle()

md = InteractiveFractal()
Z = md.compute_image(-2., 0.5, -1.25, 1.25)

fig1, (ax1, ax2) = plt.subplots(1, 2, figsize=(7, 4))
ax1.imshow(Z, origin='lower',
   extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max()))
ax2.imshow(Z, origin='lower',
   extent=(md.x.min(), md.x.max(), md.y.min(), md.y.max()))

rect = UpdateRectangle(
   [0, 0], 0, 0, facecolor='none', edgecolor='black', linewidth=1.0)
rect.set_bounds(*ax2.viewLim.bounds)
ax1.add_patch(rect)

# Connect for changing the view limits
ax2.callbacks.connect('xlim_changed', rect)
ax2.callbacks.connect('ylim_changed', rect)

ax2.callbacks.connect('xlim_changed', md.axes_update)
ax2.callbacks.connect('ylim_changed', md.axes_update)
ax2.set_title("Zoom here")

plt.show()
输出

执行上述程序后,您将得到以下输出:

Viewlims 示例 1 观看下面的视频以了解视窗限制在此处的运作方式:Viewlims gif 文件
广告