Как обновить сюжет в matplotlib?

156

У меня проблемы с перерисовкой фигуры здесь. Я позволяю пользователю указать единицы измерения во временной шкале (ось X), а затем я пересчитываю и вызываю эту функцию plots(). Я хочу, чтобы сюжет просто обновлялся, а не добавлял другой сюжет к рисунку.

def plots():
    global vlgaBuffSorted
    cntr()

    result = collections.defaultdict(list)
    for d in vlgaBuffSorted:
        result[d['event']].append(d)

    result_list = result.values()

    f = Figure()
    graph1 = f.add_subplot(211)
    graph2 = f.add_subplot(212,sharex=graph1)

    for item in result_list:
        tL = []
        vgsL = []
        vdsL = []
        isubL = []
        for dict in item:
            tL.append(dict['time'])
            vgsL.append(dict['vgs'])
            vdsL.append(dict['vds'])
            isubL.append(dict['isub'])
        graph1.plot(tL,vdsL,'bo',label='a')
        graph1.plot(tL,vgsL,'rp',label='b')
        graph2.plot(tL,isubL,'b-',label='c')

    plotCanvas = FigureCanvasTkAgg(f, pltFrame)
    toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
    toolbar.pack(side=BOTTOM)
    plotCanvas.get_tk_widget().pack(side=TOP)
thenickname
источник
1
Возможный дубликат прорисовки в реальном времени в цикле while с matplotlib
Тревор Бойд Смит

Ответы:

178

По сути, у вас есть два варианта:

  1. Делайте именно то, что вы делаете в данный момент, но звоните graph1.clear()и graph2.clear()до пересадки данных. Это самый медленный, но самый простой и надежный вариант.

  2. Вместо того, чтобы реплотировать, вы можете просто обновить данные объектов графика. Вам нужно будет внести некоторые изменения в ваш код, но это должно быть намного, намного быстрее, чем каждый раз повторять реплоты. Тем не менее, форма данных, которые вы выводите, не может измениться, и если диапазон ваших данных меняется, вам необходимо вручную сбросить ограничения по осям x и y.

Чтобы привести пример второго варианта:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)

# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()

fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma

for phase in np.linspace(0, 10*np.pi, 500):
    line1.set_ydata(np.sin(x + phase))
    fig.canvas.draw()
    fig.canvas.flush_events()
Джо Кингтон
источник
Я пытался проверить "1" и в результате, после того, как я пересчитал данные, в моем GUI был нарисован другой набор графиков, так что теперь у меня было 4 графика после пересчета, как и раньше.
прозвище
@thenickname - Куда именно в вашем коде вы звоните clear? Вы должны звонить graph1.clear(); graph2.clear()внутри своего forцикла, как раз перед тем, как звонить graph1.plot(...), и graph2.plot(...)т.д ...
Джо Кингтон,
Цикл for создает вызовы graphx.plot (...) N раз и помещая в них операторы clear только графики последнего. Я на самом деле вытащил код холста и поместил его в основной цикл программы вместе с кодом рисунка, и теперь моя функция вызывается кнопкой. По какой-то причине, если я просто вызываю функцию, графики обновляются, но если я нажимаю кнопку, графики не обновляются. Это довольно интересное поведение. Я думаю, что это должно быть ошибка в Tkinter.
прозвище
Просто предчувствие ... Вы пропускаете звонок fig.canvas.draw()или plt.draw()после составления графика каждого кадра? (Т.е. вы должны иметь последовательность clear, plot, drawкаждый раз , когда вы хотите , чтобы показать кадр) Я предполагаю, но я думаю , что может вызвать именно поведение вы описываете ... Удачи, во всяком случае!
Джо Кингтон
3
Это 2k14, и я споткнулся, чтобы достичь чего-то вроде этого ... он работает, как ожидалось, но окно графика поворачивает "не отвечает" .. какие-либо предложения ??
DevC
39

Вы также можете сделать следующее: Это нарисует случайные матричные данные 10x1 на графике для 50 циклов цикла for.

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()
Arindam
источник
Это не похоже на вывод графика. Я что-то упускаю? У меня %matplotlib inlineв тетради Jupyter тоже есть.
MasayoMusic
хаха, работал на меня, когда я удалил plt.clf(). О matplotlib, ты негодяй :)
AndyP
15

