动画是展示数据随时间变化或动态过程的强大方式。Matplotlib 的 animation 模块提供了创建动画的工具,
让你能够轻松制作动态图表。本章将带你从零开始掌握动画制作,包括基础用法、保存动画以及实际案例。
Matplotlib 的动画功能主要通过 matplotlib.animation 实现。最常用的类是:
FuncAnimation:通过反复调用一个更新函数来生成动画,适合大多数场景。ArtistAnimation:使用预先绘制的一系列艺术家对象生成动画,适合已有静态图像序列。首先需要导入模块:
import matplotlib.animation as animation
使用 FuncAnimation 创建一个移动的正弦波:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# 准备数据
x = np.linspace(0, 2*np.pi, 100)
fig, ax = plt.subplots()
line, = ax.plot(x, np.sin(x))
def update(frame):
# frame 从0到99
line.set_ydata(np.sin(x + frame/10)) # 相位移动
return line,
ani = FuncAnimation(fig, update, frames=100, interval=50, blit=True)
plt.show()
frames 指定帧数,interval 为帧间隔(毫秒),blit=True 优化重绘速度。| 参数 | 说明 | 示例 |
|---|---|---|
fig | 动画所在的图形对象 | fig |
func | 每一帧调用的更新函数,接收帧号作为参数 | update |
frames | 帧数(整数)或可迭代对象 | 100 或 range(50) |
init_func | 初始化函数(可选),返回初始艺术家 | init |
fargs | 传递给更新函数的额外参数 | (param1, param2) |
interval | 帧间隔(毫秒) | 50 |
repeat | 是否循环播放 | True (默认) |
blit | 是否只重绘变化的区域(提升性能) | True |
在动画开始前,有时需要设置初始状态,可以定义 init_func:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = np.linspace(0, 2*np.pi, 100)
fig, ax = plt.subplots()
line, = ax.plot([], []) # 初始为空
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)
def init():
line.set_data([], [])
return line,
def update(frame):
y = np.sin(x + frame/10)
line.set_data(x, y)
return line,
ani = FuncAnimation(fig, update, frames=100, init_func=init, blit=True)
plt.show()
保存动画需要安装额外的依赖:
imagemagick 或 pillow(writer='pillow')ffmpeg# 保存为 GIF(需要 pillow 或 imagemagick)
ani.save('sine_wave.gif', writer='pillow', fps=20) # fps 为每秒帧数
# 保存为 MP4(需要 ffmpeg)
ani.save('sine_wave.mp4', writer='ffmpeg', fps=20)
# 也可以指定额外参数,如 dpi
ani.save('sine_wave.mp4', writer='ffmpeg', fps=20, dpi=200)
模拟粒子运动:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
np.random.seed(42)
num_points = 50
x = np.random.randn(num_points)
y = np.random.randn(num_points)
colors = np.random.rand(num_points)
sizes = np.random.rand(num_points) * 100
fig, ax = plt.subplots()
scat = ax.scatter(x, y, c=colors, s=sizes, alpha=0.6)
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
def update(frame):
# 随机移动
x_new = x + np.random.randn(num_points) * 0.1
y_new = y + np.random.randn(num_points) * 0.1
scat.set_offsets(np.c_[x_new, y_new])
return scat,
ani = FuncAnimation(fig, update, frames=200, interval=50, blit=True)
plt.show()
模拟数据随时间变化:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
categories = ['A', 'B', 'C', 'D', 'E']
initial_values = [10, 15, 7, 12, 9]
fig, ax = plt.subplots()
bars = ax.bar(categories, initial_values, color='skyblue')
ax.set_ylim(0, 30)
def update(frame):
# 随机增减
new_vals = [v + np.random.randn()*2 for v in initial_values]
for bar, val in zip(bars, new_vals):
bar.set_height(val)
return bars
ani = FuncAnimation(fig, update, frames=100, interval=200, blit=False) # bar 不支持 blit 时设为 False
plt.show()
通过鼠标点击可以控制动画的播放:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 100)
line, = ax.plot(x, np.sin(x))
def update(frame):
line.set_ydata(np.sin(x + frame/10))
return line,
ani = FuncAnimation(fig, update, frames=100, interval=50, blit=True)
def on_click(event):
if ani.running:
ani.event_source.stop()
ani.running = False
else:
ani.event_source.start()
ani.running = True
ani.running = True
fig.canvas.mpl_connect('button_press_event', on_click)
plt.show()
如果已经生成了多幅静态图,可以用 ArtistAnimation 合成动画:
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 100)
artists = []
for phase in np.linspace(0, 2*np.pi, 50):
line, = ax.plot(x, np.sin(x + phase), 'b')
artists.append([line])
ani = ArtistAnimation(fig, artists, interval=50, blit=True)
plt.show()
不断添加新数据点,保持窗口固定长度:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
max_len = 100
x_data, y_data = [], []
line, = ax.plot([], [])
def init():
ax.set_xlim(0, max_len)
ax.set_ylim(-2, 2)
return line,
def update(frame):
# 模拟新数据
new_y = np.random.randn() * 0.5
y_data.append(new_y)
x_data.append(frame)
if len(x_data) > max_len:
x_data.pop(0)
y_data.pop(0)
line.set_data(x_data, y_data)
return line,
ani = FuncAnimation(fig, update, frames=np.arange(0, 200), init_func=init,
interval=100, blit=True)
plt.show()
blit=True,并确保更新函数只返回已改变的艺术家。%matplotlib notebook 或 %matplotlib widget,或者使用 animation.HTML 内嵌显示。savefig_kwargs={'facecolor': 'white'}。from IPython.display import HTML; HTML(ani.to_html5_video()) 直接嵌入视频。
展示更复杂的物理模拟动画(代码较长,仅作演示):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.integrate import odeint
# 双摆运动方程(略)
# ... 完整代码参考官方示例
# 这里省略具体实现,仅说明可创建复杂动画
通过本章的学习,你已掌握了使用 Matplotlib 创建动画的基本和高级技巧。动画能为数据故事增添动态魅力,是数据展示的强力工具。下一章我们将探讨性能优化,帮助你在处理大规模数据时保持绘图效率。