Como plotar em tempo real em um loop while usando o matplotlib?


233

Estou tentando plotar alguns dados de uma câmera em tempo real usando o OpenCV. No entanto, a plotagem em tempo real (usando o matplotlib) não parece estar funcionando.

Eu isolei o problema neste exemplo simples:

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

Eu esperaria que este exemplo plotasse 1000 pontos individualmente. O que realmente acontece é que a janela aparece com o primeiro ponto exibido (ok com isso) e aguarda o loop terminar antes de preencher o restante do gráfico.

Alguma idéia de por que não estou vendo pontos preenchidos um de cada vez?

Respostas:


313

Aqui está a versão funcional do código em questão (requer pelo menos a versão Matplotlib 1.1.0 de 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()

Observe algumas das alterações:

  1. Ligue plt.pause(0.05)para desenhar os novos dados e ele executa o loop de eventos da GUI (permitindo a interação do mouse).

3
Isso funcionou para mim no Python2. No Python3, isso não aconteceu. Pausaria o loop após renderizar a janela de plotagem. Mas depois de mover o método plt.show () para depois do loop ... ele resolveu o Python3, para mim.
continuousqa

1
Estranho, trabalhou bem para mim em Python 3 (versão 3.4.0) Matplotlib (ver 1.3.1) Numpy (ver 1.8.1) Ubuntu Linux 3.13.0 64 bits
Velimir Mlaker

37
em vez de plt.show () e plt.draw () apenas substituir plt.draw () com plt.pause (0,1)
denfromufa

4
Não funcionou no Win64 / Anaconda matplotlib .__ versão__ 1.5.0. Uma janela de figura inicial aberto, mas não exibe nada, permaneceu em um estado bloqueado até que eu fechei
isti_spl

5
Essa resposta requer conhecimento a priori dos dados x / y ... o que não é necessário: prefiro 1. não chame, plt.axis()mas crie duas listas x e ye chame plt.plot(x,y)2. no seu loop, anexe novos valores de dados a as duas listas 3. ligarplt.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

Se você estiver interessado em plotagem em tempo real, recomendo consultar a API de animação do matplotlib . Em particular, usar blitpara evitar redesenhar o plano de fundo em cada quadro pode proporcionar ganhos substanciais de velocidade (~ 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)

Resultado:

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

1
@bejota A versão original foi projetada para funcionar em uma sessão interativa do matplotlib. Para fazê-lo funcionar como um script independente, é necessário 1) selecionar explicitamente um back-end para o matplotlib e 2) forçar a figura a ser exibida e desenhada antes de entrar no loop de animação usando plt.show()e plt.draw(). Adicionei essas alterações ao código acima.
22415 Ali_m em 02/02

2
A intenção / motivação do blit()parece ser "melhorar a plotagem em tempo real"? Se você tem um desenvolvedor / blog matplotlib discutindo o porquê / objetivo / intenção / motivação, isso seria ótimo. (parece que essa nova operação de conversão converteria o Matplotlib de apenas uso para dados offline ou alterando muito lentamente para agora você pode usar o Matplotlib com dados de atualização muito rápidos ... quase como um osciloscópio).
Trevor Boyd Smith

1
Descobri que essa abordagem deixa a janela do enredo sem resposta: não consigo interagir com ela, e isso pode causar um travamento.
Ninjakannon

1
Para quem recebe o problema "gtk não encontrado", funciona bem com um back-end diferente (usei 'TKAgg'). Para encontrar um apoiaram apoiados eu usei esta solução: stackoverflow.com/questions/3285193/...
James Nelson

1
O link nesta resposta parece não funcionar mais. Isso pode ser um link de up-to-date: scipy-cookbook.readthedocs.io/items/...
awelkie

35

Eu sei que estou um pouco atrasado para responder a esta pergunta. No entanto, criei algum código há algum tempo para traçar gráficos ao vivo, que gostaria de compartilhar:

Código para 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_())

''''''

 
Recentemente, reescrevi o código do PyQt5.
Código para 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_())

