Як я будую графік у режимі реального часу через цикл за допомогою 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()

Я б очікував, що цей приклад зможе скласти 1000 балів окремо. Що насправді відбувається, це те, що у вікні з'являється перша точка, що показує (нормально з цим), а потім чекає завершення циклу, перш ніж воно заповнить решту графіка.

Будь-які думки, чому я не бачу точок, заселених один за одним?

Відповіді:


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 (ver 1.3.1) Numpy (ver 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 .__ версія__ 1.5.0. Початкове вікно фігури відкрилося, але нічого не відображало, воно залишалося в заблокованому стані, поки я його не закрив
isti_spl

5
Ця відповідь вимагає апріорного знання даних x / y ... що не потрібно: я вважаю за краще 1. не дзвоніть, plt.axis()а натомість створіть два списки x і y та call 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

Якщо вас цікавить побудова сюжету в реальному часі, я рекомендую ознайомитись з анімаційним API matplotlib . Зокрема, використання 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 Оригінальна версія була розроблена для роботи в рамках інтерактивного сеансу матплотлібу. Для того, щоб він працював як окремий сценарій, необхідно 1) явно вибрати сервер для matplotlib, і 2) змусити фігуру відображатись і малювати перед входом у цикл анімації за допомогою plt.show()і plt.draw(). Ці зміни я додав до коду вище.
ali_m

2
Чи blit()здається, що намір / мотивація, начебто, є "поліпшенням графіку в режимі реального часу"? Якщо у вас є розробник / блог matplotlib, який обговорює, чому / мета / намір / мотивація була б чудовою. (схоже, що ця нова операція "блит" перетворила б Matplotlib з використання лише в режимі офлайн або дуже повільно змінюються дані, тепер ви можете використовувати Matplotlib з дуже швидкими оновленнями даних (майже як осцилоскоп).
Тревор Бойд Сміт

1
Я виявив, що такий підхід робить вікно сюжету невідповідним: я не можу з ним взаємодіяти, і це може зламати його.
Ninjakannon

1
Для тих, хто отримує "gtk не знайдено", він відмінно працює з іншим бек-ендом (я використовував "TKAgg"). Щоб знайти підтримку, яку я підтримую, я використав це рішення: stackoverflow.com/questions/3285193/…
Джеймс Нельсон,

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ключове слово, щоб вирішити цю проблему.
К.Мюльє

1
Віртуальне середовище для цього зайняло трохи роботи. Нарешті, conda install pyqt=4зробив трюк.
Reb.Cabin

1
Дякую за основний код. Це допомогло мені створити простий інтерфейс, змінивши та додавши функції на основі вашого коду. Це врятувало мій час =]
Ісаак Сім

Привіт @IsaacSim, велике спасибі за ваше люб'язне повідомлення. Я щасливий, що цей код був корисним :-)
К.Мюльє

Тому я взяв цей скрипт і додав часові позначки до осі x, змінивши механізм слота сигналу для використання типу np.ndarry та випромінюючи np.array відносної мітки часу та сигналу. Я оновлюю xlim () на кожному малюнку кадру, що чудово відображає сигнал з новою віссю, але не x-мітки / галочки лише коротко оновлюються, коли я змінюю розмір вікна. @ K.Mulier Я в основному після ковзної осі xtick, як це дані, і мені цікаво, чи маєте ви успіх у чомусь подібному?
nimig18

33

showмабуть, не найкращий вибір для цього. Що я б робив, це використовувати pyplot.draw()замість цього. Ви також можете включити невелику часову затримку (наприклад, time.sleep(0.05)) у цикл, щоб ви могли бачити, як відбуваються сюжети. Якщо я вношу ці зміни до вашого прикладу, це працює для мене, і я бачу, як кожна точка з'являється одна за одною.


10
У мене дуже схожа частина коду, і коли я спробую ваше рішення (малювати замість шоу та затримки в часі), python взагалі не відкриває вікно фігури, просто переходить цикл ...
Джордж Априлс

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?
Франциско Варгас

@FranciscoVargas, якщо ви використовуєте imshow, вам потрібно використовувати set_data, дивіться тут: stackoverflow.com/questions/17835302/…
Oren

22

Верхні (та багато інших) відповіді були побудовані на цьому plt.pause(), але це був старий спосіб анімації сюжету в matplotlib. Це не тільки повільно, але й спричиняє фокус при кожному оновленні (мені важко було зупинити процес побудови схеми python).

TL; DR: ви можете використовувати matplotlib.animation( як зазначено в документації ).

Після перегляду різноманітних відповідей та фрагментів коду, це насправді виявилося для мене безперебійним малюнком вхідних даних.

Ось мій код для швидкого початку. Він відображає поточний час із випадковим числом у [0, 100) кожні 200 м нескінченно, одночасно обробляючи автоматичне переосмислення зображення:

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 pyploy.show()має заблокувати. Якщо ви хочете додати дані, отримайте їх та оновіть у updateфункції.
Хай Чжан

Я боюся, що я не дуже розумію вашу відповідь. Чи підкажете, будь ласка, свою пропозицію.
Олександр Цска

Я маю на увазі, якщо ви зателефонуєте pyplot.showв цикл, цей цикл буде заблокований цим викликом і не продовжиться. Якщо ви хочете покроково додати дані до кривої, введіть свою логіку update, яка буде називатися кожна, intervalтому це також крок за кроком.
Хай Чжан

Код Чжана працює з консолі, але не в джуптері. Я просто отримую там порожній сюжет. Насправді, коли я заповнюю масив у jupyter у послідовному циклі та друкую масив у міру зростання з випискою pet.plot, я можу отримати друк із масивів окремо, але лише один графік. дивіться цей код: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin

15

Я знаю, що це питання давнє, але зараз на GitHub доступний пакет під назвою drawnow як "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 - це тонка обгортка навколо, plt.drawале забезпечує можливість підтвердження (або налагодження) після відображення фігури.


Це змушує десь висіти
chwi

Якщо так, надішліть проблему з більш контекстом github.com/scottsievert/python-drawnow/isissue
Скотт

+1 Це працювало для мене для побудови графіку даних в реальному часі на кадр захоплення відео з opencv, тоді як matplotlib застиг.
jj080808

Я спробував це, і це здавалося повільніше, ніж інші методи.
Дейв C

не використовуйте, перезавантажте мій сервер, matplotlib заморожений
big-vl

6

Здається, проблема полягає в тому, що ви розраховуєте plt.show()показати вікно, а потім повернутися. Це не робить. Програма зупиниться в цьому пункті і відновиться лише після закриття вікна. Ви повинні мати можливість перевірити це: Якщо ви закриєте вікно, а потім з’явиться інше вікно.

Щоб вирішити цю проблему, просто зателефонуйте plt.show()один раз після циклу. Тоді ви отримуєте повний сюжет. (Але це не "змова в режимі реального часу")

Ви можете спробувати встановити ключове слово-аргумент blockтак: plt.show(block=False)один раз на початку, а потім використовувати .draw()для оновлення.


1
Створення графіків у реальному часі - це дійсно те, про що я йду. Я буду проводити тест на 5 годин на щось і хочу побачити, як все прогресує.
Кріс

@Chris ти зміг провести 5-годинний тест? Я також шукаю щось подібне. Я використовую plyplot.pause (time_duration) для оновлення сюжету. Чи є інший спосіб зробити це?
Прахар Мохан Срівастава

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

Приклад використання для побудови графіку використання процесора в режимі реального часу.

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 Мб оперативної пам’яті і починає різко сповільнюватися.
pfabri
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.