Jak rysować w czasie rzeczywistym w pętli while za pomocą matplotlib?


233

Próbuję wykreślić niektóre dane z kamery w czasie rzeczywistym za pomocą OpenCV. Wydaje się jednak, że drukowanie w czasie rzeczywistym (przy użyciu matplotlib) nie działa.

Wyizolowałem problem w tym prostym przykładzie:

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

Spodziewałbym się, że ten przykład nakreśli 1000 punktów indywidualnie. Faktycznie dzieje się to, że okno wyskakuje z pierwszym punktem pokazującym (ok z tym), a następnie czeka na zakończenie pętli, zanim zapełni resztę wykresu.

Czy są jakieś przemyślenia, dlaczego nie widzę pojedynczych punktów?

Odpowiedzi:


312

Oto działająca wersja tego kodu (wymaga co najmniej wersji Matplotlib 1.1.0 z 14.11.2011):

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

Zwróć uwagę na niektóre zmiany:

  1. Wywołanie, plt.pause(0.05)aby narysować nowe dane i uruchamia pętlę zdarzeń GUI (umożliwiając interakcję myszy).

3
To działało dla mnie w Python2. W Python3 tak nie było. Zatrzymałby pętlę po renderowaniu okna wydruku. Ale po przeniesieniu metody plt.show () do pętli po ... rozwiązałem ją dla Python3.
continuousqa

1
Dziwne, działało dobrze dla mnie w Pythonie 3 (wer. 3.4.0) Matplotlib (wer. 1.3.1) Numpy (wer. 1.8.1) Ubuntu Linux 3.13.0 64-bit
Velimir Mlaker

37
zamiast plt.show () i plt.draw () wystarczy zamienić plt.draw () na plt.pause (0.1)
denfromufa 25.09

4
Nie działał na Win64 / Anaconda matplotlib .__ wersja__ 1.5.0. Okno początkowej figury otworzyło się, ale niczego nie wyświetlało, pozostało w stanie zablokowanym, dopóki go nie zamknąłem
isti_spl

5
Ta odpowiedź wymaga a priori znajomości danych x / y ... co nie jest potrzebne: wolę 1. nie dzwoń, plt.axis()ale zamiast tego utwórz dwie listy xiy i wywołaj plt.plot(x,y)2. w swojej pętli dodaj nowe wartości danych do dwie listy 3. zadzwoń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);
Trevor Boyd Smith

76

Jeśli interesuje Cię kreślenie w czasie rzeczywistym, polecam przyjrzenie się API animacji matplotlib . W szczególności użycie w blitcelu uniknięcia przerysowywania tła na każdej klatce może zapewnić znaczne zwiększenie prędkości (~ 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)

Wynik:

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

1
@bejota Oryginalna wersja została zaprojektowana do pracy w interaktywnej sesji matplotlib. Aby działał jako samodzielny skrypt, konieczne jest 1) jawne wybranie backendu dla matplotlib oraz 2) wymuszenie wyświetlania i rysowania figury przed wejściem do pętli animacji za pomocą plt.show()i plt.draw(). Dodałem te zmiany do powyższego kodu.
ali_m

2
Czy intencją / motywacją blit()wydaje się być „poprawa kreślenia w czasie rzeczywistym”? Jeśli masz programistę / bloga matplotlib omawiającego, dlaczego / cel / zamiar / motywacja byłaby świetna. (wygląda na to, że ta nowa operacja blit przekształciłaby Matplotlib tylko z trybu offline lub bardzo wolno zmieniających się danych, teraz możesz używać Matplotlib z bardzo szybkimi aktualizacjami danych ... prawie jak oscyloskop).
Trevor Boyd Smith

1
Przekonałem się, że takie podejście powoduje, że okno fabuły nie odpowiada: nie mogę z nim wchodzić w interakcje, a to może spowodować awarię.
Ninjakannon,

1
Dla tych, którzy dostają problem „Nie znaleziono GTK”, działa dobrze z innym back-endem (użyłem „TKAgg”). Aby znaleźć obsługiwaną kopię zapasową, skorzystałem z tego rozwiązania: stackoverflow.com/questions/3285193/...
James Nelson

1
Link w tej odpowiedzi wydaje się już nie działać. To może być aktualny link: scipy-cookbook.readthedocs.io/items/…
awelkie

35

Wiem, że jestem trochę spóźniony, aby odpowiedzieć na to pytanie. Niemniej jednak jakiś czas temu napisałem trochę kodu, aby drukować wykresy na żywo, które chciałbym udostępnić:

Kod dla 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_())

''''''

 
Niedawno przepisałem kod dla PyQt5.
Kod dla 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_())

