ฉันจะพล็อตแบบเรียลไทม์ในการวนรอบโดยใช้ matplotlib ได้อย่างไร


233

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

ฉันได้แยกปัญหาออกเป็นตัวอย่างง่ายๆนี้:

fig = plt.figure()
plt.axis([0, 1000, 0, 1])

i = 0
x = list()
y = list()

while i < 1000:
    temp_y = np.random.random()
    x.append(i)
    y.append(temp_y)
    plt.scatter(i, temp_y)
    i += 1
    plt.show()

ฉันคาดหวังว่าตัวอย่างนี้จะพล็อต 1,000 คะแนนแยกกัน สิ่งที่เกิดขึ้นจริงคือหน้าต่างปรากฏขึ้นพร้อมกับจุดแรกที่แสดง (ตกลงกับสิ่งนั้น) จากนั้นรอให้การวนซ้ำให้เสร็จก่อนที่มันจะเติมส่วนที่เหลือของกราฟ

ความคิดใดที่ว่าทำไมฉันถึงไม่เห็นคะแนนเติมทีละครั้ง?

คำตอบ:


312

นี่คือรหัสการทำงานที่เป็นปัญหา (ต้องมีรุ่นอย่างน้อย Matplotlib 1.1.0 ตั้งแต่ 2011-11-14):

import numpy as np
import matplotlib.pyplot as plt

plt.axis([0, 10, 0, 1])

for i in range(10):
    y = np.random.random()
    plt.scatter(i, y)
    plt.pause(0.05)

plt.show()

บันทึกการเปลี่ยนแปลงบางอย่าง:

  1. การเรียกใช้plt.pause(0.05)เพื่อดึงข้อมูลใหม่และเรียกใช้เหตุการณ์ลูปของ GUI (อนุญาตการโต้ตอบของเมาส์)

3
สิ่งนี้ใช้ได้กับฉันใน Python2 ใน Python3 มันทำไม่ได้ มันจะหยุดการวนซ้ำชั่วคราวหลังจากเรนเดอร์หน้าต่างพล็อต แต่หลังจากย้ายวิธีการ plt.show () ไปยังลูป ... มันแก้ปัญหาได้สำหรับ Python3 สำหรับฉัน
Continuousqa

1
แปลกทำงานได้สำหรับฉันใน Python 3 (ver 3.4.0) Matplotlib (เวอร์ชั่น 1.3.1) Numpy (เวอร์ชั่น 1.8.1) Ubuntu Linux 3.13.0 64- บิต
Velimir Mlaker

37
แทน plt.show () และ plt.draw () เพียงแค่แทนที่ plt.draw () ด้วย plt.pause (0.1)
denfromufa

4
ไม่ทำงานบน Win64 / Anaconda matplotlib .__ version__ 1.5.0 หน้าต่างรูปเริ่มต้นเปิดขึ้น แต่ไม่ได้แสดงอะไรเลยมันยังคงอยู่ในสถานะที่ถูกบล็อกจนกว่าฉันจะปิด
isti_spl

5
คำตอบนี้ต้องการความรู้เบื้องต้นเกี่ยวกับข้อมูล x / y ... ซึ่งไม่จำเป็น: ฉันชอบ 1. ไม่ต้องโทรplt.axis()แต่สร้างสองรายการ x และ y และการโทรplt.plot(x,y)2 ในวงของคุณผนวกค่าข้อมูลใหม่เพื่อ ทั้งสองรายการ 3. โทรplt.gca().lines[0].set_xdata(x); plt.gca().lines[0].set_ydata(y); plt.gca().relim(); plt.gca().autoscale_view(); plt.pause(0.05);
เทรเวอร์บอยด์สมิ ธ

76

หากคุณกำลังสนใจในการวางแผนเรียลไทม์ผมขอแนะนำให้มองเข้าไปในmatplotlib ของแอนิเมชั่ API โดยเฉพาะอย่างยิ่งการใช้blitเพื่อหลีกเลี่ยงการวาดพื้นหลังในทุกเฟรมสามารถเพิ่มความเร็วได้อย่างมาก (~ 10x):

#!/usr/bin/env python

import numpy as np
import time
import matplotlib
matplotlib.use('GTKAgg')
from matplotlib import pyplot as plt


