matplotlib에서 동적으로 플롯 업데이트


114

직렬 포트에서 데이터를 수집하고 도착 시간에 대해 수집 된 데이터의 그래프를 그리는 Python으로 응용 프로그램을 만들고 있습니다. 데이터 도착 시간은 불확실합니다. 데이터가 수신되면 플롯을 업데이트하고 싶습니다. 이 작업을 수행하는 방법을 검색하고 두 가지 방법을 찾았습니다.

  1. 플롯을 지우고 모든 포인트로 플롯을 다시 그립니다.
  2. 특정 간격 후에 플롯을 변경하여 애니메이션화합니다.

프로그램이 장시간 (예 : 하루) 데이터를 실행하고 수집하므로 첫 번째 것을 선호하지 않으며 플롯을 다시 그리는 속도가 매우 느립니다. 두 번째 것은 데이터 도착 시간이 불확실하고 데이터가 수신 될 때만 플롯이 업데이트되기를 원하기 때문에 바람직하지 않습니다.

데이터를받을 때만 더 많은 포인트를 추가하여 플롯을 업데이트 할 수있는 방법이 있습니까?


답변:


138

더 많은 포인트를 추가하여 플롯을 업데이트 할 수있는 방법이 있습니까?

사용중인 버전에 따라 matplotlib에서 데이터를 애니메이션하는 방법에는 여러 가지가 있습니다. matplotlib 요리 책 예제를 보셨습니까 ? 또한 matplotlib 문서에서 보다 현대적인 애니메이션 예제 를 확인하십시오 . 마지막으로 애니메이션 APIFuncAnimation 함수를 정의합니다. 시간에 따라 함수를 애니메이션하는 함수 을 . 이 함수는 데이터를 수집하는 데 사용하는 함수일 수 있습니다.

각 방법은 기본적으로 data그려지는 객체 의 속성을 설정 하므로 화면이나 그림을 지울 필요가 없습니다. 그만큼data이전 포인트를 유지하고 당신의 라인 (또는 이미지 또는 당신이 무엇을 그리는)에 계속 추가 할 수 있도록 속성을 간단하게 확장 할 수 있습니다.

데이터 도착 시간이 확실하지 않다고한다면 다음과 같이하는 것이 가장 좋습니다.

import matplotlib.pyplot as plt
import numpy

hl, = plt.plot([], [])

def update_line(hl, new_data):
    hl.set_xdata(numpy.append(hl.get_xdata(), new_data))
    hl.set_ydata(numpy.append(hl.get_ydata(), new_data))
    plt.draw()

그런 다음 직렬 포트에서 데이터를 수신하면 update_line.


드디어! 나는이 +1에 대한 답을 찾고 있었다. :) 어떻게 우리가 플롯을 자동으로 재조정하도록 만들까. ax.set_autoscale_on (True)가 작동하지 않는 것 같습니다.
Edward Newell 2013 년

13
답을 찾았습니다. 데이터를 업데이트 한 후 plt.draw ()를 호출하기 전에 ax.relim ()을 호출 한 다음 ax.autoscale_view ()를 호출합니다.
Edward Newell

하기 matplotlib 요리 책 (에 대한 링크 scipy.org/Cookbook/Matplotlib/Animations은 ) 깨진 것 같다 (I 얻을 "금지"오류)
데이비드 도리아

21
show ()에 대한 호출이 없기 때문에 플롯이 화면에 나타나지 않습니다. show ()를 호출하면 업데이트를 차단하고 수행하지 않습니다. 내가 뭔가를 놓치고 있습니까? gist.github.com/daviddoria/027b5c158b6f200527a4
데이비드 도리아

2
유사하지만 서로 다른 링크 자체에 포함 된 실행할 수있는 코드 답은 (이 대답은 바로 일반적인 생각을 가지고 있지만 예제 코드를 실행할 수 없습니다)
트레버 보이드 스미스에게

44

FuncAnimation없이이 작업을 수행하려면 (예 : 플롯이 생성되는 동안 코드의 다른 부분을 실행하거나 동시에 여러 플롯을 업데이트하려는 경우) draw단독으로 호출 해도 플롯이 생성되지 않습니다 (적어도 qt 백엔드).

다음은 나를 위해 작동합니다.