Po prostu spróbuj. Skopiuj i wklej ten kod do nowego pliku python i uruchom go. Powinieneś otrzymać piękny, płynnie przesuwający się wykres:

wprowadź opis zdjęcia tutaj


Zauważyłem, że dataSendLoopwątek działał w tle po zamknięciu okna. Więc dodałem daemon = Truesłowo kluczowe, aby rozwiązać ten problem.
K.Mulier

1
Środowisko wirtualne wymagało trochę pracy. Wreszcie conda install pyqt=4udało się.
Reb.Cabin

1
Wielkie dzięki za podstawowy kod. Pomógł mi zbudować prosty interfejs użytkownika, modyfikując i dodając funkcje w oparciu o Twój kod. Zaoszczędził mój czas =]
Izaak Sim,

Cześć @IsaacSim, bardzo dziękuję za życzliwą wiadomość. Cieszę się, że ten kod był pomocny :-)
K.Mulier

Więc wziąłem ten skrypt i dodałem znaczniki czasu do osi X, modyfikując mechanizm gniazda sygnału tak, aby używał typu np.ndarry i emitując tablicę np. Względnego znacznika czasu i sygnału. Aktualizuję xlim () na każdym losowaniu klatki, co jest dobre do wyświetlania sygnału z nową osią, ale nie x-etykiety / tiki aktualizują się tylko na krótko, kiedy zmieniam rozmiar okna. @ K.Mulier Zasadniczo jestem po ślizgającej się osi xtick, takiej jak dane, i zastanawiałem się, czy udało ci się coś takiego osiągnąć?
nimig18

33

showprawdopodobnie nie jest najlepszym wyborem do tego. Co chciałbym zrobić to użyć pyplot.draw()zamiast. Możesz także zawrzeć time.sleep(0.05)w pętli małe opóźnienie czasowe (np. ), Aby zobaczyć przebieg wydarzeń. Jeśli dokonam tych zmian w twoim przykładzie, zadziała to dla mnie i widzę, że każdy punkt pojawia się pojedynczo.


10
Mam bardzo podobną część kodu, a kiedy wypróbowuję twoje rozwiązanie (rysuj zamiast pokaż i opóźnij czas), python w ogóle nie otwiera okna z figurami, po prostu przechodzi przez pętlę ...
George Aprilis

31

Żadna z metod nie działała dla mnie. Ale znalazłem, że ten wykres matplotlib w czasie rzeczywistym nie działa, gdy wciąż jest w pętli

Wszystko czego potrzebujesz to dodać

plt.pause(0.0001)

i wtedy można zobaczyć nowe wątki.

Twój kod powinien wyglądać tak i będzie działać

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
Otwiera to nowe okno figurki / fabuły za każdym razem, czy istnieje sposób, aby zaktualizować istniejącą figurę? może dlatego, że używam imshow?
Francisco Vargas,

@FranciscoVargas, jeśli używasz imshow, musisz użyć set_data, spójrz tutaj: stackoverflow.com/questions/17835302/...
Oren,

22

plt.pause()Bazowano na najlepszych (i wielu innych) odpowiedziach , ale był to stary sposób animowania fabuły w matplotlib. Jest nie tylko powolny, ale także powoduje, że skupia się na każdej aktualizacji (miałem trudności z zatrzymaniem procesu drukowania w Pythonie).

TL; DR: możesz użyć matplotlib.animation( jak wspomniano w dokumentacji ).

Po przeszukaniu różnych odpowiedzi i fragmentów kodu okazało się to dla mnie płynnym sposobem nieskończonego rysowania przychodzących danych.

Oto mój kod na szybki start. Rysuje aktualny czas z losową liczbą w [0, 100) co 200 ms w nieskończoność, a także obsługuje automatyczne przeskalowanie widoku:

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

Możesz także eksplorować, blitaby uzyskać jeszcze lepszą wydajność, jak w dokumentacji FuncAnimation .

Przykład z blitdokumentacji:

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

Cześć, co się stanie, jeśli wszystko będzie w pętli. powiedzmy for i in range(1000): x,y = some func_func(). Tutaj some_func()generuje się x,ypary danych online , które chciałbym wykreślić, gdy będą dostępne. Czy można to zrobić za pomocą FuncAnimation. Moim celem jest budowanie krzywej zdefiniowanej przez dane krok po kroku przy każdej iteracji.
Alexander Cska,

@Alexander Cska pyploy.show()powinien zablokować. Jeśli chcesz dołączyć dane, pobierz je i zaktualizuj w updatefunkcji.
Hai Zhang

Obawiam się, że tak naprawdę nie rozumiem twojej odpowiedzi. Czy mógłbyś wzmocnić swoją sugestię?
Alexander Cska