def randomwalk(dims=(256, 256), n=20, sigma=5, alpha=0.95, seed=1):
    """ A simple random walk with memory """

    r, c = dims
    gen = np.random.RandomState(seed)
    pos = gen.rand(2, n) * ((r,), (c,))
    old_delta = gen.randn(2, n) * sigma

    while True:
        delta = (1. - alpha) * gen.randn(2, n) * sigma + alpha * old_delta
        pos += delta
        for ii in xrange(n):
            if not (0. <= pos[0, ii] < r):
                pos[0, ii] = abs(pos[0, ii] % r)
            if not (0. <= pos[1, ii] < c):
                pos[1, ii] = abs(pos[1, ii] % c)
        old_delta = delta
        yield pos


def run(niter=1000, doblit=True):
    """
    Display the simulation using matplotlib, optionally using blit for speed
    """

    fig, ax = plt.subplots(1, 1)
    ax.set_aspect('equal')
    ax.set_xlim(0, 255)
    ax.set_ylim(0, 255)
    ax.hold(True)
    rw = randomwalk()
    x, y = rw.next()

    plt.show(False)
    plt.draw()

    if doblit:
        # cache the background
        background = fig.canvas.copy_from_bbox(ax.bbox)

    points = ax.plot(x, y, 'o')[0]
    tic = time.time()

    for ii in xrange(niter):

        # update the xy data
        x, y = rw.next()
        points.set_data(x, y)

        if doblit:
            # restore background
            fig.canvas.restore_region(background)

            # redraw just the points
            ax.draw_artist(points)

            # fill in the axes rectangle
            fig.canvas.blit(ax.bbox)

        else:
            # redraw everything
            fig.canvas.draw()

    plt.close(fig)
    print "Blit = %s, average FPS: %.2f" % (
        str(doblit), niter / (time.time() - tic))

if __name__ == '__main__':
    run(doblit=False)
    run(doblit=True)

เอาท์พุท:

Blit = False, average FPS: 54.37
Blit = True, average FPS: 438.27

1
@bejota เวอร์ชันดั้งเดิมออกแบบมาเพื่อทำงานภายในเซสชัน matplotlib แบบโต้ตอบ เพื่อให้การทำงานเป็นสคริปต์แบบสแตนด์อโลนก็จำเป็นที่จะต้อง 1) อย่างชัดเจนเลือกแบ็กเอนด์สำหรับ matplotlib และ 2) เพื่อบังคับให้ร่างที่จะแสดงและวาดก่อนที่จะเข้าห่วงการเคลื่อนไหวโดยใช้และplt.show() plt.draw()ฉันได้เพิ่มการเปลี่ยนแปลงเหล่านี้ในรหัสด้านบน
ali_m

2
ความตั้งใจ / แรงจูงใจของสิ่งที่blit()ดูเหมือนจะเป็น "ปรับปรุงการวางแผนแบบเรียลไทม์" หรือไม่? หากคุณมีนักพัฒนา / บล็อกของ matplotlib พูดคุยเกี่ยวกับสาเหตุ / วัตถุประสงค์ / เจตนา / แรงจูงใจที่จะยอดเยี่ยม (ดูเหมือนว่าการดำเนินการ blit ใหม่นี้จะแปลง Matplotlib จากการใช้ข้อมูลออฟไลน์หรือการเปลี่ยนแปลงที่ช้ามากเป็นตอนนี้คุณสามารถใช้ Matplotlib กับการอัปเดตข้อมูลที่รวดเร็วมาก ... เกือบเหมือนออสซิลโลสโคป)
Trevor Boyd Smith

1
ฉันพบว่าวิธีการนี้ทำให้หน้าต่างพล็อตไม่ตอบสนอง: ฉันไม่สามารถโต้ตอบกับมันได้และการทำเช่นนั้นอาจทำให้เกิดปัญหาได้
Ninjakannon

1
สำหรับผู้ที่ได้รับปัญหา "gtk not found" มันใช้งานได้ดีกับแบ็คเอนด์อื่น (ฉันใช้ 'TKAgg') ในการค้นหาผู้สนับสนุนที่ได้รับการสนับสนุนฉันใช้วิธีแก้ปัญหานี้: stackoverflow.com/questions/3285193/…
James Nelson

