HOWTO · Matplotlib

如何使用 Matplotlib 实时绘制数据

使用 FuncAnimation()、手动重绘画布或不断更新的散点图 artist,在 Matplotlib 窗口中绘制变化的数据。

本页内容

要在 Matplotlib 窗口中绘制不断变化的数据,请更新图中 artist 保存的数据,并让支持 GUI 的后端刷新显示。数据按帧到达时可使用 FuncAnimation();需要在循环中直接控制更新时,可使用 canvas.draw() 和 canvas.flush_events();每次更新都添加一个点时,可使用散点 artist。这些示例在本地生成数据;改编时,可将更新逻辑替换为自己的数据来源。它们不会连接传感器或网络数据流。

使用 FuncAnimation() 为图形添加动画

matplotlib.animation.FuncAnimation 会针对每一帧调用更新函数,并安排重绘。回调函数会收到当前帧的值,更新现有曲线,并返回发生变化的 artist。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()

plot in real-time using FuncAnimation function

如果动画需要单独的初始状态,请使用 init_func;如果要向回调函数传递额外的固定参数,请使用 fargs。如果窗口打开后动画没有更新,请检查所选 Matplotlib 后端是否支持 GUI 事件循环,并在窗口显示期间保留 ani 的引用。

使用 canvas.draw() 和 canvas.flush_events() 更新曲线

如果要由循环控制每次更新,可先创建一条曲线,再使用 set_ydata() 修改其数据。随后,canvas.draw() 会绘制更新后的图形,而 canvas.flush_events() 会让 GUI 处理待处理的事件。这些是 Matplotlib canvas 的方法,不会清除图形。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()

Real time plot in Matplotlib using canvas_draw

如果循环还需要协调其他工作,这种方式会很有用。每次更新都应尽量简短,以便事件循环处理输入和重绘请求。在非交互式后端中,这些调用可以绘制图形,但无法提供响应及时的窗口;若要进行交互式更新,请选择支持 GUI 的后端。

创建实时散点图

对于连续到来的点,可以先创建一个散点 artist,再随着新坐标到达替换它的偏移量。重复使用同一个 artist,可以避免每次循环都累积一个新的绘图对象。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()

plot scatter in real time

如果只关心最新的点,只需保留一组坐标,无需保存完整历史。如果要让所有历史点持续可见,请随着数据流增长扩大坐标轴范围。对于高频数据,可以降低更新频率,或只保留最近一段数据,以免绘图成为性能瓶颈。