การพล็อตแบบไม่ปิดกั้นด้วย Matplotlib


147

ฉันเล่นกับ Numpy และ matplotlib ในช่วงสองสามวันที่ผ่านมา ฉันมีปัญหาในการพยายามทำให้ matplotlib พล็อตฟังก์ชันโดยไม่ปิดกั้นการดำเนินการ ฉันรู้ว่ามีกระทู้มากมายที่ SO ถามคำถามที่คล้ายกันและฉันก็ googled ค่อนข้างมาก แต่ยังไม่สามารถทำงานนี้ได้

ฉันได้ลองใช้ show (block = False) ตามที่บางคนแนะนำ แต่สิ่งที่ฉันได้รับคือหน้าต่างที่ค้าง ถ้าฉันเรียก show () ผลลัพธ์จะถูกวางแผนอย่างถูกต้อง แต่การดำเนินการจะถูกบล็อกจนกว่าหน้าต่างจะปิด จากหัวข้ออื่น ๆ ที่ฉันเคยอ่านฉันสงสัยว่าการแสดง (block = False) ทำงานหรือไม่ขึ้นอยู่กับแบ็กเอนด์ ถูกต้องหรือไม่ ส่วนหลังของฉันคือ Qt4Agg คุณช่วยดูรหัสของฉันและบอกฉันว่าคุณเห็นสิ่งผิดปกติหรือไม่? นี่คือรหัสของฉัน ขอบคุณสำหรับความช่วยเหลือ

from math import *
from matplotlib import pyplot as plt
print plt.get_backend()



def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print y

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __name__ == '__main__':
    main()

ปล. ฉันลืมบอกไปว่าฉันต้องการอัปเดตหน้าต่างที่มีอยู่ทุกครั้งที่ฉันวางแผนอะไรบางอย่างแทนที่จะสร้างใหม่


1
คุณเคยลองโหมดโต้ตอบ matplotlib plt.ion()มาก่อนplt.show()หรือไม่? จากนั้นควรไม่ปิดกั้นเนื่องจากแต่ละพล็อตถูกสร้างเป็นเธรดย่อย
Anzel

@ แอนเซลฉันเพิ่งลองใช้ แต่ดูเหมือนว่าจะไม่แตกต่าง
opetroch

3
คุณเรียกใช้สคริปต์ของคุณอย่างไร หากฉันเรียกใช้โค้ดตัวอย่างของคุณจากเทอร์มินัล / พรอมต์คำสั่งดูเหมือนว่าจะทำงานได้ดี แต่ฉันคิดว่าฉันเคยมีปัญหาในอดีตเมื่อพยายามทำสิ่งนี้จาก IPython QtConsole หรือ IDE
Marius

1
@ มาริอุสอ๊าาา !! คุณพูดถูก อันที่จริงฉันกำลังเรียกใช้จากคอนโซลของ IDE ของฉัน (PyCharm) เมื่อเรียกใช้จากพรอมต์ cmd plt.show (block = False) ทำงานได้ดี! ฉันจะถามมากเกินไปหรือไม่ถ้าฉันถามคุณว่าคุณพบแนวคิด / วิธีแก้ปัญหาหรือไม่? ขอบคุณมาก!
opetroch

ไม่รู้ขอโทษจริงๆ ผมไม่เข้าใจจริงๆรายละเอียดของวิธีปฏิสัมพันธ์ matplotlib matplotlibกับคอนโซลดังนั้นฉันโดยทั่วไปก็สลับกับการทำงานจากคำสั่งพรอมต์ถ้าผมต้องทำสิ่งนี้ด้วย
Marius

คำตอบ:


176

ฉันใช้เวลานานในการค้นหาวิธีแก้ปัญหาและพบคำตอบนี้

ดูเหมือนว่าในการสั่งซื้อที่จะได้รับสิ่งที่คุณ (และฉัน) ต้องการที่คุณจะต้องรวมกันของplt.ion(), plt.show()(ไม่ได้ด้วยblock=False) และที่สำคัญที่สุดplt.pause(.001)(หรืออะไรก็ตามเวลาที่คุณต้องการ) หยุดเป็นสิ่งจำเป็นเพราะเหตุการณ์ GUI เกิดขึ้นในขณะที่รหัสหลักที่กำลังนอนหลับรวมทั้งการวาดภาพ เป็นไปได้ว่าสิ่งนี้ถูกนำไปใช้โดยการเลือกเวลาจากเธรดการนอนหลับดังนั้น IDE อาจยุ่งกับสิ่งนั้นฉันไม่รู้

