在 matplotlib 中放大插图而不重新绘制数据

chr*_*-sc 5 python plot matplotlib

我正在处理一些 matplotlib 图,需要放大插图。这是可能的zoomed_inset_axesaxes_grid1工具包。请参阅此处的示例:

import matplotlib.pyplot as plt

from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset

import numpy as np

def get_demo_image():
    from matplotlib.cbook import get_sample_data
    import numpy as np
    f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)
    z = np.load(f)
    # z is a numpy array of 15x15
    return z, (-3,4,-4,3)

fig, ax = plt.subplots(figsize=[5,4])

# prepare the demo image
Z, extent = get_demo_image()
Z2 = np.zeros([150, 150], dtype="d")
ny, nx = Z.shape
Z2[30:30+ny, 30:30+nx] = Z

# extent = [-3, 4, -4, 3]
ax.imshow(Z2, extent=extent, interpolation="nearest",
          origin="lower")

axins = zoomed_inset_axes(ax, 6, loc=1) # zoom = 6
axins.imshow(Z2, extent=extent, interpolation="nearest",
             origin="lower")

# sub region of the original image
x1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9
axins.set_xlim(x1, x2)
axins.set_ylim(y1, y2)

plt.xticks(visible=False)
plt.yticks(visible=False)

# draw a bbox of the region of the inset axes in the parent axes and
# connecting lines between the bbox and the inset axes area
mark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5")

plt.draw()
plt.show()
Run Code Online (Sandbox Code Playgroud)

这将给出所需的结果:

结果http://matplotlib.org/1.3.1/_images/inset_locator_demo21.png

但是正如您在代码中看到的那样,数据必须绘制两次 - 一次用于主轴 ( ax.imshow...),一次用于插入轴 ( axins.imshow...)。

我的问题是:

有没有办法在主图完成添加放大的插图,无需在新轴上再次绘制所有内容?

请注意:我不是在寻找用函数包装绘图调用并让函数绘图axaxins(参见下面的示例)的解决方案,而是(如果存在)利用ax. 有谁知道是否存在这样的解决方案?

这是包装解决方案:

def plot_with_zoom(*args, **kwargs):
    ax.imshow(*args, **kwargs)
    axins.imshow(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

它有效,但感觉有点像黑客,因为如果我只想放大现有绘图的一个区域,我为什么需要再次绘制所有数据。


ed-smith 回答后的一些额外说明:

上面的例子当然只是最小的例子。可能有很多不同的数据集在剧情(与数据集我的意思是事物通过绘制imshowplot等)。想象一下,例如一个散点图,有 10 个点数组,所有的点都是相对于公共 x 绘制的。

正如我上面写的,最直接的方法是使用一个包装器来绘制所有实例中的数据。但是我正在寻找的是一种从最终ax对象(而不是单个绘图命令)开始并以某种方式创建缩放插图的方法(如果存在)。

Ed *_*ith 4

我认为以下内容可以满足您的要求。请注意,您使用返回的第一个句柄imshow并将其添加到插入的轴。您需要制作一份副本,以便每个图形都有一个单独的句柄,

\n\n
import matplotlib.pyplot as plt\n\nfrom mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes\nfrom mpl_toolkits.axes_grid1.inset_locator import mark_inset\n\nimport numpy as np\nimport copy\n\ndef get_demo_image():\n    from matplotlib.cbook import get_sample_data\n    import numpy as np\n    f = get_sample_data("axes_grid/bivariate_normal.npy", asfileobj=False)\n    z = np.load(f)\n    # z is a numpy array of 15x15\n    return z, (-3,4,-4,3)\n\nfig, ax = plt.subplots(figsize=[5,4])\n\n# prepare the demo image\nZ, extent = get_demo_image()\nZ2 = np.zeros([150, 150], dtype="d")\nny, nx = Z.shape\nZ2[30:30+ny, 30:30+nx] = Z\n\n# extent = [-3, 4, -4, 3]\nim = ax.imshow(Z2, extent=extent, interpolation="nearest",\n          origin="lower")\n\n#Without copy, image is shown in insert only\nimcopy = copy.copy(im)\naxins = zoomed_inset_axes(ax, 6, loc=1) # zoom = 6\naxins.add_artist(imcopy)\n\n# sub region of the original image\nx1, x2, y1, y2 = -1.5, -0.9, -2.5, -1.9\naxins.set_xlim(x1, x2)\naxins.set_ylim(y1, y2)\n\nplt.xticks(visible=False)\nplt.yticks(visible=False)\n\n# draw a bbox of the region of the inset axes in the parent axes and\n# connecting lines between the bbox and the inset axes area\nmark_inset(ax, axins, loc1=2, loc2=4, fc="none", ec="0.5")\n\nplt.draw()\nplt.show()\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于你的包装函数,这将是这样的,

\n\n
def plot_with_zoom(*args, **kwargs):\n    im = ax.imshow(*args, **kwargs)\n    imcopy = copy.copy(im)\n    axins.add_artist(imcopy)\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,由于imshow只是将存储在数组中的数据显示Z为图像,我认为这个解决方案实际上会比两次单独调用imshow. 对于需要更多时间的绘图,例如contour绘图或pcolormesh,这种方法可能是明智的......

\n\n

编辑:

\n\n

除了单个imshow, 以及不同类型的多个图。绘图函数都返回不同的句柄(例如,plot 返回线列表,imshow 返回 matplotlib.image.AxesImage 等)。您可以在绘图时不断将这些句柄添加到列表(或字典)中(如果它们足够相似,则可以使用集合)。然后,您可以编写一个通用函数,使用来自缩放轴的 add_artist 或 add_patch 方法将它们添加到轴,可能使用 if 类型检查来处理图中使用的各种类型。更简单的方法可能是循环ax.get_children()用不是轴本身元素的任何内容。

\n\n

另一种选择可能是研究位块传输技术、光栅化或其他用于加速动画的技术,例如使用fig.canvas.copy_from_bboxfig.canvas.tostring_rgb复制整个图形作为图像(看看为什么用 Matplotlib 绘图如此慢? \xe2\x80\x8c\xe2 \x80\x8blow)。您还可以绘制图形,将其保存到非矢量图形(使用savefig或保存到 StringIO缓冲区),读回并绘制放大版本。

\n