Bagaimana saya memplot secara real-time dalam loop sementara menggunakan matplotlib?

233

Saya mencoba memplot beberapa data dari kamera secara real time menggunakan OpenCV. Namun, plot waktu nyata (menggunakan matplotlib) tampaknya tidak berfungsi.

Saya telah mengisolasi masalahnya ke dalam contoh sederhana ini:

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

Saya berharap contoh ini akan memplot 1000 poin secara individual. Apa yang sebenarnya terjadi adalah bahwa jendela muncul dengan titik pertama yang menunjukkan (ok dengan itu), kemudian menunggu loop selesai sebelum mengisi seluruh grafik.

Adakah pemikiran mengapa saya tidak melihat poin diisi satu per satu?

Chris
sumber

Jawaban:

313

Berikut adalah versi kode yang berfungsi (membutuhkan setidaknya versi Matplotlib 1.1.0 dari 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()

Perhatikan beberapa perubahan:

  1. Panggil plt.pause(0.05)keduanya untuk menggambar data baru dan menjalankan loop acara GUI (memungkinkan interaksi mouse).
Velimir Mlaker
sumber
3
Ini bekerja untuk saya di Python2. Dalam Python3 tidak. Ini akan menghentikan sementara loop setelah merender jendela plot. Tetapi setelah memindahkan metode plt.show () ke setelah loop ... itu diselesaikan untuk Python3, bagi saya.
continuousqa
1
Aneh, bekerja dengan baik untuk saya di 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
alih-alih plt.show () dan plt.draw () ganti saja plt.draw () dengan plt.pause (0.1)
denfromufa
4
Tidak bekerja di Win64 / Anaconda matplotlib .__ version__ 1.5.0. Jendela angka awal dibuka, tetapi tidak menampilkan apa-apa, tetap dalam keadaan diblokir sampai saya menutupnya
isti_spl
5
Jawaban ini membutuhkan pengetahuan a-priori tentang data x / y ... yang tidak diperlukan: Saya lebih suka 1. jangan panggil, plt.axis()tapi buat dua daftar x dan y dan panggil plt.plot(x,y)2. di loop Anda, tambahkan nilai data baru ke dua daftar 3. panggilanplt.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

Jika Anda tertarik untuk merencanakan waktu nyata, saya sarankan melihat ke dalam API animasi matplotlib . Secara khusus, menggunakan blituntuk menghindari menggambar ulang latar belakang pada setiap frame dapat memberi Anda keuntungan kecepatan yang substansial (~ 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)

Keluaran:

Blit = False, average FPS: 54.37
Blit = True, average FPS: 438.27
ali_m
sumber
1
@bejota Versi asli dirancang untuk bekerja dalam sesi matplotlib interaktif. Untuk membuatnya berfungsi sebagai skrip mandiri, perlu 1) secara eksplisit memilih backend untuk matplotlib, dan 2) untuk memaksa gambar yang akan ditampilkan dan digambar sebelum memasukkan loop animasi menggunakan plt.show()dan plt.draw(). Saya telah menambahkan perubahan ini ke kode di atas.
ali_m
2
Apakah niat / motivasi yang blit()kelihatannya sangat "meningkatkan perencanaan waktu nyata"? Jika Anda memiliki pengembang / blog matplotlib membahas mengapa / tujuan / niat / motivasi yang akan menjadi besar. (Sepertinya operasi blit baru ini akan mengubah Matplotlib dari hanya digunakan untuk offline atau sangat lambat mengubah data untuk sekarang Anda dapat menggunakan Matplotlib dengan data pembaruan yang sangat cepat ... hampir seperti osiloskop).
Trevor Boyd Smith
1
Saya telah menemukan bahwa pendekatan ini membuat jendela plot tidak responsif: Saya tidak dapat berinteraksi dengannya, dan hal itu dapat merusaknya.
Ninjakannon
1
Bagi mereka yang mendapatkan masalah "gtk not found", ini berfungsi dengan baik dengan back-end yang berbeda (saya menggunakan 'TKAgg'). Untuk menemukan dukungan yang didukung, saya menggunakan solusi ini: stackoverflow.com/questions/3285193/…
James Nelson
1
Tautan dalam jawaban ini sepertinya tidak berfungsi lagi. Ini mungkin tautan terbaru: scipy-cookbook.readthedocs.io/items/…
awelkie
35

Saya tahu saya agak terlambat untuk menjawab pertanyaan ini. Namun demikian, saya telah membuat beberapa kode beberapa waktu lalu untuk memplot grafik langsung, yang ingin saya bagikan:

Kode untuk 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_())

''''''

 
Saya baru-baru ini menulis ulang kode untuk PyQt5.
Kode untuk 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_())

Coba saja. Salin-tempel kode ini dalam file python baru, dan jalankan. Anda harus mendapatkan grafik yang indah dan bergerak dengan lancar:

