Làm cách nào để tôi vẽ trong thời gian thực trong một vòng lặp while bằng matplotlib?


232

Tôi đang cố gắng vẽ một số dữ liệu từ máy ảnh trong thời gian thực bằng OpenCV. Tuy nhiên, âm mưu thời gian thực (sử dụng matplotlib) dường như không hoạt động.

Tôi đã tách vấn đề thành ví dụ đơn giản này:

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

Tôi hy vọng ví dụ này sẽ vẽ 1000 điểm riêng lẻ. Điều thực sự xảy ra là cửa sổ bật lên với điểm đầu tiên hiển thị (ok với điều đó), sau đó đợi vòng lặp kết thúc trước khi nó xuất hiện phần còn lại của biểu đồ.

Bất kỳ suy nghĩ tại sao tôi không nhìn thấy các điểm dân cư tại một thời điểm?

Câu trả lời:


311

Đây là phiên bản làm việc của mã được đề cập (yêu cầu ít nhất là phiên bản Matplotlib 1.1.0 từ 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()

Lưu ý một số thay đổi:

  1. Gọi plt.pause(0.05)cả hai để vẽ dữ liệu mới và nó chạy vòng lặp sự kiện của GUI (cho phép tương tác chuột).

3
Điều này làm việc cho tôi trong Python2. Trong Python3 thì không. Nó sẽ tạm dừng vòng lặp sau khi kết xuất cửa sổ cốt truyện. Nhưng sau khi chuyển phương thức plt.show () sang sau vòng lặp ... nó đã giải quyết nó cho Python3, đối với tôi.
liên tục

1
Thật lạ, làm việc ổn với tôi trong Python 3 (ver 3.4.0) Matplotlib (ver 1.3.1) Numpy (ver 1.8.1) Ubuntu Linux 3.13.0 64-bit
Velimir Mlaker

37
thay vì plt.show () và plt.draw () chỉ cần thay thế plt.draw () bằng plt.pause (0.1)
denfromufa

4
Không hoạt động trên Win64 / Anaconda matplotlib .__ phiên bản__ 1.5.0. Một cửa sổ hình ban đầu đã mở, nhưng không hiển thị gì cả, nó vẫn ở trạng thái bị chặn cho đến khi tôi đóng nó
isti_spl

5
Câu trả lời này đòi hỏi kiến ​​thức tiên phong về dữ liệu x / y ... không cần thiết: Tôi thích 1. không gọi plt.axis()mà thay vào đó hãy tạo hai danh sách x và y và gọi plt.plot(x,y)2. trong vòng lặp của bạn, nối các giá trị dữ liệu mới vào hai danh sách 3. cuộc gọiplt.gca().lines[0].set_xdata(x); plt.gca().lines[0].set_ydata(y); plt.gca().relim(); plt.gca().autoscale_view(); plt.pause(0.05);
Trevor Boyd Smith

76

Nếu bạn quan tâm đến âm mưu thời gian thực, tôi khuyên bạn nên xem xét API hoạt hình của matplotlib . Cụ thể, sử dụng blitđể tránh vẽ lại nền trên mỗi khung hình có thể giúp bạn tăng tốc đáng kể (~ 10 x):

#!/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)

Đầu ra:

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

1
@bejota Phiên bản gốc được thiết kế để hoạt động trong phiên matplotlib tương tác. Để làm cho nó hoạt động như một tập lệnh độc lập, cần phải 1) chọn rõ ràng một phụ trợ cho matplotlib và 2) để buộc hình được hiển thị và vẽ trước khi vào vòng lặp hoạt hình bằng cách sử dụng plt.show()plt.draw(). Tôi đã thêm những thay đổi này vào mã ở trên.
ali_m 2/2/2015

2
Là ý định / động lực của blit()dường như rất nhiều để "cải thiện âm mưu thời gian thực"? Nếu bạn có một nhà phát triển / blog matplotlib thảo luận về lý do tại sao / mục đích / mục đích / động lực sẽ là tuyệt vời. (có vẻ như thao tác blit mới này sẽ chuyển đổi Matplotlib từ chỉ sử dụng cho ngoại tuyến hoặc thay đổi dữ liệu rất chậm để bây giờ bạn có thể sử dụng Matplotlib với dữ liệu cập nhật rất nhanh ... gần giống như máy hiện sóng).
Trevor Boyd Smith