1
ลิงค์ในคำตอบนี้ดูเหมือนจะไม่ทำงานอีกต่อไป นี่อาจเป็นลิงก์ที่อัปเดต: scipy-cookbook.readthedocs.io/items/ …
awelkie

35

ฉันรู้ว่าฉันมาสายเพื่อตอบคำถามนี้ อย่างไรก็ตามฉันได้ทำโค้ดสักครู่แล้วเพื่อพล็อตกราฟสดที่ฉันต้องการแชร์:

รหัสสำหรับ PyQt4:

###################################################################
#                                                                 #
#                    PLOT A LIVE GRAPH (PyQt4)                    #
#                  -----------------------------                  #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################


import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading


def setCustomSize(x, width, height):
    sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
    sizePolicy.setHorizontalStretch(0)
    sizePolicy.setVerticalStretch(0)
    sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
    x.setSizePolicy(sizePolicy)
    x.setMinimumSize(QtCore.QSize(width, height))
    x.setMaximumSize(QtCore.QSize(width, height))

''''''

class CustomMainWindow(QtGui.QMainWindow):

    def __init__(self):

        super(CustomMainWindow, self).__init__()

        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")

        # Create FRAME_A
        self.FRAME_A = QtGui.QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
        self.LAYOUT_A = QtGui.QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)

        # Place the zoom button
        self.zoomBtn = QtGui.QPushButton(text = 'zoom')
        setCustomSize(self.zoomBtn, 100, 50)
        self.zoomBtn.clicked.connect(self.zoomBtnAction)
        self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))

        # Place the matplotlib figure
        self.myFig = CustomFigCanvas()
        self.LAYOUT_A.addWidget(self.myFig, *(0,1))

        # Add the callbackfunc to ..
        myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
        myDataLoop.start()

        self.show()

    ''''''


    def zoomBtnAction(self):
        print("zoom in")
        self.myFig.zoomIn(5)

    ''''''

    def addData_callbackFunc(self, value):
        # print("Add data: " + str(value))
        self.myFig.addData(value)



''' End Class '''


class CustomFigCanvas(FigureCanvas, TimedAnimation):

    def __init__(self):

        self.addedData = []
        print(matplotlib.__version__)

        # The data
        self.xlim = 200
        self.n = np.linspace(0, self.xlim - 1, self.xlim)
        a = []
        b = []
        a.append(2.0)
        a.append(4.0)
        a.append(2.0)
        b.append(4.0)
        b.append(3.0)
        b.append(4.0)
        self.y = (self.n * 0.0) + 50

        # The window
        self.fig = Figure(figsize=(5,5), dpi=100)
        self.ax1 = self.fig.add_subplot(111)


        # self.ax1 settings
        self.ax1.set_xlabel('time')
        self.ax1.set_ylabel('raw data')
        self.line1 = Line2D([], [], color='blue')
        self.line1_tail = Line2D([], [], color='red', linewidth=2)
        self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
        self.ax1.add_line(self.line1)
        self.ax1.add_line(self.line1_tail)
        self.ax1.add_line(self.line1_head)
        self.ax1.set_xlim(0, self.xlim - 1)
        self.ax1.set_ylim(0, 100)


        FigureCanvas.__init__(self, self.fig)
        TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)

    def new_frame_seq(self):
        return iter(range(self.n.size))

    def _init_draw(self):
        lines = [self.line1, self.line1_tail, self.line1_head]
        for l in lines:
            l.set_data([], [])

    def addData(self, value):
        self.addedData.append(value)

    def zoomIn(self, value):
        bottom = self.ax1.get_ylim()[0]
        top = self.ax1.get_ylim()[1]
        bottom += value
        top -= value
        self.ax1.set_ylim(bottom,top)
        self.draw()


    def _step(self, *args):
        # Extends the _step() method for the TimedAnimation class.
        try:
            TimedAnimation._step(self, *args)
        except Exception as e:
            self.abc += 1
            print(str(self.abc))
            TimedAnimation._stop(self)
            pass

    def _draw_frame(self, framedata):
        margin = 2
        while(len(self.addedData) > 0):
            self.y = np.roll(self.y, -1)
            self.y[-1] = self.addedData[0]
            del(self.addedData[0])


        self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
        self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
        self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
        self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]

''' End Class '''

