Bagaimana cara memperbarui plot di matplotlib?


156

Saya mengalami masalah dengan menggambar ulang gambar di sini. Saya mengizinkan pengguna untuk menentukan unit dalam skala waktu (sumbu x) dan kemudian saya menghitung ulang dan memanggil fungsi ini plots(). Saya ingin plot hanya memperbarui, tidak menambahkan plot lain pada gambar.

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)

Jawaban:


178

Anda pada dasarnya memiliki dua opsi:

  1. Lakukan persis apa yang sedang Anda lakukan, tetapi panggil graph1.clear()dan graph2.clear()sebelum mengganti data. Ini adalah opsi paling lambat, tetapi paling sederhana dan paling kuat.

  2. Alih-alih mengganti, Anda bisa memperbarui data objek plot. Anda harus membuat beberapa perubahan dalam kode Anda, tetapi ini harus jauh, lebih cepat daripada mengganti hal-hal setiap saat. Namun, bentuk data yang Anda rencanakan tidak dapat berubah, dan jika rentang data Anda berubah, Anda harus mengatur ulang batas sumbu x dan y secara manual.

Untuk memberikan contoh opsi kedua:

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

Saya mencoba pengujian "1." dan hasilnya adalah, setelah saya mengganti data, satu set plot diambil di GUI saya, jadi sekarang saya punya 4 plot setelah perhitungan ulang, seperti sebelumnya.
thenickname

@thenickname - Di mana tepatnya dalam kode Anda Anda menelepon clear? Anda harus menelepon graph1.clear(); graph2.clear()di dalam forlingkaran Anda , tepat sebelum menelepon graph1.plot(...), graph2.plot(...)dll ...
Joe Kington

Itu untuk loop membuat panggilan graphx.plot (...) N kali dan menempatkan pernyataan yang jelas di sana hanya plot yang terakhir. Saya benar-benar mengeluarkan kode kanvas dan memasukkannya ke dalam loop program utama bersama dengan kode angka dan sekarang saya memiliki fungsi saya dipanggil oleh sebuah tombol. Untuk beberapa alasan jika saya hanya memanggil fungsi, plot dapat diperbarui, tetapi jika saya menekan tombol, plot tidak diperbarui. Itu perilaku yang cukup menarik. Saya pikir itu pasti bug di Tkinter.
thenickname

Hanya dugaan ... Apakah Anda kehilangan panggilan fig.canvas.draw()atau plt.draw()setelah merencanakan setiap frame? (Yaitu Anda harus memiliki urutanclear , plot, drawsetiap kali Anda ingin menampilkan frame) aku menebak, tapi saya pikir itu akan menyebabkan persis perilaku Anda menggambarkan ... Good luck, pada setiap tingkat!
Joe Kington

3
Ini 2k14 dan saya tersandung untuk mencapai sesuatu seperti ini ... itu berfungsi seperti yang diharapkan tetapi jendela plot berubah "tidak merespons" .. ada saran ??
DevC

39

Anda juga dapat melakukan hal-hal berikut: Ini akan menggambar data matriks acak 10x1 pada plot selama 50 siklus dari for loop.

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

Ini sepertinya tidak menghasilkan grafik. Apakah saya melewatkan sesuatu? Saya punya %matplotlib inlinedi notebook Jupyter juga.
MasayoMusic

haha, bekerja untuk saya ketika saya menghapus plt.clf(). Oh matplotlib, kau bajingan :)
AndyP

15

Ini berhasil untuk saya. Berulang kali memanggil fungsi yang memperbarui grafik setiap saat.

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" adalah fungsi yang mengembalikan integer. FuncAnimation akan berulang kali memanggil "pembaruan", itu akan melakukan itu "xmax" kali.


Bisakah Anda memberikan contoh tentang bagaimana Anda memanggil fungsi ini (terutama bagaimana Anda melewatkan fungsi dalam panggilan fungsi) serta bagaimana fungsi fun () terlihat?
bjornasm

1
Tentu. "fun ()" adalah fungsi apa pun yang mengembalikan bilangan bulat. Anda dapat meneruskan fungsi sebagai argumen ke argumen lain seperti ini: "plot_cont (my_function, 123)". Di sana Anda meminta saya menelepon plot_cont di baris 86: github.com/vitobasso/audio-ml/blob/…
Vituel

1
Perhatikan bahwa "a =" diperlukan atau FuncAnimation akan menjadi sampah yang dikumpulkan dan kode tidak akan berfungsi!
Kiuhnm

8

Jika ada orang yang menemukan artikel ini untuk mencari apa yang saya cari, saya menemukan contoh di

Bagaimana cara memvisualisasikan data 2D skalar dengan Matplotlib?

dan

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

kemudian memodifikasinya untuk menggunakan imshow dengan tumpukan input bingkai, alih-alih menghasilkan dan menggunakan kontur dengan cepat.


Dimulai dengan array 3D gambar bentuk (nBins, nBins, nBins), disebut 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)

Saya juga menemukan cara yang lebih sederhana untuk menyelesaikan seluruh proses ini, meskipun kurang kuat:

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

Perhatikan bahwa kedua ini sepertinya hanya bekerja dengan ipython --pylab=tkaliasbackend = TkAgg

Terima kasih atas bantuannya untuk semuanya.


6

Saya telah merilis paket yang disebut python-drawow yang menyediakan fungsionalitas untuk membiarkan pembaruan angka, biasanya disebut dalam for loop, mirip dengan Matlab drawnow.

Contoh penggunaan:

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)

Paket ini berfungsi dengan semua figur matplotlib dan menyediakan opsi untuk menunggu setelah setiap pembaruan angka atau jatuh ke debugger.


2
Bagaimana itu kuat dan tidak stabil pada saat yang sama?
BlueMoon93

2
Maksud saya kuat seperti dalam "bekerja dengan angka matplotlib" dan tidak stabil seperti dalam "proyek akhir pekan". Saya telah memperbarui jawaban saya
Scott

3

Semua hal di atas mungkin benar, namun bagi saya "pemutakhiran online" angka hanya bekerja dengan beberapa backend, khususnya wx. Anda mungkin mencoba mengubah ini, misalnya dengan memulai ipython / pylab dengan ipython --pylab=wx! Semoga berhasil!


1
Terima kasih atas pesan Anda, saya tidak pernah menggunakan mode interaktif karena tidak pernah berfungsi dengan backend default yang saya gunakan. Jauh lebih baik menggunakan mode interaktif daripada menghentikan eksekusi setiap kali Anda ingin melihat grafik!
PierreE

1
Tidak ada jawaban lain yang membantu dalam kasus saya. Saya menggunakan pycharm dan masalahnya adalah dengan merencanakan dan interaktivitas dari konsol. Saya perlu menambahkan From pylab import * dan kemudian ion () di badan kode untuk menghidupkan interaktif. Sekarang berfungsi dengan baik untuk saya.
shelbypereira

2

Ini bekerja untuk saya:

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()
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.