จะอัพเดตพล็อตใน 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. " และผลลัพธ์ก็คือหลังจากที่ฉันได้ทำการแปลงข้อมูลอีกชุดหนึ่งถูกวาดใน GUI ของฉันดังนั้นตอนนี้ฉันมี 4 แปลงหลังจากการคำนวณใหม่เหมือนก่อนหน้านี้
thenickname

@thenickname - รหัสที่คุณโทรหาอยู่ตรงไหนclear? คุณควรจะเรียกร้องgraph1.clear(); graph2.clear()ภายในของคุณforห่วงเพียงแค่ก่อนที่จะสายgraph1.plot(...), graph2.plot(...)ฯลฯ ...
โจ Kington

สำหรับการวนรอบนั้นจะสร้าง calles graphx.plot (... ) N ครั้งและวางคำสั่งที่ชัดเจนไว้ในนั้นโดยจะทำการแปลงคำสั่งสุดท้ายเท่านั้น ฉันดึงรหัส Canvas ออกมาแล้วใส่เข้าไปในลูปหลักของโปรแกรมพร้อมกับรหัสรูปภาพและตอนนี้ฉันมีฟังก์ชั่นของฉันถูกเรียกโดยปุ่ม ด้วยเหตุผลบางอย่างถ้าฉันเพียงแค่เรียกฟังก์ชั่นแปลงได้รับการปรับปรุง แต่ถ้าฉันกดปุ่มพล็อตไม่ได้ มันเป็นพฤติกรรมที่น่าสนใจ ฉันคิดว่าต้องเป็นข้อบกพร่องใน Tkinter
thenickname

เพียงลางสังหรณ์ ... คุณไม่ได้รับสายfig.canvas.draw()หรือplt.draw()หลังจากการวางแผนแต่ละเฟรม? (เช่นคุณควรจะมีลำดับของclear, plot, drawทุกครั้งที่คุณต้องการที่จะแสดงกรอบ) ฉันคาดเดา แต่ผมคิดว่าจะทำให้เกิดว่าพฤติกรรมที่คุณอธิบาย ... ขอให้โชคดีในอัตราใด ๆ !
Joe Kington

3
มันเป็น 2k14 และฉันสะดุดเพื่อให้บรรลุสิ่งนี้ ... มันทำงานได้ตามที่คาดไว้ แต่หน้าต่างการวางแผนกำลังเปลี่ยนเป็น "ไม่ตอบสนอง" .. ข้อเสนอแนะใด ๆ ??
DevC

39

คุณยังสามารถทำสิ่งต่อไปนี้: นี่จะวาดข้อมูลเมทริกซ์แบบสุ่ม 10x1 บนพล็อตเป็นเวลา 50 รอบของลูปสำหรับ

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ในสมุดบันทึก Jupyter เช่นกัน
MasayoMusic

ฮ่า ๆ plt.clf()ทำงานให้ฉันเมื่อฉันออก โอ้matplotlibคุณ
ร้ายกาจ

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/
......

1
โปรดทราบว่าจำเป็นต้องใช้ "a =" มิฉะนั้น FuncAnimation จะถูกรวบรวมขยะและรหัสจะไม่ทำงาน!
Kiuhnm

8

ในกรณีที่ทุกคนเจอบทความนี้เพื่อค้นหาสิ่งที่ฉันกำลังมองหาฉันพบตัวอย่างที่

วิธีการเห็นภาพข้อมูลสเกลาร์ 2 มิติด้วย Matplotlib

และ

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

จากนั้นปรับเปลี่ยนให้ใช้ imshow กับอินพุตสแต็กของเฟรมแทนที่จะสร้างและใช้รูปทรงในทันที


เริ่มต้นด้วยอาร์เรย์ 3 มิติของภาพของรูปทรง (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 for loop คล้ายกับ Matlab'sdrawnowMatlab

ตัวอย่างการใช้งาน:

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 ใด ๆ " และไม่แน่นอนเช่นเดียวกับใน "โครงการวันหยุดสุดสัปดาห์" ฉันได้อัปเดตคำตอบของฉันแล้ว
Scott

3

ทั้งหมดที่กล่าวมาอาจจะจริง แต่สำหรับฉัน "ออนไลน์การอัปเดต" wxของตัวเลขเท่านั้นทำงานร่วมกับแบ็กเอนด์บางส่วนโดยเฉพาะ คุณอาจลองเปลี่ยนเป็นเช่นเริ่มจาก ipython / pylab โดย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()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.