masukkan deskripsi gambar di sini

K. Mullier
sumber
Saya perhatikan bahwa dataSendLooputas terus berjalan di latar belakang ketika Anda menutup jendela. Jadi saya menambahkan daemon = Truekata kunci untuk mengatasi masalah itu.
K.Mulier
1
Lingkungan virtual untuk ini butuh sedikit kerja. Akhirnya, conda install pyqt=4lakukan triknya.
Reb.Cabin
1
Terima kasih banyak untuk kode dasarnya. Ini membantu saya untuk membangun beberapa UI sederhana dengan memodifikasi dan menambahkan fitur di sekitar berdasarkan kode Anda. Ini menghemat waktu saya =]
Isaac Sim
Hai @IsaacSim, terima kasih banyak atas pesannya. Saya senang kode ini membantu :-)
K.Mulier
Jadi saya telah mengambil skrip ini dan menambahkan cap waktu ke sumbu x dengan memodifikasi mekanisme slot sinyal untuk menggunakan tipe np.ndarry dan memancarkan np.array dari timestamp dan sinyal relatif. Saya memperbarui xlim () pada setiap gambar bingkai yang tidak apa-apa untuk menampilkan sinyal dengan sumbu baru tetapi tidak x-label / ticks hanya memperbarui secara singkat ketika saya mengubah ukuran jendela. @ K. Mullier Saya pada dasarnya setelah geser xtick geser seperti data dan bertanya-tanya apakah Anda berhasil pada sesuatu seperti ini?
nimig18
33

showmungkin bukan pilihan terbaik untuk ini. Apa yang akan saya lakukan adalah menggunakannya pyplot.draw(). Anda juga mungkin ingin memasukkan sedikit waktu tunda (misalnya, time.sleep(0.05)) dalam loop sehingga Anda dapat melihat plot terjadi. Jika saya membuat perubahan pada contoh Anda ini berfungsi untuk saya dan saya melihat setiap titik muncul satu per satu.

BrenBarn
sumber
10
Saya memiliki bagian kode yang sangat mirip, dan ketika saya mencoba solusi Anda (menggambar bukan pertunjukan dan waktu tunda) python tidak membuka jendela gambar sama sekali, hanya melewati loop ...
George Aprilis
31

Tidak ada metode yang bekerja untuk saya. Tapi saya telah menemukan plot matplotlib Real time ini tidak berfungsi saat masih dalam satu lingkaran

Yang Anda butuhkan hanyalah menambahkan

plt.pause(0.0001)

dan kemudian Anda bisa melihat plot baru.

Jadi kode Anda akan terlihat seperti ini, dan itu akan berfungsi

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
Oren
sumber
6
Ini membuka jendela gambar / plot baru setiap kali bagi saya apakah ada cara untuk hanya memperbarui angka yang ada? mungkin karena itu saya menggunakan imshow?
Francisco Vargas
@FranciscoVargas jika Anda menggunakan imshow, Anda perlu menggunakan set_data, lihat di sini: stackoverflow.com/questions/17835302/…
Oren
22

Jawaban teratas (dan banyak lainnya) dibangun di atas plt.pause(), tetapi itu adalah cara lama menghidupkan plot di matplotlib. Ini tidak hanya lambat, tetapi juga menyebabkan fokus untuk diambil pada setiap pembaruan (saya mengalami kesulitan menghentikan proses merencanakan python).

TL; DR: Anda mungkin ingin menggunakan matplotlib.animation( sebagaimana disebutkan dalam dokumentasi ).

Setelah menggali berbagai jawaban dan potongan kode, ini sebenarnya terbukti menjadi cara yang mulus untuk menggambar data yang masuk tak terhingga bagi saya.

Ini kode saya untuk memulai dengan cepat. Ini memplot waktu saat ini dengan angka acak dalam [0, 100) setiap 200 ms tanpa batas, sambil juga menangani penskalaan otomatis tampilan:

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

Anda juga dapat menjelajahi blituntuk kinerja yang lebih baik seperti dalam dokumentasi FuncAnimation .

