我想用 matplotlib 创建一个动画来监控聚类算法的收敛。它应该在第一次调用时绘制我的数据的散点图,并在每次更新图时绘制错误椭圆。我正在尝试使用 canvas_copy_from_bbox()
和 restore_region()
来保存散点图,然后在我更新散点图时绘制一组新的椭圆。
然而,代码只是在旧椭圆之上绘制新椭圆,而没有先清除之前的绘图。
我怀疑,不知何故,这种方法不适用于 Ellipse()
和 add_path()
命令,但我不知道如何解决这个问题。
代码如下:
import wx
import math
from math import pi
from matplotlib.patches import Ellipse
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigureCanvas
TIMER_ID = wx.NewId()
class _MonitorPlot(wx.Frame):
def __init__(self, data, scale=1):
self.scale = scale
wx.Frame.__init__(self, None, wx.ID_ANY,
title="FlowVB Progress Monitor", size=(800, 600))
self.fig = Figure((8, 6), 100)
self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig)
self.ax = self.fig.add_subplot(111)
x_lims = [data[:, 0].min(), data[:, 0].max()]
y_lims = [data[:, 1].min(), data[:, 1].max()]
self.ax.set_xlim(x_lims)
self.ax.set_ylim(y_lims)
self.ax.set_autoscale_on(False)
self.l_data = self.ax.plot(data[:, 0], data[:, 1], color='blue',
linestyle='', marker='o')
self.canvas.draw()
self.bg = self.canvas.copy_from_bbox(self.ax.bbox)
self.Bind(wx.EVT_IDLE, self._onIdle)
def update_plot(self, pos, cov):
self.canvas.restore_region(self.bg)
for k in range(pos.shape[0]):
l_center, = self.ax.plot(pos[k, 0], pos[k, 1],
color='red', marker='+')
U, s, Vh = np.linalg.svd(cov[k, :, :])
orient = math.atan2(U[1, 0], U[0, 0]) * 180 / pi
ellipsePlot = Ellipse(xy=pos[k, :], width=2.0 * math.sqrt(s[0]),
height=2.0 * math.sqrt(s[1]),
angle=orient, facecolor='none',
edgecolor='red')
self.ax.add_patch(ellipsePlot)
self.canvas.draw()
self.canvas.blit(self.ax.bbox)
最佳答案
实际情况是,您每次都在向绘图添加新的补丁,然后在调用 self.canvas.draw()
时绘制所有这些补丁。
最快的解决方法是在添加每个补丁后调用 self.canvas.draw_artist(ellipsePlot)
并删除对 self.canvas.draw()
的调用。 p>
作为一个简单的独立示例:
# Animates 3 ellipses overlain on a scatterplot
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
import numpy as np
num = 10
x = np.random.random(num)
y = np.random.random(num)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line = ax.plot(x, y, 'bo')
fig.canvas.draw()
bg = fig.canvas.copy_from_bbox(ax.bbox)
# Pseudo-main loop
for i in range(100):
fig.canvas.restore_region(bg)
# Make a new ellipse each time... (inefficient!)
for i in range(3):
width, height, angle = np.random.random(3)
angle *= 180
ellip = Ellipse(xy=(0.5, 0.5), width=width, height=height,
facecolor='red', angle=angle, alpha=0.5)
ax.add_patch(ellip)
ax.draw_artist(ellip)
fig.canvas.blit(ax.bbox)
但是,随着时间的推移,这可能会导致内存消耗问题,因为轴对象会跟踪添加到其中的所有艺术家。如果你的轴长时间不挂起,这可能可以忽略不计,但你至少应该意识到它会导致内存泄漏。解决此问题的一种方法是通过在绘制每个椭圆后调用 ax.remove(ellipsePlot)
从轴上删除省略号艺术家。然而,这仍然有点低效,因为你不断地创建和销毁椭圆艺术家,而你可以只更新它们。 (创建和销毁它们根本没有太多开销,不过,这主要是一个风格问题......)
如果椭圆的数量随着时间的推移保持不变,那么只更新每个椭圆艺术家对象的属性而不是创建和添加新的对象会更好也更容易。这将避免从轴上删除“旧”省略号,因为只有您需要的数字才会存在。
作为一个简单的独立示例:
# Animates 3 ellipses overlain on a scatterplot
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
import numpy as np
num = 10
x = np.random.random(num)
y = np.random.random(num)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line = ax.plot(x, y, 'bo')
fig.canvas.draw()
bg = fig.canvas.copy_from_bbox(ax.bbox)
# Make and add the ellipses the first time (won't ever be drawn)
ellipses = []
for i in range(3):
ellip = Ellipse(xy=(0.5, 0.5), width=1, height=1,
facecolor='red', alpha=0.5)
ax.add_patch(ellip)
ellipses.append(ellip)
# Pseudo-main loop
for i in range(100):
fig.canvas.restore_region(bg)
# Update the ellipse artists...
for ellip in ellipses:
ellip.width, ellip.height, ellip.angle = np.random.random(3)
ellip.angle *= 180
ax.draw_artist(ellip)
fig.canvas.blit(ax.bbox)
关于python - 使用 wxPython 清除 matplotlib 中的背景,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4222344/