# You need to setup a signal slot mechanism, to 
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
    data_signal = QtCore.pyqtSignal(float)

''' End Class '''


def dataSendLoop(addData_callbackFunc):
    # Setup the signal-slot mechanism.
    mySrc = Communicate()
    mySrc.data_signal.connect(addData_callbackFunc)

    # Simulate some data
    n = np.linspace(0, 499, 500)
    y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
    i = 0

    while(True):
        if(i > 499):
            i = 0
        time.sleep(0.1)
        mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
        i += 1
    ###
###


if __name__== '__main__':
    app = QtGui.QApplication(sys.argv)
    QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())

''''''

 
ฉันเพิ่งเขียนรหัสสำหรับ PyQt5 ใหม่
รหัสสำหรับ PyQt5:

###################################################################
#                                                                 #
#                    PLOT A LIVE GRAPH (PyQt5)                    #
#                  -----------------------------                  #
#            EMBED A MATPLOTLIB ANIMATION INSIDE YOUR             #
#            OWN GUI!                                             #
#                                                                 #
###################################################################

import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading

class CustomMainWindow(QMainWindow):
    def __init__(self):
        super(CustomMainWindow, self).__init__()
        # Define the geometry of the main window
        self.setGeometry(300, 300, 800, 400)
        self.setWindowTitle("my first window")
        # Create FRAME_A
        self.FRAME_A = QFrame(self)
        self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QColor(210,210,235,255).name())
        self.LAYOUT_A = QGridLayout()
        self.FRAME_A.setLayout(self.LAYOUT_A)
        self.setCentralWidget(self.FRAME_A)
        # Place the zoom button
        self.zoomBtn = QPushButton(text = 'zoom')
        self.zoomBtn.setFixedSize(100, 50)
        self.zoomBtn.clicked.connect(self.zoomBtnAction)
        self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
        # Place the matplotlib figure
        self.myFig = CustomFigCanvas()
        self.LAYOUT_A.addWidget(self.myFig, *(0,1))
        # Add the callbackfunc to ..
        myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
        myDataLoop.start()
        self.show()
        return

    def zoomBtnAction(self):
        print("zoom in")
        self.myFig.zoomIn(5)
        return

    def addData_callbackFunc(self, value):
        # print("Add data: " + str(value))
        self.myFig.addData(value)
        return

''' End Class '''


class CustomFigCanvas(FigureCanvas, TimedAnimation):
    def __init__(self):
        self.addedData = []
        print(matplotlib.__version__)
        # The data
        self.xlim = 200
        self.n = np.linspace(0, self.xlim - 1, self.xlim)
        a = []
        b = []
        a.append(2.0)
        a.append(4.0)
        a.append(2.0)
        b.append(4.0)
        b.append(3.0)
        b.append(4.0)
        self.y = (self.n * 0.0) + 50
        # The window
        self.fig = Figure(figsize=(5,5), dpi=100)
        self.ax1 = self.fig.add_subplot(111)
        # self.ax1 settings
        self.ax1.set_xlabel('time')
        self.ax1.set_ylabel('raw data')
        self.line1 = Line2D([], [], color='blue')
        self.line1_tail = Line2D([], [], color='red', linewidth=2)
        self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
        self.ax1.add_line(self.line1)
        self.ax1.add_line(self.line1_tail)
        self.ax1.add_line(self.line1_head)
        self.ax1.set_xlim(0, self.xlim - 1)
        self.ax1.set_ylim(0, 100)
        FigureCanvas.__init__(self, self.fig)
        TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
        return

    def new_frame_seq(self):
        return iter(range(self.n.size))

    def _init_draw(self):
        lines = [self.line1, self.line1_tail, self.line1_head]
        for l in lines:
            l.set_data([], [])
        return

    def addData(self, value):
        self.addedData.append(value)
        return

    def zoomIn(self, value):
        bottom = self.ax1.get_ylim()[0]
        top = self.ax1.get_ylim()[1]
        bottom += value
        top -= value
        self.ax1.set_ylim(bottom,top)
        self.draw()
        return

    def _step(self, *args):
        # Extends the _step() method for the TimedAnimation class.
        try:
            TimedAnimation._step(self, *args)
        except Exception as e:
            self.abc += 1
            print(str(self.abc))
            TimedAnimation._stop(self)
            pass
        return

    def _draw_frame(self, framedata):
        margin = 2
        while(len(self.addedData) > 0):
            self.y = np.roll(self.y, -1)
            self.y[-1] = self.addedData[0]
            del(self.addedData[0])

        self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
        self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
        self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
        self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
        return

