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

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

Espero que este exemplo plote 1000 pontos individualmente. O que realmente acontece é que a janela aparece com o primeiro ponto aparecendo (ok com isso), então espera que o loop termine antes de preencher o resto do gráfico.

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

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

Observe algumas das alterações:

  1. Chame plt.pause(0.05) para desenhar os novos dados e execute o loop de events da GUI (permitindo a interação com o mouse).

Se você estiver interessado em plotagens em tempo real, recomendo investigar a API de animação do matplotlib . Em particular, usar blit para evitar redesenhar o fundo em cada quadro pode lhe dar 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) 

Saída:

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

show provavelmente não é a melhor escolha para isso. O que eu faria é usar o pyplot.draw() . Você também pode querer include um pequeno intervalo de tempo (por exemplo, time.sleep(0.05) ) no loop para que você possa ver os charts acontecendo. Se eu fizer essas alterações no seu exemplo, isso funciona para mim e vejo cada ponto aparecendo um de cada vez.

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

 ################################################################### # # # PLOTTING A LIVE GRAPH # # ---------------------------- # # 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_()) '''''' 

Apenas experimente. Copie e cole este código em um novo arquivo python e execute-o. Você deve obter um gráfico bonito e suave:

insira a descrição da imagem aqui

Nenhum dos methods funcionou para mim. Mas eu encontrei este gráfico em tempo real matplotlib não está funcionando enquanto ainda em um loop

Tudo que você precisa é adicionar

 plt.pause(0.0001) 

e do que você pode ver o novo enredo.

Então o seu código deve ficar assim, e vai 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 

Eu sei que esta pergunta é antiga, mas agora há um pacote disponível chamado drawnow no GitHub como “python-drawnow”. Isso fornece uma interface semelhante à drawnow do MATLAB – você pode facilmente atualizar 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) 

O python-drawnow é um wrapper fino em torno do plt.draw mas fornece a capacidade de confirmar (ou depurar) a exibição da figura.

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

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

Depois de vasculhar várias respostas e trechos de código, isso de fato provou ser uma maneira fácil de extrair infinitamente os dados que chegam para mim.

Aqui está o meu código para um começo rápido. Ele plota o tempo atual com um número random em [0, 100) a cada 200ms infinitamente, enquanto também manipula 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 blit para um desempenho ainda melhor como na documentação do FuncAnimation .

O problema parece ser que você espera que o plt.show() mostre a janela e depois retorne. Não faz isso. O programa será interrompido nesse ponto e será retomado assim que você fechar a janela. Você deve ser capaz de testar isso: se você fechar a janela e, em seguida, outra janela deve aparecer.

Para resolver esse problema, apenas chame plt.show() uma vez após o loop. Então você começa o enredo completo. (Mas não uma ‘plotagem em tempo real’)

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

Aqui está uma versão que eu comecei a trabalhar 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 drawnow (makeFig) pode ser substituída por um makeFig (); sequência plt.draw () e ainda funciona OK.

Se você quer desenhar e não congelar seu tópico à medida que mais pontos são sorteados, você deve usar plt.pause () não time.sleep ()

estou usando o seguinte código 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) 
    Intereting Posts