นี่คือการใช้งานที่เหมาะกับฉันใน python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __name__ == '__main__':
    main()

3
คำตอบของคุณช่วยฉันได้มากในการแก้ปัญหาที่คล้ายกันที่ฉันพบ ก่อนหน้านี้ฉันplt.drawทำตามplt.show(block = False)แต่แล้วมันก็หยุดทำงาน: รูปไม่ตอบสนองปิดมันพัง iPython วิธีการแก้ปัญหาของฉันถูกลบอินสแตนซ์ของทุกและแทนที่ด้วยplt.draw() plt.pause(0.001)แทนที่จะต้องมันตามมาด้วยplt.show(block = False)เหมือนplt.drawเดิมก่อนที่มันจะถูกนำหน้าด้วยและplt.ion() plt.show()ตอนนี้ฉันมีMatplotlibDeprecationWarningแต่ให้ฉันวางแผนตัวเลขของฉันดังนั้นฉันจึงพอใจกับวิธีแก้ปัญหานี้
blue_chip

3
โปรดทราบว่าในหลาม 2.7 คุณจำเป็นต้องใช้ไม่ได้raw_input inputดูที่นี่
คริส

วิธีแก้ปัญหาที่มีประโยชน์จริงๆเมื่อไม่สามารถใช้วิธีการ "เคลื่อนไหว" ที่ตอบสนองได้! ใครทราบวิธีกำจัดคำเตือนการเลิกใช้งาน
Frederic Fortier

ใครช่วยบอกทีว่าทำไมฉันถึงได้รับคำสั่ง freezen เมื่อฉันพยายามเพิ่ม plt.ion ก่อน plt.show?
Gabriel Augusto

@GabrielAugusto ฉันไม่แน่ใจว่าอะไรเป็นสาเหตุและฉันไม่แน่ใจว่าคุณหมายถึงอะไร ฉันเพิ่งทดสอบตัวอย่างนี้ใน Python 3.6 และยังใช้งานได้ หากคุณใช้รูปแบบเดียวกันและค้างอาจมีบางอย่างผิดปกติกับการติดตั้งของคุณ คุณควรตรวจสอบว่าการพล็อตปกติใช้ได้ผลก่อนหรือไม่ หากคุณลองทำอะไรที่แตกต่างออกไปความคิดเห็นก็ไม่ต้องทำอะไรมากมาย ไม่ว่าในกรณีใดคุณอาจพิจารณาถามคำถามแยกกัน
krs013

24

เคล็ดลับง่ายๆที่เหมาะกับฉันมีดังต่อไปนี้:

  1. ใช้block = Falseอาร์กิวเมนต์ภายใน show: plt.show (block = False)
  2. ใช้plt.show () อื่น ต่อท้ายสคริปต์. py

ตัวอย่าง :

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

หมายเหตุ : plt.show()เป็นบรรทัดสุดท้ายของสคริปต์ของฉัน