Apenas tente. Copie e cole esse código em um novo arquivo python e execute-o. Você deve obter um gráfico bonito e com movimentos suaves:

insira a descrição da imagem aqui


Notei que o dataSendLoopsegmento continuava sendo executado em segundo plano quando você fecha a janela. Então, adicionei a daemon = Truepalavra-chave para resolver esse problema.
K.Mulier

1
O ambiente virtual para isso levou um pouco de trabalho. Finalmente, conda install pyqt=4fiz o truque.
Reb.Cabin

1
Muito obrigado pelo código básico. Isso me ajudou a criar uma interface simples, modificando e adicionando recursos com base no seu código. Ele salvou o meu tempo =]
Isaac Sim

Olá @IsaacSim, muito obrigado pela sua amável mensagem. Estou feliz este código foi útil :-)
K.Mulier

Então, eu peguei esse script e adicionei carimbos de data e hora no eixo x, modificando o mecanismo do slot de sinal para usar um tipo np.ndarry e emitindo um np.array do carimbo de data e hora relativo. Estou atualizando o xlim () em cada desenho do quadro, o que é bom para exibir o sinal com o novo eixo, mas não os rótulos x / ticks são atualizados brevemente apenas quando altero o tamanho da janela. @ K.Mulier Estou basicamente atrás de um eixo deslizante xtick como os dados estão e queria saber se você teve algum sucesso em algo assim?
nimig18

33

showprovavelmente não é a melhor escolha para isso. O que eu faria é usar em seu pyplot.draw()lugar. Você também pode incluir um pequeno atraso de tempo (por exemplo, time.sleep(0.05)) no loop, para poder ver as plotagens acontecendo. Se eu fizer essas alterações no seu exemplo, funcionará para mim e vejo cada ponto aparecendo um de cada vez.


10
Eu tenho parte muito semelhante de código, e quando eu tentar a sua solução (empate em vez de show e atraso de tempo) python não abre uma janela figura em tudo, só vai throught o loop ...
George Aprilis

31

Nenhum dos métodos funcionou para mim. Mas descobri que esse gráfico em tempo real do matplotlib não está funcionando enquanto ainda está em loop

Tudo que você precisa é adicionar

plt.pause(0.0001)

e então você pode ver os novos gráficos.

Portanto, seu código deve ficar assim, e funcionará

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
Isso abre uma nova janela de figura / plot sempre que existe uma maneira de atualizar a figura existente? talvez seja porque eu estou usando o imshow?
Francisco Vargas

@FranciscoVargas Se você estiver usando imshow, precisará usar set_data, veja aqui: stackoverflow.com/questions/17835302/…
Oren

22

As respostas principais (e muitas outras) foram baseadas plt.pause(), mas essa era uma maneira antiga de animar a trama no matplotlib. Não é apenas lento, mas também faz com que o foco seja capturado a cada atualização (tive dificuldade em parar o processo de plotagem de python).

TL; DR: você pode querer usar matplotlib.animation( como mencionado na documentação ).

Depois de pesquisar várias respostas e partes do código, isso de fato provou ser uma maneira fácil de extrair infinitamente os dados recebidos para mim.

Aqui está o meu código para um início rápido. Ele plota o tempo atual com um número aleatório em [0, 100) a cada 200ms infinitamente, enquanto também gerencia o redimensionamento automático da exibição:

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

Você também pode explorar blitpara obter um desempenho ainda melhor, como na documentação do FuncAnimation .

Um exemplo da blitdocumentação:

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

Oi, o que acontecerá se tudo isso estiver em um loop. dizer for i in range(1000): x,y = some func_func(). Aqui some_func()gera x,ypares de dados on-line , que eu gostaria de plotar quando estiverem disponíveis. É possível fazer isso com FuncAnimation. Meu objetivo é construir a curva definida pelos dados passo a passo com cada iteração.
Alexander Cska

@Alexander Cska pyploy.show()deve bloquear. Se você deseja anexar dados, recupere-os e atualize na updatefunção.
Hai Zhang

Receio não entender realmente sua resposta. Você amplificaria sua sugestão, por favor.
Alexander Cska

