Як оновити сюжет у 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)

Відповіді:


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." і результат був після того, як я повторно використала дані, в моєму графічному інтерфейсі було намальовано ще один набір сюжетів, тож тепер у мене було 4 сюжети після перерахунку, як і раніше.
прізвище

@thenickname - Куди саме у своєму коді телефонуєте clear? Вам слід дзвонити graph1.clear(); graph2.clear()всередині вашого forциклу, безпосередньо перед тим, як дзвонити graph1.plot(...), graph2.plot(...)тощо ...
Джо Кінгтон

Це для циклу створює calles graphx.plot (...) N разів, а чіткі заяви там розміщують лише останню. Я фактично витягнув код канви і помістив його в основний цикл програми разом з кодом фігури, і тепер у мене функція викликається кнопкою. Чомусь, якщо я просто називаю функцію, сюжети оновлюються, але якщо я натискаю кнопку, сюжети не роблять. Це досить цікава поведінка. Я думаю, що це має бути помилка в 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()

Схоже, це не відображає графік. Я щось пропускаю? У мене є %matplotlib inlineі зошит Юпітера.
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" разів.


Чи можете ви навести приклад того, як ви викликаєте цю функцію (особливо як ви передаєте функцію під час виклику функції), а також як виглядає функція fun ()?
bjornasm

1
Звичайно. "fun ()" - це будь-яка функція, яка повертає ціле число. Ви можете передати функцію як аргумент іншому на зразок цього: "plot_cont (my_function, 123)". Там ви телефонуєте plot_cont на лінію 86: github.com/vitobasso/audio-ml/blob/…
Vituel

1
Зауважте, що "a =" необхідний, або FuncAnimation буде збирати сміття, і код не працюватиме!
Кіунм

8

Якщо хтось стикається з цією статтею, шукає те, що я шукав, я знайшов приклади

Як візуалізувати скалярні 2D дані за допомогою Matplotlib?

і

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

потім модифікували їх, щоб використовувати імшоу з вхідним стеком кадрів, а не генерувати та використовувати контури на льоту.


Починаючи з 3D-масиву зображень форми (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

Дякую за допомогу у всьому.


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 by ipython --pylab=wx! Удачі!


1
Дякую за ваше повідомлення, я ніколи не використовував інтерактивний режим, тому що він ніколи не працював із замовчуванням, який я використовував. Набагато приємніше використовувати інтерактивний режим, ніж зупиняти виконання кожного разу, коли ви хочете побачити графік!
PierreE

1
Жодна з інших відповідей у ​​моєму випадку не допомогла. Я використовую pycharm, і проблема полягала в побудові графіки та інтерактивності консолі. Мені потрібно було додати з імпорту * pylab *, а потім 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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.