8
สิ่งนี้ก่อให้เกิด (สำหรับฉันบน Linux, Anaconda, Python 2.7, แบ็กเอนด์เริ่มต้น) ซึ่งเป็นหน้าต่างว่างเปล่าซึ่งยังคงว่างเปล่าจนกว่าจะสิ้นสุดการดำเนินการเมื่อได้รับการกรอกข้อมูลในที่สุดไม่มีประโยชน์สำหรับการอัปเดตพล็อตในระหว่างการดำเนินการ :-(
sh37211

@ sh37211 ไม่แน่ใจว่าเป้าหมายของคุณคืออะไร ในบางกรณีที่คุณพยายามลงจุดบางอย่าง แต่หลังจากคำสั่ง plot คุณมีคำสั่งอื่นสิ่งนี้มีประโยชน์เนื่องจากช่วยให้คุณสามารถลงจุดและเรียกใช้คำสั่งอื่น ๆ ได้ ดูโพสต์เพิ่มเติมได้ที่stackoverflow.com/questions/458209/… . หากคุณต้องการอัปเดตพล็อตควรเป็นวิธีอื่น
seralouk

18

คุณสามารถหลีกเลี่ยงการปิดกั้นการดำเนินการได้โดยการเขียนพล็อตไปยังอาร์เรย์จากนั้นแสดงอาร์เรย์ในเธรดอื่น นี่คือตัวอย่างของการสร้างและแสดงพล็อตพร้อมกันโดยใช้ pf.screen จากpyformulas 0.2.8 :

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

ผลลัพธ์:

แอนิเมชั่นไซน์

ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้ดูแล pyformulas

การอ้างอิง: Matplotlib: บันทึกพล็อตไปยังอาร์เรย์ numpy


9

คำตอบจำนวนมากเหล่านี้สูงเกินจริงและจากสิ่งที่ฉันหาได้คำตอบไม่ใช่เรื่องยากที่จะเข้าใจ

คุณสามารถใช้ได้plt.ion()หากต้องการ แต่ฉันพบว่าการใช้plt.draw()มีประสิทธิภาพพอ ๆ กัน

สำหรับโครงการที่เฉพาะเจาะจงของฉันฉันวางแผนภาพ แต่คุณสามารถใช้plot()หรือscatter()หรือสิ่งแทนfigimage()ก็ไม่ได้เรื่อง

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

หรือ

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

หากคุณกำลังใช้รูปจริง
ฉันใช้ @ krs013 และคำตอบของ @Default Picture เพื่อหาสิ่งนี้
หวังว่าจะช่วยไม่ให้ใครบางคนเปิดตัวทุกรูปในเธรดที่แยกจากกันหรือจากการต้องอ่านนวนิยายเหล่านี้เพื่อหาสิ่งนี้


5

พล็อตสด

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

หากปริมาณข้อมูลมากเกินไปคุณสามารถลดอัตราการอัปเดตได้ด้วยตัวนับธรรมดา

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

ถือแผนหลังจากออกจากโปรแกรม

นี่เป็นปัญหาจริงของฉันที่ไม่สามารถหาคำตอบที่น่าพอใจได้ฉันต้องการพล็อตที่ไม่ปิดหลังจากสคริปต์เสร็จสิ้น (เช่น MATLAB)

หากคุณคิดเกี่ยวกับเรื่องนี้หลังจากสคริปต์เสร็จสิ้นโปรแกรมจะถูกยกเลิกและไม่มีวิธีเชิงตรรกะในการยึดพล็อตด้วยวิธีนี้ดังนั้นจึงมีสองตัวเลือก

  1. บล็อกสคริปต์ไม่ให้ออก (นั่นคือ plt.show () ไม่ใช่สิ่งที่ฉันต้องการ)
  2. เรียกใช้พล็อตบนเธรดแยกต่างหาก (ซับซ้อนเกินไป)

สิ่งนี้ไม่เป็นที่พอใจสำหรับฉันดังนั้นฉันจึงพบวิธีแก้ปัญหาอื่นนอกกรอบ

SaveToFile และดูในโปรแกรมดูภายนอก

สำหรับสิ่งนี้การบันทึกและการดูควรเป็นไปอย่างรวดเร็วและโปรแกรมดูไม่ควรล็อกไฟล์และควรอัปเดตเนื้อหาโดยอัตโนมัติ

การเลือกรูปแบบสำหรับการบันทึก

รูปแบบที่ใช้เวกเตอร์มีทั้งขนาดเล็กและรวดเร็ว

  • SVGนั้นดี แต่ไม่พบผู้ดูที่ดียกเว้นเว็บเบราว์เซอร์ซึ่งโดยค่าเริ่มต้นต้องรีเฟรชด้วยตนเอง
  • PDFสามารถรองรับรูปแบบเวกเตอร์และมีโปรแกรมดูน้ำหนักเบาซึ่งรองรับการอัปเดตแบบสด

Fast Lightweight Viewer พร้อม Live Update

สำหรับPDFมีตัวเลือกที่ดีมากมาย

  • ใน Windows ฉันใช้SumatraPDFซึ่งฟรีเร็วและเบา (ใช้ RAM 1.8MB สำหรับกรณีของฉันเท่านั้น)

  • บน Linux มีหลายตัวเลือกเช่นEvince (GNOME) และOcular (KDE)

โค้ดตัวอย่างและผลลัพธ์

โค้ดตัวอย่างสำหรับการส่งออกพล็อตไปยังไฟล์

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

หลังจากเรียกใช้ครั้งแรกให้เปิดไฟล์เอาต์พุตในหนึ่งในโปรแกรมดูที่กล่าวถึงข้างต้นและเพลิดเพลิน

นี่คือภาพหน้าจอของ VSCode ควบคู่ไปกับ SumatraPDF นอกจากนี้กระบวนการนี้ยังเร็วพอที่จะรับอัตราการอัปเดตกึ่งถ่ายทอดสด (ฉันสามารถรับใกล้ 10Hz ในการตั้งค่าของฉันเพียงแค่ใช้time.sleep()ระหว่างช่วงเวลา) pyPlot ไม่ปิดกั้น


2

คำตอบของ Iggyเป็นวิธีที่ง่ายที่สุดสำหรับฉันที่จะทำตาม แต่ฉันได้รับข้อผิดพลาดต่อไปนี้เมื่อทำsubplotคำสั่งต่อมาที่ไม่มีในตอนที่ฉันกำลังทำshow:

MatplotlibDeprecationWarning: การเพิ่มแกนโดยใช้อาร์กิวเมนต์เดียวกันกับแกนก่อนหน้านี้จะใช้อินสแตนซ์ก่อนหน้านี้ซ้ำ ในเวอร์ชันอนาคตอินสแตนซ์ใหม่จะถูกสร้างและส่งคืนเสมอ ในขณะเดียวกันสามารถระงับคำเตือนนี้และตรวจสอบพฤติกรรมในอนาคตได้โดยส่งป้ายกำกับที่ไม่ซ้ำกันไปยังแต่ละแกน

เพื่อหลีกเลี่ยงข้อผิดพลาดนี้จะช่วยปิด (หรือล้าง ) พล็อตหลังจากที่ผู้ใช้เข้ามา

นี่คือรหัสที่ใช้ได้กับฉัน:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()

1

แพ็คเกจ Python ที่ดึงออกมาช่วยให้อัปเดตพล็อตแบบเรียลไทม์โดยไม่ปิดกั้น
นอกจากนี้ยังทำงานร่วมกับเว็บแคมและ OpenCV เพื่อวางแผนการวัดสำหรับแต่ละเฟรม
ดูโพสต์ต้นฉบับ


1

ฉันคิดว่าplt.pause(0.001)คำสั่งเป็นสิ่งเดียวที่จำเป็นและไม่มีอะไรอื่น

plt.show () และ plt.draw () ไม่จำเป็นและ / หรือปิดกั้นไม่ทางใดก็ทางหนึ่ง นี่คือรหัสที่วาดและอัปเดตรูปและดำเนินการต่อไป โดยพื้นฐานแล้ว plt.pause (0.001) ดูเหมือนจะใกล้เคียงที่สุดกับการดึงของ matlab

น่าเสียดายที่พล็อตเหล่านั้นจะไม่โต้ตอบ (มันค้าง) ยกเว้นคุณใส่คำสั่ง input () แต่โค้ดจะหยุดลง

เอกสารของคำสั่งplt.pause (interval)ระบุ:

หากมีตัวเลขที่ใช้งานอยู่จะมีการอัปเดตและแสดงก่อนหยุดชั่วคราว ...... สามารถใช้สำหรับภาพเคลื่อนไหวหยาบ

และนี่คือสิ่งที่เราต้องการจริงๆ ลองใช้รหัสนี้:

import numpy as np
from matplotlib import pyplot as plt

x = np.arange(0, 51)
for pow in range(10, 50):
    y = np.power(x, pow/10)

    plt.cla()                      # erase previous lines
    plt.axis([-50, 50, 0, 10000])
    plt.plot(x, y)

    # use plt.pause() instead of the blocking plt.show() and plt.draw()
    # this DOES the actual plotting (plus a pause)

    plt.pause(0.1)  # pick 0.001 or smaller if you don't want to wait
    
    # you can put your input() statement here or anything else
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.