Làm thế nào để cập nhật một cốt truyện trong matplotlib?


156

Tôi đang gặp vấn đề với việc vẽ lại hình ở đây. Tôi cho phép người dùng chỉ định các đơn vị theo thang thời gian (trục x) và sau đó tôi tính toán lại và gọi hàm này plots(). Tôi muốn cốt truyện chỉ đơn giản là cập nhật, không nối thêm một cốt truyện khác vào hình.

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)

Câu trả lời:


178

Về cơ bản, bạn có hai lựa chọn:

  1. Làm chính xác những gì bạn đang làm, nhưng gọi graph1.clear()graph2.clear()trước khi thay thế dữ liệu. Đây là tùy chọn chậm nhất, nhưng đơn giản nhất và mạnh mẽ nhất.

  2. Thay vì thay thế, bạn chỉ có thể cập nhật dữ liệu của các đối tượng cốt truyện. Bạn sẽ cần thực hiện một số thay đổi trong mã của mình, nhưng điều này sẽ nhanh hơn nhiều so với việc thay thế mọi thứ mỗi lần. Tuy nhiên, hình dạng của dữ liệu mà bạn đang vẽ không thể thay đổi và nếu phạm vi dữ liệu của bạn thay đổi, bạn sẽ cần đặt lại thủ công các giới hạn trục x và y.

Để đưa ra một ví dụ về tùy chọn thứ hai:

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

Tôi đã thử kiểm tra "1." và kết quả là, sau khi tôi thay thế dữ liệu, một tập hợp các ô khác đã được vẽ trong GUI của tôi, vì vậy bây giờ tôi đã có 4 ô sau khi tính toán lại, giống như trước đây.
thenickname

@thenickname - Bạn đang gọi chính xác ở đâu trong mã của bạn clear? Bạn nên gọi graph1.clear(); graph2.clear()bên trong forvòng lặp của mình , ngay trước khi bạn gọi graph1.plot(...), graph2.plot(...)v.v ...
Joe Kington

Vòng lặp đó tạo ra các lệnh gọi graphx.plot (...) N lần và đặt các câu lệnh rõ ràng trong đó chỉ vẽ sơ đồ cuối cùng. Tôi thực sự đã rút mã canvas và đưa nó vào vòng lặp chương trình chính cùng với mã hình và bây giờ tôi có chức năng của mình được gọi bằng một nút. Vì một số lý do nếu tôi chỉ gọi hàm thì các ô được cập nhật, nhưng nếu tôi nhấn nút thì các ô không được. Đó là hành vi khá thú vị. Tôi nghĩ đó phải là một lỗi trong Tkinter.
thenickname

Chỉ là một linh cảm ... Bạn đang bỏ lỡ một cuộc gọi fig.canvas.draw()hoặc plt.draw()sau khi vẽ từng khung hình? (Tức là bạn nên có một chuỗi các clear, plot, drawmỗi lần bạn muốn hiển thị một khung hình) Tôi đoán, nhưng tôi nghĩ rằng sẽ gây ra chính xác những hành vi mà bạn đang mô tả ... Chúc may mắn, ở mức nào!
Joe Kington

3
Đó là 2k14 và tôi đã vấp ngã để đạt được thứ gì đó như thế này ... nó hoạt động như mong đợi nhưng cửa sổ âm mưu đang "không phản hồi" .. có đề xuất nào không ??
DevC

39

Bạn cũng có thể làm như sau: Điều này sẽ vẽ dữ liệu ma trận ngẫu nhiên 10 x1 trên ô trong 50 chu kỳ của vòng lặp 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()

Điều này dường như không xuất ra một biểu đồ. Tui bỏ lỡ điều gì vậy? Tôi có %matplotlib inlinetrong máy tính xách tay Jupyter là tốt.
MasayoMusic

haha, làm việc cho tôi khi tôi gỡ bỏ plt.clf(). Ôi matplotlib, bạn thật điên rồ :)
AndyP

15