Mam na myśli, że jeśli zadzwonisz pyplot.showw pętli, pętla zostanie zablokowana przez to połączenie i nie będzie kontynuowana. Jeśli chcesz dołączać dane do krzywej krok po kroku, włącz logikę update, która będzie wywoływana co, intervalaby była również krok po kroku.
Hai Zhang

Kod Zhanga działa z konsoli, ale nie w jupyter. Mam tam tylko pustą fabułę. W rzeczywistości, kiedy zapełniam tablicę w jupyter w pętli sekwencyjnej i drukuję tablicę, gdy rośnie wraz z instrukcją pet.plot, mogę uzyskać wydruk z tablic indywidualnie, ale tylko jeden wykres. zobacz ten kod: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin

15

Wiem, że to pytanie jest stare, ale teraz dostępny jest pakiet o nazwie drawnow na GitHub jako „python-drawnow”. Zapewnia to interfejs podobny do drawnow MATLAB - możesz łatwo zaktualizować figurę.

Przykład twojego przypadku użycia:

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 to cienkie opakowanie, plt.drawale zapewnia możliwość potwierdzenia (lub debugowania) po wyświetleniu rysunku.


To sprawia, że ​​tk gdzieś się zawiesza
chwi,

Jeśli tak, zgłoś problem z większym kontekstem zgłoś github.com/scottsievert/python-drawnow/issues
Scott

+1 To działało dla mnie do wykreślania danych na żywo na klatkę przechwytywania wideo z opencv, podczas gdy matplotlib zamarł.
jj080808

Próbowałem tego i wydawało się to wolniejsze niż inne metody.
Dave C

nie używaj, mój serwer ponownie się uruchamia, matplotlib zamrożony
big-vl

6

Problem wydaje się być taki, jakiego oczekujesz plt.show() się pokazać okno, a następnie powrócić. Nie robi tego. Program zatrzyma się w tym momencie i wznowi dopiero po zamknięciu okna. Powinieneś być w stanie to sprawdzić: Jeśli zamkniesz okno, a następnie pojawi się kolejne okno.

Aby rozwiązać ten problem, po prostu zadzwoń plt.show()po pętli. Następnie otrzymujesz pełną fabułę. (Ale nie „spisek w czasie rzeczywistym”)

Możesz spróbować ustawić argument-słowo kluczowe w blockten sposób: plt.show(block=False)raz na początku, a następnie użyj go .draw()do aktualizacji.


1
knowanie w czasie rzeczywistym jest naprawdę tym, o co mi chodzi. Będę przeprowadzać 5-godzinny test na czymś i chcę zobaczyć, jak się sprawy potoczyły.
Chris

@Chris, czy byłeś w stanie przeprowadzić 5-godzinny test? Szukam też czegoś podobnego. Korzystam z plyplot.pause (czas_czasu), aby zaktualizować wykres. Czy jest na to inny sposób?
Prakhar Mohan Srivastava

4

Oto wersja, którą mam do pracy w moim systemie.

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)

Linia drawnow (makeFig) może być zastąpiona przez makeFig (); sekwencja plt.draw () i nadal działa OK.


1
Skąd wiesz, jak długo zatrzymać? Wygląda na to, że zależy od samej fabuły.
CMCDragonkai

1

Jeśli chcesz rysować, a nie zamrozić wątek w miarę rysowania większej liczby punktów, powinieneś użyć plt.pause (), a nie time.sleep ()

używam następującego kodu, aby wykreślić serię współrzędnych 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

Inną opcją jest przejście z bokeh . IMO to dobra alternatywa przynajmniej dla wykresów w czasie rzeczywistym. Oto wersja kodu bokeh w pytaniu:

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)

i do uruchomienia:

pip3 install bokeh
bokeh serve --show test.py

bokeh pokazuje wynik w przeglądarce internetowej za pośrednictwem komunikacji z gniazdem internetowym. Jest to szczególnie przydatne, gdy dane są generowane przez zdalne procesy serwera bezgłowego.

wykres próbny bokeh


0

Przykładowy przypadek użycia do wykreślenia wykorzystania procesora w czasie rzeczywistym.

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

Naprawdę zaczyna zwalniać po około 2 minutach. Jaki może być powód? Być może wcześniejsze punkty, które nie mieszczą się w bieżącym widoku, powinny zostać porzucone.
pfabri

Wygląda to naprawdę ładnie, ale wiąże się z tym kilka problemów: 1. nie można wyjść z programu 2. już po kilku minutach program zużywa prawie 100 Mb pamięci RAM i zaczyna gwałtownie zwalniać.
pfabri
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.