1
Tôi đã thấy rằng cách tiếp cận này làm cho cửa sổ cốt truyện không phản hồi: Tôi không thể tương tác với nó và làm như vậy có thể làm sập nó.
Ninjakannon

1
Đối với những người gặp sự cố "không tìm thấy gtk", nó hoạt động tốt với một back-end khác (tôi đã sử dụng 'TKAgg'). Để tìm một hỗ trợ được hỗ trợ, tôi đã sử dụng giải pháp này: stackoverflow.com/questions/3285193/ Khăn
James Nelson

1
Liên kết trong câu trả lời này dường như không hoạt động nữa. Đây có thể là một liên kết cập nhật: scipy-cookbook.readthedocs.io/items/ mẹo
awelkie

35

Tôi biết tôi hơi muộn để trả lời câu hỏi này. Tuy nhiên, tôi đã tạo một số mã trước đây để vẽ đồ thị trực tiếp mà tôi muốn chia sẻ:

Mã cho 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_())

''''''

 
Gần đây tôi đã viết lại mã cho PyQt5.
Mã cho 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_())

Hãy thử nó. Sao chép-dán mã này vào một tệp python mới và chạy nó. Bạn sẽ nhận được một biểu đồ đẹp, di chuyển trơn tru:

nhập mô tả hình ảnh ở đây


Tôi nhận thấy rằng các dataSendLoopchủ đề tiếp tục chạy trong nền khi bạn đóng cửa sổ. Vì vậy, tôi đã thêm daemon = Truetừ khóa để giải quyết vấn đề đó.
K.Mulier

1
Môi trường ảo cho việc này mất một chút công việc. Cuối cùng, conda install pyqt=4đã làm được mẹo.
Reb.Cabin

1
Cảm ơn rất nhiều cho các mã cơ bản. Nó giúp tôi xây dựng một số UI đơn giản bằng cách sửa đổi và thêm các tính năng xung quanh dựa trên mã của bạn. Nó đã tiết kiệm thời gian của tôi =]
Isaac Sim

Xin chào @IsaacSim, cảm ơn bạn rất nhiều vì tin nhắn của bạn. Tôi rất vui vì mã này rất hữu ích :-)
K.Mulier 18/12/18

Vì vậy, tôi đã lấy tập lệnh này và thêm dấu thời gian vào trục x bằng cách sửa đổi cơ chế khe tín hiệu để sử dụng loại np.ndarry và phát ra np.array của dấu thời gian và tín hiệu tương đối. Tôi đang cập nhật xlim () trên mỗi khung vẽ, điều này rất tốt để hiển thị tín hiệu với trục mới nhưng không phải là nhãn x / tick chỉ cập nhật ngắn gọn khi tôi thay đổi kích thước cửa sổ. @ K.Mulier Về cơ bản, tôi sau một trục xtick trượt giống như dữ liệu và đang tự hỏi liệu bạn có thành công nào trong việc này không?
nimig18

33

showcó lẽ không phải là sự lựa chọn tốt nhất cho việc này Những gì tôi sẽ làm là sử dụng pyplot.draw()thay thế. Bạn cũng có thể muốn bao gồm một độ trễ thời gian nhỏ (ví dụ time.sleep(0.05):) trong vòng lặp để bạn có thể thấy các ô xảy ra. Nếu tôi thực hiện những thay đổi này cho ví dụ của bạn, nó sẽ hoạt động với tôi và tôi thấy mỗi điểm xuất hiện từng điểm một.


10
Tôi có một phần mã rất giống nhau và khi tôi thử giải pháp của bạn (vẽ thay vì hiển thị và trì hoãn thời gian) python hoàn toàn không mở cửa sổ hình, chỉ đi qua vòng lặp ...
George Aprilis

