摘要:本文通过几个例子学习了 matplotlib.animation 画动图的方法
---
对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章
我的网站:
潮汐朝夕的生活实验室
我的公众号:
潮汐朝夕
我的知乎:
潮汐朝夕
我的github:
FennelDumplings
我的leetcode:
FennelDumplings
我们在使用 matplotlib 时,常用的是 pyplot,可以画各种静态图,常见的代码模板如下
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(...)
ax.plot(...)
plt.show()
有时我们希望画一些动态图,类似于放动画片的那种效果,例如再用算法拟合曲线时候,想要每隔几步就画一下当前拟合的曲线。
在 matplotlib 中,不仅可以绘制静态图形,也可以绘制动态图形。在 Matplotlib 中绘制动态图形的方法主要有两种,第一种是用模块?animation ,另一种是用 pyplot 的 API。但是如果要存 gif 图片的话,还是要用 animation 模块。
animation 的用法
用 animation 绘制复杂动画,最核心的是 FuncAnimation 这个类。下面我们看一下 FuncAnimation 的用法。
FuncAnimation(fig, func, frames, init_func, interval, blit)
主要参数的含义如下
fig : 绘制动图的画布名称func : 回调函数,每次更新时调用,即下边程序定义的函数update,可以在这个方法中更新 line2d 对象。frames : 真个动画 frame 的取值范围,在函数运行时,其值会传递给函数 update(n) 的形参 n。init_func : 自定义开始帧,即传入刚定义的函数init,初始化函数。interval : frame之间的更新频率,以 ms 计。blit : 选择更新所有点,还是仅更新产生变化的点。
frames
以上 FuncAnimation 构造函数中的参数中,frames 参数可以取值 iterable, int, generator 或者 None。如果取整数 n,相当于给参数赋值 range(n)
frames 会在 interval 时间内迭代一次,然后将值传给 func,直至 frames 迭代完毕。
init 与 update
init 的作用是绘制下一帧画面前清空画布窗口的当前画面。update 的作用是绘制每帧画面
注意 init 和 update 的返回值的逗号不可省略,如果不带逗号,返回的是 list,带了逗号之后,返回的才是?Line2D ?对象。类似地,ax.plot ?返回值 line 后面也要加逗号。
保存 gif 或 mp4
用 FuncAnimation 生成的动图,如果要存 mp4,需要安装 ffmpeg,如果要存 gif,需要安装 imagemagick
ani.save("animation.mp4", fps=20, writer="ffmpeg")
ani.save("animation.gif", fps=50, writer="imagemagick")
例子
例子1: 每一轮 x 不变,清空并一次性更新 y
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
x = np.linspace(0, 2 * np.pi, 5000)
y = np.exp(-x) * np.cos(2 * np.pi * x)
line, = ax.plot(x, y, color="cornflowerblue", lw=3)
ax.set_ylim(-1.1, 1.1)
# 清空当前帧
def init():
line.set_ydata([np.nan] * len(x))
return line,
# 更新新一帧的数据
def update(frame):
line.set_ydata(np.exp(-x) * np.cos(2 * np.pi * x + float(frame)/100))
return line,
# 调用 FuncAnimation
ani = FuncAnimation(fig
,update
,init_func=init
,frames=200
,interval=2
,blit=True
)
ani.save("animation.gif", fps=25, writer="imagemagick")
?
例子2: 每一轮同时新增 x 和 y 的一个点
一个点一个点地画 sin 函数,每一帧新增一个点 (x, y)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
line, = plt.plot([], [], "r-", animated=True)
x = []
y = []
def init():
ax.set_xlim(-np.pi, np.pi)
ax.set_ylim(-1, 1)
return line,
def update(frame):
x.append(frame)
y.append(np.sin(frame))
line.set_data(x, y)
return line,
ani = FuncAnimation(fig
,update
,frames=np.linspace(-np.pi ,np.pi, 90)
,interval=10
,init_func=init
,blit=True
)
ani.save("animation.gif", fps=25, writer="imagemagick")
?
例子3: 同时画两条线,每轮新增两个点
代码与例子2整体差不多,但是是同时画两条线。每一轮新增两个点 (x, y1), (x, y2)
代码中的 init 函数只有一个壳,实际去掉也可以。
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
line1, = ax.plot(x, y1, color='k')
line2, = ax.plot(x, y2, color='b')
def init():
return line1, line2,
def update(num):
line1.set_data(x[:num], y1[:num])
line2.set_data(x[:num], y2[:num])
return line1, line2
ani = FuncAnimation(fig
,update
,init_func=init
,frames=len(x)
,interval=25
,blit=True
)
ani.save("3.gif", fps=25, writer="imagemagick")
?
例子4: 同时画两条线,每轮重新画两条线
每秒更新一帧,FuncAnimation 对象的 interval 设为 1000(ms),save 时 fps 设为 1。
在 update 中设置 ax 属性
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
import matplotlib.font_manager as fm
font = fm.FontProperties(fname="./simsun.ttc")
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.set_title("动态图", fontproperties=font)
ax.grid(True)
ax.set_xlabel("X轴", fontproperties=font)
ax.set_ylabel("Y轴", fontproperties=font)
line1, = ax.plot([], [], "b--", linewidth=2.0, label="sin示例")
line2, = ax.plot([], [], "g+", linewidth=2.0, label="cos示例")
ax.legend(loc="upper left", prop=font, shadow=True)
def init():
line1, = ax.plot([], [], "b--", linewidth=2.0, label="sin示例")
line2, = ax.plot([], [], "g+", linewidth=2.0, label="cos示例")
return line1, line2
def update(frame):
x = np.linspace(-np.pi + 0.1 * frame, np.pi + 0.1 * frame, 256, endpoint=True)
y_cos, y_sin = np.cos(x), np.sin(x)
ax.set_xlim(-4 + 0.1 * frame, 4 + 0.1 * frame)
ax.set_xticks(np.linspace(-4 + 0.1 * frame, 4 + 0.1 * frame, 9, endpoint=True))
ax.set_ylim(-1.0, 1.0)
ax.set_yticks(np.linspace(-1, 1, 9, endpoint=True))
line1, = ax.plot(x, y_cos, "b--", linewidth=2.0, label="sin示例")
line2, = ax.plot(x, y_sin, "g+", linewidth=2.0, label="cos示例")
return line1, line2
ani = FuncAnimation(fig
,update
,init_func=init
,frames=np.linspace(-5 ,5, 5)
,interval=1000
,blit=True
)
ani.save("4.gif", fps=1, writer="imagemagick")
|