Quero dizer, se você chamar pyplot.showem um loop, o loop será bloqueado por essa chamada e não continuará. Se você quiser acrescentar dados à curva passo a passo, insira sua lógica update, que será chamada todos os intervaldemais, para que também seja passo a passo.
Hai Zhang

O código de Zhang funciona no console, mas não no jupyter. Acabei de receber uma trama em branco lá. De fato, quando eu preencho uma matriz no jupyter em um loop seqüencial e imprimo a matriz à medida que cresce com uma instrução pet.plot, posso obter uma impressão das matrizes individualmente, mas apenas um gráfico. consulte este código: gist.github.com/bwanaaa/12252cf36b35fced0eb3c2f64a76cb8a
aquagremlin

15

Eu sei que essa pergunta é antiga, mas agora existe um pacote disponível chamado drawow no GitHub como "python-drawnow". Isso fornece uma interface semelhante à do MATLAB - você pode atualizar facilmente uma figura.

Um exemplo para o seu caso de uso:

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 é um invólucro fino, plt.drawmas fornece a capacidade de confirmar (ou depurar) após a exibição da figura.


Isso faz com que tk pendurar em algum lugar
chwi

Se assim for, apresentar um problema com mais contexto github.com/scottsievert/python-drawnow/issues
Scott

+1 Isso funcionou para mim na plotagem de dados ao vivo por quadro de captura de vídeo do opencv, enquanto o matplotlib congelou.
Jj080808

Eu tentei isso e parecia mais lento que outros métodos.
Dave C

não uso, minha reinicialização do servidor, matplotlib congelado
big-vl

6

O problema parece ser que você espera plt.show() mostrar a janela e depois retornar. Isso não faz isso. O programa será interrompido nesse ponto e só será retomado quando você fechar a janela. Você deve poder testar isso: Se você fechar a janela e outra janela aparecer.

Para resolver esse problema, basta ligar plt.show()uma vez após o loop. Então você obtém a trama completa. (Mas não uma 'plotagem em tempo real')

Você pode tentar definir o argumento da palavra-chave blockassim: plt.show(block=False)uma vez no início e depois usar .draw()para atualizar.


1
plotagem em tempo real é realmente o que estou procurando. Vou fazer um teste de 5 horas em algo e quero ver como as coisas estão progredindo.
Chris

@ Chris você foi capaz de realizar o teste de 5 horas? Também estou procurando algo semelhante. Estou usando o plyplot.pause (time_duration) para atualizar o gráfico. Existe alguma outra maneira de fazer isso?
Prakhar Mohan Srivastava

4

Aqui está uma versão que eu trabalhei no meu sistema.

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)

A linha drawow (makeFig) pode ser substituída por uma makeFig (); sequência plt.draw () e ainda funciona bem.


1
Como você sabe quanto tempo parar? Parece depender do próprio enredo.
CMCDragonkai

1

Se você deseja desenhar e não congelar seu segmento, à medida que mais pontos são desenhados, use plt.pause () e não time.sleep ()

estou usando o código a seguir para plotar uma série de coordenadas 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

Outra opção é ir com bokeh . OMI, é uma boa alternativa, pelo menos para gráficos em tempo real. Aqui está uma versão bokeh do código na pergunta:

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)

e para executá-lo:

pip3 install bokeh
bokeh serve --show test.py

bokeh mostra o resultado em um navegador da web por meio de comunicações do soquete da web. É especialmente útil quando os dados são gerados por processos remotos do servidor sem cabeça.

gráfico de amostra de bokeh


0

Um exemplo de caso de uso para plotar o uso da CPU em tempo real.

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

Realmente começa a desacelerar após cerca de 2 minutos. Qual poderia ser o motivo? Talvez pontos anteriores, que ficam fora da visão atual, devam ser descartados.
pfabri 27/04

Parece realmente bom, mas existem alguns problemas: 1. É impossível sair 2. Depois de alguns minutos, o programa consome quase 100 Mb de RAM e começa a desacelerar drasticamente.
pfabri 28/04
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.