Это сработало для меня. Неоднократно вызывает функцию обновления графика каждый раз.

import matplotlib.pyplot as plt
import matplotlib.animation as anim

def plot_cont(fun, xmax):
    y = []
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    def update(i):
        yi = fun()
        y.append(yi)
        x = range(len(y))
        ax.clear()
        ax.plot(x, y)
        print i, ': ', yi

    a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
    plt.show()

«fun» - это функция, которая возвращает целое число. FuncAnimation будет неоднократно вызывать «обновление», он будет делать это «xmax» раз.

Vituel
источник
Не могли бы вы привести пример того, как вы вызываете эту функцию (особенно то, как вы передаете функцию при вызове функции), а также как выглядит функция fun ()?
bjornasm
1
Конечно. «fun ()» - это любая функция, которая возвращает целое число. Вы можете передать функцию в качестве аргумента другому, например так: "plot_cont (my_function, 123)". Там я звоню plot_cont в строке 86: github.com/vitobasso/audio-ml/blob/…
Vituel
1
Обратите внимание, что "=" необходимо, иначе FuncAnimation будет собирать мусор и код не будет работать!
Kiuhnm
8

Если кто-то сталкивается с этой статьей в поисках того, что я искал, я нашел примеры на

Как визуализировать скалярные 2D данные с помощью Matplotlib?

и

http://mri.brechmos.org/2009/07/automatics-update-a-figure-in-a-loop (на web.archive.org)

затем изменил их, чтобы использовать imshow со входным стеком кадров вместо генерации и использования контуров на лету.


Начиная с трехмерного массива изображений формы (nBins, nBins, nBins), называется frames.

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

fig = plt.figure()
ax  = fig.add_subplot(111)

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

Я также нашел гораздо более простой способ прохождения всего этого процесса, хотя и менее надежный:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

Обратите внимание , что оба они , кажется , только для работы с ipython --pylab=tkакойbackend = TkAgg

Спасибо за помощь во всем.

CavemanPhD
источник
6

Я выпустил пакет с именем python-drawnow, который предоставляет функциональность, позволяющую обновлять фигуры, обычно вызываемые в цикле for, аналогично Matlab drawnow.

Пример использования:

from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
    # can be arbitrarily complex; just to draw a figure
    #figure() # don't call!
    plot(t, x)
    #show() # don't call!

N = 1e3
figure() # call here instead!
ion()    # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
    x = sin(2 * pi * i**2 * t / 100.0)
    drawnow(draw_fig)

Этот пакет работает с любым рисунком matplotlib и предоставляет опции для ожидания после каждого обновления рисунка или перехода в отладчик.

Скотт
источник
2
Насколько он устойчив и нестабилен одновременно?
BlueMoon93
2
Я имел в виду надежный, как в «работает с любой фигурой Matplotlib» и нестабильный, как в «проекте выходного дня». Я обновил свой ответ
Скотт
3

Все вышесказанное может быть правдой, однако для меня «онлайн-обновление» фигур работает только с некоторыми бэкэндами, в частности wx. Вы можете попробовать изменить это, например, запустив ipython / pylab с помощью ipython --pylab=wx! Удачи!

jkeyser
источник
1
Спасибо за ваше сообщение, я никогда не использовал интерактивный режим, потому что он никогда не работал с бэкэндом по умолчанию, который я использовал. Гораздо приятнее использовать интерактивный режим, чем останавливать выполнение каждый раз, когда вы хотите увидеть график!
PierreE
1
Ни один из других ответов не помог в моем случае. Я использую pycharm, и проблема была с прорисовкой и интерактивностью консоли. Мне нужно было добавить Из pylab import *, а затем ion () в теле кода, чтобы включить интерактив. Теперь у меня все работает гладко.
Shelbypereira
2

Это сработало для меня:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
    clear_output(wait=True)
    y = np.random.random([10,1])
    plt.plot(y)
    plt.show()
юлианский
источник