HOWTO · Matplotlib
Matplotlib을 사용하여 실시간으로 데이터를 그리는 방법
FuncAnimation(), 수동 캔버스 다시 그리기, 또는 업데이트되는 산점도 아티스트를 사용하여 Matplotlib 창에 변화하는 데이터를 그립니다.
이 페이지의 내용
Matplotlib 창에서 바뀌는 데이터를 표시하려면 그래프 아티스트가 보유한 데이터를 갱신하고 GUI를 지원하는 백엔드가 화면을 새로 그리도록 하면 됩니다. 프레임마다 갱신할 때는 FuncAnimation()을, 반복문을 직접 제어할 때는 canvas.draw()와 canvas.flush_events()를, 갱신할 때마다 점을 추가할 때는 산점도 아티스트를 선택하세요. 이 예제는 값을 로컬에서 생성하므로, 자신의 데이터 소스에 맞게 갱신 로직을 바꾸면 됩니다. 센서나 네트워크 데이터 피드에 연결하는 예제는 아닙니다.
FuncAnimation()으로 그래프 애니메이션 만들기
matplotlib.animation.FuncAnimation은 각 프레임마다 갱신 함수를 호출하고 다시 그리기를 예약합니다. 콜백은 현재 프레임 값을 받아 기존 선을 갱신하고, 변경된 아티스트를 반환합니다. frames=10은 0부터 9까지의 값을 제공하고, interval=50은 프레임 사이에 50밀리초의 지연을 요청합니다. 실제 간격은 GUI 이벤트 루프에 따라 달라집니다.
창이 열려 있는 동안 애니메이션 객체에 대한 참조를 유지하세요. 예제는 애니메이션을 대화형 창에 표시합니다. 파일로 저장하려면 plt.show() 전에 ani.save("animation.gif", writer="pillow", fps=10)을 호출하고 Pillow와 같은 호환 애니메이션 작성기를 설치하세요. 저장은 별도의 렌더링 단계이며 실시간 데이터 피드를 만들지는 않습니다.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
figure, ax = plt.subplots()
line, = ax.plot([], [])
ax.set(xlim=(0, 2 * np.pi), ylim=(-1, 1), xlabel="x", ylabel="sin(x)")
def update(frame):
x = np.linspace(0, 2 * np.pi, 200)
y = np.sin(x - frame / 5)
line.set_data(x, y)
return (line,)
ani = FuncAnimation(figure, update, frames=40, interval=50, blit=True)
plt.show()
애니메이션에 별도의 초기 상태가 필요하면 init_func을 사용하고, 콜백에 추가 고정 입력을 전달하려면 fargs를 사용하세요. 창은 열리지만 갱신되지 않는다면 선택한 Matplotlib 백엔드가 GUI 이벤트 루프를 지원하는지 확인하고, 창이 표시되는 동안 ani에 대한 참조를 유지하세요.
canvas.draw()와 canvas.flush_events()로 선 갱신하기
각 갱신을 직접 제어하는 반복문에서는 선을 한 번 만들고 set_ydata()로 데이터를 바꾸세요. 그런 다음 canvas.draw()로 갱신된 그림을 렌더링하고 canvas.flush_events()로 GUI가 대기 중인 이벤트를 처리하게 합니다. 두 메서드는 Matplotlib 캔버스의 메서드이며 그림을 지우지 않습니다. plt.ion()은 대화형 모드를 켜고, 짧게 일시 정지하면 GUI가 응답할 시간을 확보할 수 있습니다.
import time
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 200)
figure, ax = plt.subplots()
line, = ax.plot(x, np.sin(x))
ax.set(xlim=(0, 2 * np.pi), ylim=(-1, 1), xlabel="x", ylabel="sin(x)")
plt.ion()
for frame in range(40):
line.set_ydata(np.sin(x - frame / 5))
figure.canvas.draw()
figure.canvas.flush_events()
time.sleep(0.05)
plt.ioff()
plt.show()
반복문에서 다른 작업도 함께 조정해야 할 때 유용한 방식입니다. 이벤트 루프가 입력과 다시 그리기 요청을 처리할 수 있도록 각 갱신 작업을 짧게 유지하세요. 대화형이 아닌 백엔드에서는 이 호출로 그림을 렌더링할 수는 있지만 응답성이 있는 창을 제공할 수 없습니다. 대화형 갱신에는 GUI 백엔드를 선택하세요.
실시간 산점도 만들기
점이 계속 들어오는 경우 산점도 아티스트를 하나 만들고 새 좌표가 도착할 때마다 오프셋을 바꾸세요. 같은 아티스트를 갱신하면 반복할 때마다 새 그래프 객체가 쌓이지 않습니다. plt.pause()는 프레임 사이에 GUI 이벤트 루프를 갱신하며, 아래의 축 범위는 생성된 점이 모두 보이도록 유지합니다.
import numpy as np
import matplotlib.pyplot as plt
figure, ax = plt.subplots()
points = ax.scatter([], [])
ax.set(xlim=(0, 4), ylim=(-1.1, 1.1), xlabel="x", ylabel="sin(x)")
x_values = []
y_values = []
for frame in range(100):
x = frame * 0.04
x_values.append(x)
y_values.append(np.sin(x))
points.set_offsets(np.column_stack((x_values, y_values)))
plt.pause(0.05)
plt.show()
가장 최근 점만 필요하다면 전체 이력을 저장하는 대신 좌표 쌍 하나만 유지하세요. 과거 점을 모두 표시하려면 데이터가 늘어날 때 축 범위를 확장하세요. 데이터가 빠르게 들어오는 경우에는 수신 빈도보다 적은 횟수로 갱신하거나 최근 구간만 유지해 그리기가 병목이 되지 않도록 하세요.