''' End Class '''


# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QObject):
    data_signal = pyqtSignal(float)

''' End Class '''



def dataSendLoop(addData_callbackFunc):
    # Setup the signal-slot mechanism.
    mySrc = Communicate()
    mySrc.data_signal.connect(addData_callbackFunc)

    # Simulate some data
    n = np.linspace(0, 499, 500)
    y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
    i = 0

    while(True):
        if(i > 499):
            i = 0
        time.sleep(0.1)
        mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
        i += 1
    ###
###

if __name__== '__main__':
    app = QApplication(sys.argv)
    QApplication.setStyle(QStyleFactory.create('Plastique'))
    myGUI = CustomMainWindow()
    sys.exit(app.exec_())

แค่ลองดู คัดลอกวางรหัสนี้ในไฟล์ python ใหม่และเรียกใช้ คุณควรได้กราฟที่สวยงามและเคลื่อนไหวอย่างราบรื่น:

ป้อนคำอธิบายรูปภาพที่นี่


ฉันสังเกตเห็นว่าdataSendLoopเธรดยังคงทำงานในพื้นหลังเมื่อคุณปิดหน้าต่าง ดังนั้นฉันจึงเพิ่มdaemon = Trueคำหลักเพื่อแก้ไขปัญหานั้น
K.Mulier

1
สภาพแวดล้อมเสมือนจริงสำหรับสิ่งนี้ใช้เวลาทำงานเล็กน้อย ในที่สุดconda install pyqt=4ก็หลอกลวงได้
Reb.Cabin

1
ขอบคุณมากสำหรับรหัสพื้นฐาน มันช่วยให้ฉันสร้าง UI ที่เรียบง่ายได้โดยการแก้ไขและเพิ่มคุณสมบัติต่างๆตามรหัสของคุณ มันช่วยประหยัดเวลาของฉัน =]
ไอแซคซิม

สวัสดี @IsaacSim ขอบคุณมากสำหรับข้อความกรุณาของคุณ ฉันมีความสุขรหัสนี้มีประโยชน์ :-)
K.Mulier

ดังนั้นฉันจึงใช้สคริปต์นี้และเพิ่มการประทับเวลาลงในแกน x โดยปรับเปลี่ยนกลไกช่องสัญญาณเพื่อใช้ประเภท np.ndarry และเปล่ง np.array ของการประทับเวลาและสัญญาณที่สัมพันธ์กัน ฉันกำลังอัปเดต xlim () ในการวาดเฟรมแต่ละครั้งซึ่งทำได้ดีสำหรับการแสดงสัญญาณด้วยแกนใหม่ แต่ไม่ใช่ x-label / ticks จะอัปเดตสั้น ๆ เมื่อฉันเปลี่ยนขนาดหน้าต่างเท่านั้น @ K.Mulier ฉันโดยทั่วไปหลังจากแกน xtick ที่เลื่อนเหมือนข้อมูลและสงสัยว่าคุณประสบความสำเร็จในเรื่องนี้หรือไม่?
nimig18

33

showอาจไม่ใช่ตัวเลือกที่ดีที่สุดสำหรับสิ่งนี้ สิ่งที่ฉันจะทำคือใช้pyplot.draw()แทน คุณอาจต้องการรวมการหน่วงเวลาเล็กน้อย (เช่นtime.sleep(0.05)) ในลูปเพื่อให้คุณเห็นการแปลงเกิดขึ้น ถ้าฉันทำการเปลี่ยนแปลงเหล่านี้กับตัวอย่างของคุณมันใช้งานได้สำหรับฉันและฉันเห็นแต่ละจุดปรากฏขึ้นทีละรายการ


10
ฉันมีส่วนคล้ายกันมากของรหัสและเมื่อผมลองแก้ปัญหาของคุณ (วาดแทนของการแสดงและการหน่วงเวลา) หลามไม่ได้เปิดหน้าต่างรูปที่ทุกคนเพียงแค่ไป throught ห่วง ...
จอร์จ Aprilis