31

Không có phương pháp nào làm việc cho tôi. Nhưng tôi đã tìm thấy điều này cốt truyện matplotlib thời gian thực này không hoạt động trong khi vẫn ở trong một vòng lặp

Tất cả bạn cần là thêm

plt.pause(0.0001)

và sau đó bạn có thể thấy các lô mới.

Vì vậy, mã của bạn sẽ trông như thế này và nó sẽ hoạt động

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
Điều này mở ra một cửa sổ hình / cốt truyện mới mỗi lần đối với tôi có cách nào để cập nhật con số hiện tại không? Có lẽ vì tôi đang sử dụng imshow?
Francisco Vargas

@FranciscoVargas nếu bạn đang sử dụng imshow, bạn cần sử dụng set_data, xem tại đây: stackoverflow.com/questions/17835302/
Oren

22

Các câu trả lời hàng đầu (và nhiều câu trả lời khác) đã được xây dựng plt.pause(), nhưng đó là một cách cũ để tạo hiệu ứng cho cốt truyện trong matplotlib. Nó không chỉ chậm, mà còn khiến cho việc tập trung bị thu hút sau mỗi lần cập nhật (tôi đã có một thời gian khó khăn để ngăn chặn quá trình âm mưu trăn).

TL; DR: bạn có thể muốn sử dụng matplotlib.animation( như được đề cập trong tài liệu ).

Sau khi đào xung quanh các câu trả lời và các đoạn mã khác nhau, thực tế điều này đã chứng tỏ là một cách trơn tru để vẽ dữ liệu đến vô cùng cho tôi.

Đây là mã của tôi để bắt đầu nhanh chóng. Nó vẽ thời gian hiện tại với một số ngẫu nhiên trong [0, 100) cứ sau 200ms, đồng thời xử lý tự động thay đổi kích thước của chế độ xem:

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

Bạn cũng có thể khám phá blitđể có hiệu suất cao hơn như trong tài liệu FuncAnimation .

Một ví dụ từ blittài liệu:

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

Xin chào, điều gì sẽ xảy ra nếu tất cả trong một vòng lặp. tiếng nói for i in range(1000): x,y = some func_func(). Ở đây some_func()tạo ra x,ycác cặp dữ liệu trực tuyến , mà tôi muốn vẽ khi chúng có sẵn. Có thể làm điều này với FuncAnimation. Mục tiêu của tôi là xây dựng đường cong được xác định bởi dữ liệu từng bước với mỗi lần lặp.
Alexander Cska

@Alexander Cska pyploy.show()nên chặn. Nếu bạn muốn chắp thêm dữ liệu, hãy lấy chúng và cập nhật trong updatehàm.
Hai Zhang

Tôi sợ rằng tôi không thực sự hiểu câu trả lời của bạn. Bạn có thể khuếch đại đề nghị của bạn xin vui lòng.
Alexander Cska

Ý tôi là, nếu bạn gọi pyplot.showtrong một vòng lặp, vòng lặp sẽ bị chặn bởi cuộc gọi này và sẽ không tiếp tục. Nếu bạn muốn nối dữ liệu theo đường cong từng bước, hãy đưa logic của bạn vào update, nó sẽ được gọi từng bước intervalđể nó cũng từng bước một.
Hai Zhang

Mã của Zhang hoạt động từ bảng điều khiển nhưng không phải trong jupyter. Tôi chỉ nhận được một âm mưu trống ở đó. Trong thực tế, khi tôi điền vào một mảng trong jupyter trong một vòng lặp liên tiếp và in mảng đó khi nó phát triển bằng một câu lệnh pet.plot, tôi có thể nhận được một bản in ra khỏi các mảng riêng lẻ nhưng chỉ một âm mưu. xem mã này: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin

15

Tôi biết câu hỏi này đã cũ, nhưng giờ đây có một gói có tên là drawow trên GitHub là "python-drawow". Điều này cung cấp một giao diện tương tự như bản vẽ của MATLAB - bạn có thể dễ dàng cập nhật một hình.