import matplotlib.pyplot as plt
plt.ion()
class DynamicUpdate():
    #Suppose we know the x range
    min_x = 0
    max_x = 10

    def on_launch(self):
        #Set up plot
        self.figure, self.ax = plt.subplots()
        self.lines, = self.ax.plot([],[], 'o')
        #Autoscale on unknown axis and known lims on the other
        self.ax.set_autoscaley_on(True)
        self.ax.set_xlim(self.min_x, self.max_x)
        #Other stuff
        self.ax.grid()
        ...

    def on_running(self, xdata, ydata):
        #Update data (with the new _and_ the old points)
        self.lines.set_xdata(xdata)
        self.lines.set_ydata(ydata)
        #Need both of these in order to rescale
        self.ax.relim()
        self.ax.autoscale_view()
        #We need to draw *and* flush
        self.figure.canvas.draw()
        self.figure.canvas.flush_events()

    #Example
    def __call__(self):
        import numpy as np
        import time
        self.on_launch()
        xdata = []
        ydata = []
        for x in np.arange(0,10,0.5):
            xdata.append(x)
            ydata.append(np.exp(-x**2)+10*np.exp(-(x-7)**2))
            self.on_running(xdata, ydata)
            time.sleep(1)
        return xdata, ydata

d = DynamicUpdate()
d()

예! 마지막으로 Spyder와 함께 작동하는 솔루션입니다! 내가 놓친 것은 draw () 명령 후 gcf (). canvas.flush_events ()였습니다.
np8

이 훌륭한 예제를 기반으로 반복적 인 플로팅을 허용하는 작은 Python 모듈을 작성했습니다. github.com/lorenzschmid/dynplot
lorenzli

1
아름다운 예!
vvy

명확하고, 간결하며, 다재다능하고, 유연합니다. 이것이 허용되는 대답이어야합니다.
pfabri

Jupyter Notebook 에서이를 사용하려면 %matplotlib notebookmatplotlib import 문 뒤에 magic 명령을 추가해야합니다 .
pfabri

3

다음은 특정 개수의 포인트가 표시된 후 포인트를 제거 할 수있는 방법입니다.

import matplotlib.pyplot as plt
# generate axes object
ax = plt.axes()

# set limits
plt.xlim(0,10) 
plt.ylim(0,10)

for i in range(10):        
     # add something to axes    
     ax.scatter([i], [i]) 
     ax.plot([i], [i+1], 'rx')

     # draw the plot
     plt.draw() 
     plt.pause(0.01) #is necessary for the plot to update for some reason

     # start removing points if you don't want all shown
     if i>2:
         ax.lines[0].remove()
         ax.collections[0].remove()

2

이 질문에 늦었 음을 알고 있지만 문제에 대해서는 "joystick"패키지를 살펴볼 수 있습니다. 직렬 포트에서 데이터 스트림을 플로팅하도록 설계했지만 모든 스트림에서 작동합니다. 또한 대화 형 텍스트 로깅 또는 이미지 플로팅 (그래프 플로팅 추가)을 허용합니다. 별도의 스레드에서 자체 루프를 수행 할 필요가 없습니다. 패키지가 처리하므로 원하는 업데이트 빈도 만 제공하면됩니다. 또한 플로팅하는 동안 명령을 모니터링하는 데 터미널을 사용할 수 있습니다. http://www.github.com/ceyzeriat/joystick/ 또는 https://pypi.python.org/pypi/joystick을 참조 하십시오 (설치하려면 pip 설치 조이스틱 사용)

np.random.random ()을 아래 코드의 직렬 포트에서 읽은 실제 데이터 포인트로 바꾸십시오.

import joystick as jk
import numpy as np
import time

class test(jk.Joystick):
    # initialize the infinite loop decorator
    _infinite_loop = jk.deco_infinite_loop()

    def _init(self, *args, **kwargs):
        """
        Function called at initialization, see the doc
        """
        self._t0 = time.time()  # initialize time
        self.xdata = np.array([self._t0])  # time x-axis
        self.ydata = np.array([0.0])  # fake data y-axis
        # create a graph frame
        self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))

    @_infinite_loop(wait_time=0.2)
    def _generate_data(self):  # function looped every 0.2 second to read or produce data
        """
        Loop starting with the simulation start, getting data and
    pushing it to the graph every 0.2 seconds
        """
        # concatenate data on the time x-axis
        self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
        # concatenate data on the fake data y-axis
        self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
        self.mygraph.set_xydata(t, self.ydata)

t = test()
t.start()
t.stop()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.