31

ไม่มีวิธีใดที่เหมาะกับฉัน แต่ฉันพบ พล็อต matplotlib แบบเรียลไทม์นี้ไม่ทำงานในขณะที่ยังอยู่ในลูป

ทั้งหมดที่คุณต้องการคือการเพิ่ม

plt.pause(0.0001)

แล้วคุณจะเห็นแปลงใหม่

ดังนั้นรหัสของคุณควรมีลักษณะเช่นนี้และจะใช้งานได้

import matplotlib.pyplot as plt
import numpy as np
plt.ion() ## Note this correction
fig=plt.figure()
plt.axis([0,1000,0,1])

i=0
x=list()
y=list()

while i <1000:
    temp_y=np.random.random();
    x.append(i);
    y.append(temp_y);
    plt.scatter(i,temp_y);
    i+=1;
    plt.show()
    plt.pause(0.0001) #Note this correction

6
นี่จะเปิดหน้าต่างรูป / พล็อตใหม่ทุกครั้งสำหรับฉันมีวิธีอัปเดตรูปที่มีอยู่หรือไม่ อาจเป็นเพราะฉันกำลังใช้ imshow อยู่หรือ
Francisco Vargas

@FranciscoVargas หากคุณใช้ imshow คุณต้องใช้ set_data ดูที่นี่: stackoverflow.com/questions/17835302/…
Oren

22

คำตอบด้านบน (และอื่น ๆ อีกมากมาย) ถูกสร้างขึ้นplt.pause()แต่นั่นเป็นวิธีเก่าแก่ในการทำพล็อตเรื่องใน matplotlib มันไม่เพียงช้า แต่ยังทำให้โฟกัสถูกจับได้ในการอัพเดทแต่ละครั้ง (ฉันมีเวลายากที่จะหยุดกระบวนการหลามพล็อต)

TL; DR:คุณอาจต้องการใช้matplotlib.animation( ดังกล่าวในเอกสารประกอบ )

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

นี่คือรหัสของฉันสำหรับการเริ่มต้นอย่างรวดเร็ว มันวางแผนเวลาปัจจุบันด้วยตัวเลขสุ่มใน [0, 100) ทุก ๆ 200ms อย่างไม่มีที่สิ้นสุดในขณะที่ยังจัดการ rescaling มุมมองอัตโนมัติ:

from datetime import datetime
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from random import randrange

x_data, y_data = [], []

figure = pyplot.figure()
line, = pyplot.plot_date(x_data, y_data, '-')

def update(frame):
    x_data.append(datetime.now())
    y_data.append(randrange(0, 100))
    line.set_data(x_data, y_data)
    figure.gca().relim()
    figure.gca().autoscale_view()
    return line,

animation = FuncAnimation(figure, update, interval=200)

pyplot.show()

นอกจากนี้คุณยังสามารถสำรวจblitเพื่อประสิทธิภาพที่ดียิ่งขึ้นในขณะที่เอกสาร FuncAnimation

ตัวอย่างจากblitเอกสาร:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
plt.show()

สวัสดีจะเกิดอะไรขึ้นถ้านี่เป็นเรื่องวนรอบ for i in range(1000): x,y = some func_func()พูด ที่นี่some_func()สร้างx,yคู่ข้อมูลออนไลน์ซึ่งฉันต้องการลงจุดเมื่อพร้อมใช้งาน FuncAnimationมันเป็นไปได้ที่จะทำเช่นนี้กับ เป้าหมายของฉันคือสร้างเส้นโค้งที่กำหนดโดยข้อมูลทีละขั้นตอนกับการวนซ้ำแต่ละครั้ง
Alexander Cska

@Alexander Cska pyploy.show()ควรบล็อก หากคุณต้องการผนวกข้อมูลให้เรียกคืนและปรับปรุงในupdateฟังก์ชั่น
Hai Zhang

ฉันกลัวว่าฉันไม่เข้าใจคำตอบของคุณ คุณช่วยขยายข้อเสนอแนะของคุณได้ไหม
Alexander Cska