Một ví dụ cho trường hợp sử dụng của bạn:

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-drawow là một trình bao bọc mỏng xung quanh plt.drawnhưng cung cấp khả năng xác nhận (hoặc gỡ lỗi) sau khi hiển thị hình.


Điều này làm cho tk bị treo ở đâu đó
chwi

Nếu vậy, hãy gửi một vấn đề với nhiều ngữ cảnh hơn github.com/scottsievert/python-drawnow/issues
Scott

+1 Điều này làm việc cho tôi để vẽ dữ liệu trực tiếp trên mỗi khung hình quay video từ opencv, trong khi matplotlib bị đóng băng.
jj080808

Tôi đã thử điều này và nó có vẻ chậm hơn các phương pháp khác.
Dave C

không sử dụng, máy chủ của tôi khởi động lại, matplotlib bị đóng băng
big-vl

6

Vấn đề dường như là bạn mong đợi plt.show()hiển thị cửa sổ và sau đó quay lại. Nó không làm điều đó. Chương trình sẽ dừng tại thời điểm đó và chỉ tiếp tục lại khi bạn đóng cửa sổ. Bạn sẽ có thể kiểm tra rằng: Nếu bạn đóng cửa sổ và sau đó một cửa sổ khác sẽ bật lên.

Để giải quyết vấn đề đó, chỉ cần gọi plt.show()một lần sau vòng lặp của bạn. Sau đó, bạn có được cốt truyện hoàn chỉnh. (Nhưng không phải là 'âm mưu thời gian thực')

Bạn có thể thử đặt đối số từ khóa blocknhư thế này: plt.show(block=False)một lần ở đầu và sau đó sử dụng .draw()để cập nhật.


1
âm mưu thời gian thực là những gì tôi đang đi. Tôi sẽ chạy thử nghiệm 5 giờ về một cái gì đó và muốn xem mọi thứ đang tiến triển như thế nào.
Chris

@Chris bạn đã có thể tiến hành kiểm tra 5 giờ? Tôi cũng đang tìm kiếm một cái gì đó tương tự. Tôi đang sử dụng plyplot.pause (time_duration) để cập nhật cốt truyện. Có cách nào khác để làm như vậy?
Prakhar Mohan Srivastava

4

Đây là một phiên bản mà tôi đã làm việc trên hệ thống của mình.

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)

Dòng drawow (makeFig) có thể được thay thế bằng makeFig (); Trình tự plt.draw () và nó vẫn hoạt động tốt.


1
Làm thế nào để bạn biết bao lâu để tạm dừng? Nó xuất hiện để phụ thuộc vào cốt truyện.
CMCDragonkai

1

Nếu bạn muốn vẽ và không đóng băng chủ đề của mình vì nhiều điểm hơn được vẽ, bạn nên sử dụng plt.pause () không phải time.s ngủ ()

tôi sử dụng đoạn mã sau để vẽ một chuỗi tọa độ 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

Một lựa chọn khác là đi với hiệu ứng bokeh . IMO, nó là một sự thay thế tốt ít nhất là cho các lô thời gian thực. Đây là một phiên bản Bo mạch của mã trong câu hỏi:

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)

và để chạy nó:

pip3 install bokeh
bokeh serve --show test.py

Bo mạch cho thấy kết quả trong trình duyệt web thông qua liên lạc websocket. Nó đặc biệt hữu ích khi dữ liệu được tạo bởi các quá trình máy chủ không đầu từ xa.

âm mưu mẫu


0

Một ví dụ sử dụng để vẽ sơ đồ sử dụng CPU trong thời gian thực.

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

Nó thực sự bắt đầu chậm lại sau khoảng 2 phút. Lý do có thể là gì? Có lẽ các điểm trước đó, nằm ngoài quan điểm hiện tại, nên được loại bỏ.
pfabri

Điều này có vẻ rất tốt, nhưng có một số vấn đề với nó: 1. không thể thoát 2. chỉ sau vài phút, chương trình tiêu thụ gần 100 Mb RAM và bắt đầu chậm lại đáng kể.
pfabri
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.