Как построить график в реальном времени в цикле while с помощью 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 пунктов отдельно. На самом деле происходит то, что окно всплывает с показом первой точки (хорошо с этим), затем ждет окончания цикла, прежде чем заполнить остальную часть графика.

Есть мысли, почему я не вижу населенных пунктов по одному?

Ответы:


313

Вот рабочая версия рассматриваемого кода (требуется как минимум версия 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 (версия 3.4.0) Matplotlib (версия 1.3.1) Numpy (версия 1.8.1) 64-битная версия Ubuntu Linux 3.13.0
Велимир Млакер,

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 и вызвать 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 Оригинальная версия была разработана для работы в интерактивном сеансе matplotlib. Чтобы он работал как отдельный скрипт, необходимо: 1) явно выбрать бэкэнд для matplotlib и 2) заставить рисунок отображаться и рисоваться перед входом в цикл анимации с помощью plt.show()и plt.draw(). Я добавил эти изменения в код выше.
ali_m

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

1
Я обнаружил, что этот подход делает окно графика не отвечающим: я не могу взаимодействовать с ним, и это может привести к сбою.
Ниндзяканнон

1
Для тех, у кого проблема "gtk not found", она отлично работает с другим бэкэндом (я использовал 'TKAgg'). Чтобы найти поддерживаемую
Джеймс Нельсон,

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сделали свое дело.
Реб. Кабин

1
Большое спасибо за основной код. Это помогло мне создать простой пользовательский интерфейс, изменив и добавив функции, основанные на вашем коде. Это сэкономило мое время =]
Исаак Сим

Привет @ IsaacSim, большое спасибо за доброе сообщение. Я рад, что этот код был полезен :-)
K.Mulier

Итак, я взял этот скрипт и добавил временные метки к оси x, изменив механизм слота сигнала, чтобы использовать тип np.ndarry, и выпустил массив np.ar относительно относительной метки времени и сигнала. Я обновляю xlim () при каждом отображении кадра, что хорошо для отображения сигнала с новой осью, но не для кратких обновлений / меток / тиков, только когда я меняю размер окна. @ 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/…
Орен

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. Моя цель - построить кривую, определяемую данными, шаг за шагом с каждой итерацией.
Александр Цска

@ Александр Cska pyploy.show()должен заблокировать. Если вы хотите добавить данные, восстановите их и обновите в updateфункции.
Хай Чжан

Боюсь, что я не совсем понимаю ваш ответ. Не могли бы вы усилить ваше предложение, пожалуйста.
Александр Цска,

Я имею в виду, если вы вызываете pyplot.showцикл, цикл будет заблокирован этим вызовом и не будет продолжаться. Если вы хотите добавлять данные к кривой шаг за шагом, вставьте свою логику update, которая будет вызываться каждый, intervalтак что это также шаг за шагом.
Хай Чжан

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

15

Я знаю, что этот вопрос старый, но теперь на GitHub есть пакет, называемый drawnow как «python-drawnow». Это обеспечивает интерфейс, похожий на 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/issues
Скотт

+1 Это сработало для меня для построения данных в реальном времени на кадр захвата видео из opencv, пока matplotlib завис.
jj080808

Я попробовал это, и это казалось медленнее, чем другие методы.
Дейв С

не используйте, мой сервер перезагрузился, matplotlib заморожен
big-vl

6

Кажется, проблема в том, что вы ожидаете plt.show()показать окно, а затем вернуться. Это не делает этого. Программа остановится в этой точке и возобновит работу только после закрытия окна. Вы должны быть в состоянии проверить это: если вы закроете окно, а затем появится другое окно.

Чтобы решить эту проблему, просто позвоните plt.show()один раз после цикла. Тогда вы получите полный сюжет. (Но не «в реальном времени»)

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


1
Сюжет в реальном времени - это то, к чему я стремлюсь. Я собираюсь провести 5-часовой тест на что-то и хочу посмотреть, как идут дела.
Крис

@ Крис, тебе удалось провести 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

Другой вариант - пойти с боке . ИМО, это хорошая альтернатива, по крайней мере, для графиков в реальном времени. Вот боке-версия кода в вопросе:

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

Боке показывает результат в веб-браузере через веб-сокет связи. Это особенно полезно, когда данные генерируются процессами удаленного безголового сервера.

образец боке


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 минуты. В чем может быть причина? Возможно, более ранние точки, которые выходят за пределы текущей точки зрения, следует отбросить.
Пфабри

Это выглядит очень хорошо, но есть несколько проблем: 1. невозможно выйти 2. через несколько минут программа потребляет почти 100 МБ ОЗУ и начинает резко замедляться.
Пфабри
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.