ฉันหมายถึงถ้าคุณโทรหาpyplot.showลูปลูปจะถูกบล็อคโดยการโทรนี้และจะไม่ดำเนินการต่อ หากคุณต้องการผนวกข้อมูลเข้ากับเส้นโค้งทีละขั้นตอนให้ใส่ตรรกะของคุณลงupdateไปซึ่งจะถูกเรียกทุก ๆ ครั้งintervalดังนั้นจึงเป็นแบบทีละขั้นตอน
Hai Zhang

รหัสของจางทำงานได้จากคอนโซล แต่ไม่ใช่ในจูปีเตอร์ ฉันเพิ่งได้พล็อตเปล่าที่นั่น ในความเป็นจริงเมื่อฉันเติมอาร์เรย์ใน jupyter ในลำดับวนและพิมพ์อาร์เรย์ตามที่เติบโตด้วยคำสั่ง pet.plot ฉันจะได้รับการพิมพ์ออกมาจากอาร์เรย์เป็นรายบุคคล แต่เพียงหนึ่งพล็อต ดูรหัสนี้: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin

15

ฉันรู้ว่าคำถามนี้เก่า แต่ตอนนี้มีแพ็คเกจที่เรียกว่าdrawnowบน GitHub เป็น "python-drawnow" นี่เป็นอินเทอร์เฟซที่คล้ายกับข้อเสียของ MATLAB - คุณสามารถอัพเดตรูปได้อย่างง่ายดาย

ตัวอย่างสำหรับกรณีการใช้งานของคุณ:

import matplotlib.pyplot as plt
from drawnow import drawnow

def make_fig():
    plt.scatter(x, y)  # I think you meant this

plt.ion()  # enable interactivity
fig = plt.figure()  # make a figure

x = list()
y = list()

for i in range(1000):
    temp_y = np.random.random()
    x.append(i)
    y.append(temp_y)  # or any arbitrary update to your figure's data
    i += 1
    drawnow(make_fig)

python-drawnow เป็น wrapper เล็ก ๆ รอบ ๆplt.drawแต่ให้ความสามารถในการยืนยัน (หรือ debug) หลังจากแสดงตัวเลข


สิ่งนี้ทำให้ TK แขวนอยู่ที่อื่น
chwi

ถ้าเป็นเช่นนั้นยื่นปัญหากับบริบทมากขึ้นgithub.com/scottsievert/python-drawnow/issues
สกอตต์

+1 สิ่งนี้ใช้ได้กับฉันสำหรับการวางแผนข้อมูลสดต่อเฟรมการจับภาพวิดีโอจาก opencv ในขณะที่ matplotlib แข็งตัว
jj080808

ฉันลองสิ่งนี้และมันก็ช้ากว่าวิธีอื่น ๆ
เดฟ C

อย่าใช้รีบูตเซิร์ฟเวอร์ของฉัน matplotlib แช่แข็ง
big-vl

6

ปัญหาน่าจะเป็นที่คุณคาดว่าplt.show()จะแสดงหน้าต่างแล้วกลับมา มันไม่ได้ทำอย่างนั้น โปรแกรมจะหยุดที่จุดนั้นและกลับมาทำงานต่อเมื่อคุณปิดหน้าต่าง คุณควรจะสามารถทดสอบได้ว่า: หากคุณปิดหน้าต่างแล้วหน้าต่างอื่นจะปรากฏขึ้น

หากต้องการแก้ไขปัญหาดังกล่าวให้โทรติดต่อplt.show()หลังจากลูปของคุณหนึ่งครั้ง จากนั้นคุณจะได้รับพล็อตที่สมบูรณ์ (แต่ไม่ใช่ 'การวางแผนตามเวลาจริง')

คุณสามารถลองตั้งค่าคำหลักอาร์กิวเมนต์blockเช่นนี้plt.show(block=False)หนึ่งครั้งที่จุดเริ่มต้นจากนั้นใช้.draw()เพื่ออัปเดต


1
การวางแผนตามเวลาจริงเป็นสิ่งที่ฉันต้องการ ฉันจะทำการทดสอบ 5 ชั่วโมงกับบางสิ่งและต้องการดูว่าสิ่งต่างๆกำลังดำเนินไปอย่างไร
Chris

@Chris คุณสามารถทำการทดสอบ 5 ชั่วโมงได้หรือไม่ ฉันกำลังมองหาบางสิ่งที่คล้ายกัน ฉันใช้ plyplot.pause (time_duration) เพื่ออัพเดตพล็อต มีวิธีอื่นที่จะทำเช่นนั้น?
Prakhar Mohan Srivastava