Contoh dari blitdokumentasi:

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()
Hai Zhang
sumber
Hai, apa yang akan terjadi jika ini semua dalam satu lingkaran. katakan for i in range(1000): x,y = some func_func(). Di sini some_func()menghasilkan x,ypasangan data online , yang ingin saya plot setelah tersedia. Apakah mungkin melakukan ini dengan FuncAnimation. Tujuan saya adalah untuk membangun kurva yang ditentukan oleh data langkah demi langkah dengan setiap iterasi.
Alexander Cska
@Alexander Cska pyploy.show()harus memblokir. Jika Anda ingin menambahkan data, ambil dan perbarui updatefungsinya.
Hai Zhang
Saya khawatir saya tidak begitu mengerti jawaban Anda. Apakah Anda akan memperkuat saran Anda?
Alexander Cska
Maksud saya, jika Anda menelepon pyplot.showdalam satu lingkaran, loop tersebut akan diblokir oleh panggilan ini dan tidak akan melanjutkan. Jika Anda ingin menambahkan data ke kurva langkah demi langkah, masukkan logika Anda update, yang akan dipanggil setiap intervalsehingga juga langkah-demi-langkah.
Hai Zhang
Kode Zhang berfungsi dari konsol tetapi tidak di jupyter. Saya hanya mendapatkan plot kosong di sana. Bahkan, ketika saya mengisi array di jupyter dalam loop berurutan dan mencetak array saat ia tumbuh dengan pernyataan pet.plot, saya bisa mendapatkan cetakan dari array secara individual tetapi hanya satu plot. lihat kode ini: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin
15

Saya tahu pertanyaan ini sudah lama, tetapi sekarang ada paket yang disebut drawow on GitHub sebagai "python-drawow". Ini memberikan antarmuka yang mirip dengan drawow MATLAB - Anda dapat dengan mudah memperbarui gambar.

Contoh untuk kasus penggunaan Anda:

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 adalah pembungkus tipis plt.drawtetapi menyediakan kemampuan untuk mengkonfirmasi (atau debug) setelah tampilan gambar.

Scott
sumber
Ini membuat tk nongkrong di suatu tempat
chwi
Jika demikian, ajukan masalah dengan lebih banyak konteks github.com/scottsievert/python-drawnow/issues
Scott
+1 Ini berfungsi untuk saya untuk memplot data langsung per frame dari pengambilan video dari opencv, sementara matplotlib membeku.
jj080808
Saya mencoba ini dan sepertinya lebih lambat daripada metode lain.
Dave C
jangan gunakan, server saya reboot, matplotlib beku
big-vl
6

Masalahnya tampaknya Anda berharap plt.show()untuk menunjukkan jendela dan kemudian kembali. Itu tidak melakukan itu. Program akan berhenti pada titik itu dan hanya melanjutkan setelah Anda menutup jendela. Anda harus dapat mengujinya: Jika Anda menutup jendela dan kemudian jendela lain akan muncul.

Untuk mengatasi masalah itu, panggil plt.show()sekali saja setelah loop Anda. Maka Anda mendapatkan plot lengkap. (Tapi bukan 'merencanakan waktu nyata')

Anda dapat mencoba mengatur argumen kata kunci blockseperti ini: plt.show(block=False)sekali di awal dan kemudian gunakan .draw()untuk memperbarui.

Michael Mauderer
sumber
1
Plot real-time adalah apa yang saya inginkan. Saya akan menjalankan tes 5 jam pada sesuatu dan ingin melihat bagaimana perkembangannya.
Chris
@ Chris apakah Anda bisa melakukan tes 5 jam? Saya juga mencari yang serupa. Saya menggunakan plyplot.pause (time_duration) untuk memperbarui plot. Apakah ada cara lain untuk melakukannya?
Prakhar Mohan Srivastava
4

Ini adalah versi yang saya dapat kerjakan di sistem saya.

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)

Garis drawow (makeFig) dapat diganti dengan makeFig (); urutan plt.draw () dan masih berfungsi OK.

slehar
sumber
1
Bagaimana Anda tahu berapa lama untuk berhenti? Tampaknya tergantung pada plot itu sendiri.
CMCDragonkai
1

Jika Anda ingin menggambar dan tidak membekukan utas Anda karena lebih banyak titik yang ditarik, Anda harus menggunakan plt.pause () bukan time.sleep ()

Saya menggunakan kode berikut untuk merencanakan serangkaian koordinat 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)
pengguna2672474
sumber
1

Pilihan lain adalah pergi dengan bokeh . IMO, ini adalah alternatif yang baik setidaknya untuk plot waktu nyata. Berikut ini adalah versi bokeh dari kode dalam pertanyaan:

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)

dan untuk menjalankannya:

pip3 install bokeh
bokeh serve --show test.py

bokeh menunjukkan hasilnya di browser web melalui komunikasi websocket. Ini sangat berguna ketika data dihasilkan oleh proses server tanpa kepala jarak jauh.

plot sampel bokeh

Hamid Fadishei
sumber
0

Contoh use-case untuk merencanakan penggunaan CPU secara real-time.

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
Nilani Algiriyage
sumber
Ini benar-benar mulai melambat setelah sekitar 2 menit. Apa alasannya? Mungkin poin sebelumnya, yang berada di luar tampilan saat ini, harus dibuang.
pfabri
Ini terlihat sangat bagus, tetapi ada beberapa masalah dengan itu: 1. tidak mungkin untuk berhenti 2. setelah hanya beberapa menit program mengkonsumsi hampir 100 Mb RAM dan mulai melambat secara dramatis.
pfabri