Điều này làm việc cho tôi. Liên tục gọi một chức năng cập nhật biểu đồ mỗi lần.

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

"vui vẻ" là một hàm trả về một số nguyên. FuncAnimation sẽ liên tục gọi "cập nhật", nó sẽ thực hiện "xmax" lần đó.


Bạn có thể đưa ra một ví dụ về cách bạn gọi hàm này (đặc biệt là cách bạn truyền hàm trong một lệnh gọi hàm) cũng như hàm fun () trông như thế nào không?
bjornasm 30/03/2015

1
Chắc chắn rồi. "fun ()" là bất kỳ hàm nào trả về một số nguyên. Bạn có thể truyền hàm dưới dạng đối số cho đối số khác như sau: "plot_cont (my_factor, 123)". Ở đó, bạn có tôi gọi plot_cont ở dòng 86: github.com/vitobasso/audio-ml/blob/
mẹo

1
Lưu ý rằng "a =" là cần thiết hoặc FuncAnimation sẽ là rác được thu thập và mã sẽ không hoạt động!
Kiuhnm

8

Trong trường hợp bất kỳ ai đi qua bài viết này để tìm kiếm những gì tôi đang tìm kiếm, tôi đã tìm thấy các ví dụ tại

Làm thế nào để trực quan hóa dữ liệu 2D vô hướng với Matplotlib?

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

sau đó sửa đổi chúng để sử dụng imshow với một chồng khung hình đầu vào, thay vì tạo và sử dụng các đường viền khi đang bay.


Bắt đầu với một mảng 3D hình ảnh có hình dạng (nBins, nBins, nBins), được gọi 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)

Tôi cũng tìm thấy một cách đơn giản hơn nhiều để thực hiện toàn bộ quá trình này, mặc dù ít mạnh mẽ hơn:

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

Lưu ý rằng cả hai dường như chỉ hoạt động với ipython --pylab=tk, akabackend = TkAgg

Cảm ơn bạn đã giúp đỡ với tất cả mọi thứ.


6

Tôi đã phát hành một gói có tên python-drawow cung cấp chức năng để cho phép cập nhật hình, thường được gọi trong vòng lặp for, tương tự như Matlab drawnow.

Một ví dụ sử dụng:

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)

Gói này hoạt động với bất kỳ con số matplotlib nào và cung cấp các tùy chọn để chờ sau mỗi lần cập nhật con số hoặc thả vào trình gỡ lỗi.


2
Làm thế nào là nó mạnh mẽ và không ổn định cùng một lúc?
BlueMoon93

2
Tôi có nghĩa là mạnh mẽ như trong "làm việc với bất kỳ con số matplotlib" và không ổn định như trong "dự án cuối tuần". Tôi đã cập nhật câu trả lời của mình
Scott

3

Tất cả những điều trên có thể đúng, tuy nhiên đối với tôi, "cập nhật trực tuyến" các số liệu chỉ hoạt động với một số phụ trợ, cụ thể wx. Bạn chỉ có thể cố gắng thay đổi điều này, ví dụ bằng cách bắt đầu ipython / pylab bằng cách ipython --pylab=wx! Chúc may mắn!


1
Cảm ơn tin nhắn của bạn, tôi không bao giờ sử dụng chế độ tương tác vì nó không bao giờ hoạt động với phụ trợ mặc định mà tôi đã sử dụng. Sử dụng chế độ tương tác sẽ tốt hơn nhiều so với việc dừng thực thi mỗi khi bạn muốn xem biểu đồ!
PierreE

1
Không có câu trả lời nào khác giúp ích trong trường hợp của tôi. Tôi đang sử dụng pycharm và vấn đề là về âm mưu và tính tương tác của giao diện điều khiển. Tôi cần thêm Từ nhập pylab * và sau đó ion () trong thân mã để bật tương tác. Nó hoạt động trơn tru bây giờ cho tôi.
Shelbypereira

2

Điều này làm việc cho tôi:

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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.