4

นี่คือรุ่นที่ฉันต้องทำงานกับระบบของฉัน

import matplotlib.pyplot as plt
from drawnow import drawnow
import numpy as np

def makeFig():
    plt.scatter(xList,yList) # I think you meant this

plt.ion() # enable interactivity
fig=plt.figure() # make a figure

xList=list()
yList=list()

for i in np.arange(50):
    y=np.random.random()
    xList.append(i)
    yList.append(y)
    drawnow(makeFig)
    #makeFig()      The drawnow(makeFig) command can be replaced
    #plt.draw()     with makeFig(); plt.draw()
    plt.pause(0.001)

บรรทัด drawnow (makeFig) สามารถถูกแทนที่ด้วย makeFig (); ลำดับ plt.draw () และยังใช้งานได้ดี


1
คุณรู้ได้อย่างไรว่าจะหยุดชั่วคราวนานแค่ไหน? มันดูเหมือนจะขึ้นอยู่กับพล็อตของตัวเอง
CMCDragonkai

1

หากคุณต้องการวาดและไม่หยุดเธรดของคุณเนื่องจากมีการวาดจุดมากขึ้นคุณควรใช้ plt.pause () ไม่ใช่ time.sleep ()

ฉันใช้รหัสต่อไปนี้เพื่อพล็อตชุดของพิกัด xy

import matplotlib.pyplot as plt 
import math


pi = 3.14159

fig, ax = plt.subplots()

x = []
y = []

def PointsInCircum(r,n=20):
    circle = [(math.cos(2*pi/n*x)*r,math.sin(2*pi/n*x)*r) for x in xrange(0,n+1)]
    return circle

circle_list = PointsInCircum(3, 50)

for t in range(len(circle_list)):
    if t == 0:
        points, = ax.plot(x, y, marker='o', linestyle='--')
        ax.set_xlim(-4, 4) 
        ax.set_ylim(-4, 4) 
    else:
        x_coord, y_coord = circle_list.pop()
        x.append(x_coord)
        y.append(y_coord)
        points.set_data(x, y)
    plt.pause(0.01)

1

อีกตัวเลือกหนึ่งคือการไปกับโบเก้ IMO มันเป็นทางเลือกที่ดีอย่างน้อยสำหรับแผนการเรียลไทม์ นี่เป็นรหัสโบเก้ของคำถาม:

from bokeh.plotting import curdoc, figure
import random
import time

def update():
    global i
    temp_y = random.random()
    r.data_source.stream({'x': [i], 'y': [temp_y]})
    i += 1

i = 0
p = figure()
r = p.circle([], [])
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 100)

และสำหรับการใช้งาน:

pip3 install bokeh
bokeh serve --show test.py

bokeh แสดงผลลัพธ์ในเว็บเบราว์เซอร์ผ่านการสื่อสาร websocket มันมีประโยชน์อย่างยิ่งเมื่อข้อมูลถูกสร้างขึ้นโดยกระบวนการเซิร์ฟเวอร์หัวขาดระยะไกล

พล็อตตัวอย่างโบเก้


0

ตัวอย่าง use-case เพื่อลงจุดการใช้งาน CPU แบบเรียลไทม์

import time
import psutil
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)

i = 0
x, y = [], []

while True:
    x.append(i)
    y.append(psutil.cpu_percent())

    ax.plot(x, y, color='b')

    fig.canvas.draw()

    ax.set_xlim(left=max(0, i - 50), right=i + 50)
    fig.show()
    plt.pause(0.05)
    i += 1

มันเริ่มช้าลงหลังจากผ่านไปประมาณ 2 นาที อะไรคือเหตุผล? บางทีจุดก่อนหน้านี้ซึ่งอยู่นอกมุมมองปัจจุบันควรจะลดลง
pfabri

สิ่งนี้ดูดีจริงๆ แต่มีปัญหาสองสามข้อ: 1. เป็นไปไม่ได้ที่จะออกจาก 2. หลังจากนั้นไม่กี่นาทีโปรแกรมใช้เนื้อที่แรมเกือบ 100 Mb และเริ่มช้าลงอย่างมาก
pfabri
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.