Очистка фона в matplotlib с помощью wxPython - PullRequest
6 голосов
/ 19 ноября 2010

Я хочу создать анимацию с помощью 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)

1 Ответ

5 голосов
/ 20 ноября 2010

Происходит то, что вы каждый раз добавляете новые патчи на график, а затем рисуете их все при вызове self.canvas.draw().

Самое быстрое решение - просто позвонить self.canvas.draw_artist(ellipsePlot) после добавления каждого патча и удалить вызов на self.canvas.draw()

В качестве простого, автономного примера:

# 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)

Однако это, вероятно, вызовет проблемы потребления памяти с течением времени, так как объект axes будет отслеживать всех артистов, добавленных к нему. Если ваши оси не зависают в течение длительного времени, это может быть незначительным, но вы должны по крайней мере знать, что это приведет к утечке памяти. Одним из способов решения этой проблемы является удаление художников эллипса с осей, вызывая 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...