gpt4 book ai didi

python - 高效的 Matplotlib 重绘

转载 作者:太空狗 更新时间:2023-10-29 18:22:05 39 4
gpt4 key购买 nike

我使用 Matplotlib 允许用户通过鼠标点击选择感兴趣的数据点,使用与 this answer. 非常相似的方法

实际上,散点图显示在热图图像上,鼠标点击可以添加或删除散点。

我的数据是使用 pcolormesh() 在后台绘制的,所以当我使用 axis.figure.canvas.draw() 更新 Canvas 时,散点和重新绘制背景热图。考虑到热图的大小,这对于一个可用的界面来说太慢了。

有没有一种方法可以有选择地重绘散点而不重绘背景?

示例代码:

points = []  # Holds a list of (x,y) scatter points 

def onclick(event):
# Click event handler to add points
points.append( (event.x, event.y) )
ax.figure.canvas.draw()

fig = plt.figure()
ax = plt.figure()
# Plot the background
ax.pcolormesh(heatmap_data)

# Add the listener to handle clicks
cid = fig.canvas.mpl_connect("button_press_event", onclick)
plt.show()

最佳答案

当然!你想要的是blitting。如果您不是在编写图形用户界面,则可以使用 matplotlib.animation 来简化其中的一些操作,但如果您希望内容具有交互性,则需要直接处理它。

在 matplotlib 术语中,您想要 fig.canvas.copy_from_bbox 的组合,然后交替调用 fig.canvas.restore_region(background)ax。 draw_artist(what_you_want_to_draw)fig.canvas.blit:

background = fig.canvas.copy_from_bbox(ax.bbox)

for x, y in user_interactions:
fig.canvas.restore_region(background)
points.append([x, y])
scatter.set_offsets(points)
ax.draw_artist(scatter)
fig.canvas.blit(ax.bbox)

简单的 Blitting 示例:添加点

在你的情况下,如果你只是添加点,你实际上可以跳过保存和恢复背景。但是,如果你走那条路,由于抗锯齿点被反复重新绘制在彼此之上,你最终会在情节上做出一些细微的变化。

无论如何,这是您想要的事物类型的最简单示例。这只处理添加点,并跳过我上面提到的保存和恢复背景:

import matplotlib.pyplot as plt
import numpy as np

def main():
fig, ax = plt.subplots()
ax.pcolormesh(np.random.random((100, 100)), cmap='gray')

ClickToDrawPoints(ax).show()

class ClickToDrawPoints(object):
def __init__(self, ax):
self.ax = ax
self.fig = ax.figure
self.xy = []
self.points = ax.scatter([], [], s=200, color='red', picker=20)
self.fig.canvas.mpl_connect('button_press_event', self.on_click)

def on_click(self, event):
if event.inaxes is None:
return
self.xy.append([event.xdata, event.ydata])
self.points.set_offsets(self.xy)
self.ax.draw_artist(self.points)
self.fig.canvas.blit(self.ax.bbox)

def show(self):
plt.show()

main()

有时候简单就是太简单

但是,假设我们想让右键单击删除一个点。

在那种情况下,我们需要能够在不重绘的情况下恢复背景。

好的,一切都很好。我们将使用类似于我在答案顶部提到的伪代码片段的东西。

但是,有一个警告:如果调整图形大小,我们需要更新背景。同样,如果坐标轴是交互缩放/平移的,我们需要更新背景。基本上,您需要在绘制绘图时随时更新背景。

很快您就需要变得相当复杂。


更复杂:添加/拖动/删除点

以下是您最终搭建的那种“脚手架”的一般示例。

这有点低效,因为绘图被绘制了两次。 (例如,平移将)。可以解决这个问题,但我会把这些例子留到下次再说。

这个实现了加点、拖点、删点。要在交互式缩放/平移后添加/拖动点,请再次单击工具栏上的缩放/平移工具以禁用它们。

这是一个相当复杂的示例,但希望它能让您了解通常构建的框架类型,以交互方式绘制/拖动/编辑/删除 matplotlib 艺术家,而无需重新绘制整个绘图。

import numpy as np
import matplotlib.pyplot as plt

class DrawDragPoints(object):
"""
Demonstrates a basic example of the "scaffolding" you need to efficiently
blit drawable/draggable/deleteable artists on top of a background.
"""
def __init__(self):
self.fig, self.ax = self.setup_axes()
self.xy = []
self.tolerance = 10
self._num_clicks = 0

# The artist we'll be modifying...
self.points = self.ax.scatter([], [], s=200, color='red',
picker=self.tolerance, animated=True)

connect = self.fig.canvas.mpl_connect
connect('button_press_event', self.on_click)
self.draw_cid = connect('draw_event', self.grab_background)

def setup_axes(self):
"""Setup the figure/axes and plot any background artists."""
fig, ax = plt.subplots()

# imshow would be _much_ faster in this case, but let's deliberately
# use something slow...
ax.pcolormesh(np.random.random((1000, 1000)), cmap='gray')

ax.set_title('Left click to add/drag a point\nRight-click to delete')
return fig, ax

def on_click(self, event):
"""Decide whether to add, delete, or drag a point."""
# If we're using a tool on the toolbar, don't add/draw a point...
if self.fig.canvas.toolbar._active is not None:
return

contains, info = self.points.contains(event)
if contains:
i = info['ind'][0]
if event.button == 1:
self.start_drag(i)
elif event.button == 3:
self.delete_point(i)
else:
self.add_point(event)

def update(self):
"""Update the artist for any changes to self.xy."""
self.points.set_offsets(self.xy)
self.blit()

def add_point(self, event):
self.xy.append([event.xdata, event.ydata])
self.update()

def delete_point(self, i):
self.xy.pop(i)
self.update()

def start_drag(self, i):
"""Bind mouse motion to updating a particular point."""
self.drag_i = i
connect = self.fig.canvas.mpl_connect
cid1 = connect('motion_notify_event', self.drag_update)
cid2 = connect('button_release_event', self.end_drag)
self.drag_cids = [cid1, cid2]

def drag_update(self, event):
"""Update a point that's being moved interactively."""
self.xy[self.drag_i] = [event.xdata, event.ydata]
self.update()

def end_drag(self, event):
"""End the binding of mouse motion to a particular point."""
for cid in self.drag_cids:
self.fig.canvas.mpl_disconnect(cid)

def safe_draw(self):
"""Temporarily disconnect the draw_event callback to avoid recursion"""
canvas = self.fig.canvas
canvas.mpl_disconnect(self.draw_cid)
canvas.draw()
self.draw_cid = canvas.mpl_connect('draw_event', self.grab_background)

def grab_background(self, event=None):
"""
When the figure is resized, hide the points, draw everything,
and update the background.
"""
self.points.set_visible(False)
self.safe_draw()

# With most backends (e.g. TkAgg), we could grab (and refresh, in
# self.blit) self.ax.bbox instead of self.fig.bbox, but Qt4Agg, and
# some others, requires us to update the _full_ canvas, instead.
self.background = self.fig.canvas.copy_from_bbox(self.fig.bbox)

self.points.set_visible(True)
self.blit()

def blit(self):
"""
Efficiently update the figure, without needing to redraw the
"background" artists.
"""
self.fig.canvas.restore_region(self.background)
self.ax.draw_artist(self.points)
self.fig.canvas.blit(self.fig.bbox)

def show(self):
plt.show()

DrawDragPoints().show()

关于python - 高效的 Matplotlib 重绘,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29277080/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com