[数据分析与可视化] 基于Python绘制简单动图

动画是一种高效的可视化工具,能够提升用户的吸引力和视觉体验,有助于以富有意义的方式呈现数据可视化。本文的主要介绍在Python中两种简单制作动图的方法。其中一种方法是使用matplotlib的Animations模块绘制动图,另一种方法是基于Pillow生成GIF动图。

python学习资料已打包好,需要的小伙伴可以戳这里【python资料】

1 Animations模块

Matplotlib的Animations模块提供了FuncAnimation和ArtistAnimation类来创建matplotlib绘图动画,FuncAnimation和ArtistAnimation都是Animation类的子类。它们的区别在于实现动画的方式和使用场景不同。FuncAnimation适用于根据时间更新图形状态的动画效果,且更加灵活和常用。而ArtistAnimation适用于将已有的静态图像序列组合成动画的效果。具体区别如下:

  • FuncAnimation:FuncAnimation是基于函数的方法来创建动画的。它使用用户提供的一个或多个函数来更新图形的状态,并按照一定的时间间隔连续地调用这些函数,从而实现动画效果。用户需要定义一个更新函数,该函数在每个时间步长上更新图形对象的属性,然后FuncAnimation会根据用户指定的帧数、时间间隔等参数来自动计算动画的帧序列。这种方法适用于需要根据时间变化来更新图形状态的动画效果。

  • ArtistAnimation:ArtistAnimation是基于静态图像的方法来创建动画的。它要求用户提供一系列的静态图像,称为艺术家对象。这些图像可以是通过Matplotlib创建的任何类型的可视化对象,例如Figure、Axes、Line2D等。用户需要将这些静态图像存储在一个列表中,然后通过ArtistAnimation来显示这些图像的序列。ArtistAnimation会按照用户指定的时间间隔逐帧地显示这些图像,从而实现动画效果。这种方法适用于已经有一系列静态图像需要组合成动画的场景。

本节将通过几个示例来介绍Animations模块的使用,所介绍的示例出自:gallery-animation。

1.1 FuncAnimation类

FuncAnimation构造函数的参数含义如下:

  • fig:要绘制动画的Figure对象。
  • func:用于更新每一帧的函数,该函数接受一个参数frame,表示当前待绘制的数据帧。
  • frames:用于产生待绘制的数据,可以是整数、生成器函数或迭代器。
  • init_func:在绘制动画之前调用的初始化函数。
  • fargs:传递给func函数的附加参数(可选)。
  • save_count:指定动画中缓存的帧数量(可选),默认为100。注意该参数用于确定最后生成动图和视频所用图像的数量。
  • interval:每一帧之间的时间间隔,以毫秒为单位,默认为200。
  • repeat:控制动画是否重复播放,默认为True。
  • repeat_delay:重复动画之间的延迟时间(以毫秒为单位),默认为0。
  • blit:指定是否使用blitting技术来进行绘制优化,默认为False。
  • cache_frame_data:指定是否缓存帧数据,默认为True。

示例-生成动态的正弦波动画

import itertools
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
# 定义生成数据的函数
def data_gen(max_range):
# 使用itertools.count()生成无限递增的计数器
for cnt in itertools.count():
# 当计数器超过最大范围时停止生成数据
if cnt > max_range:
break
print(cnt)
# 计算时间t和对应的y值,使用np.sin()计算sin函数,np.exp()计算指数函数
t = cnt / 10
yield t, np.sin(2*np.pi*t) * np.exp(-t/10.)
# 初始化函数,设置坐标轴范围和清空数据
def init():
ax.set_ylim(-1.1, 1.1)
ax.set_xlim(0, 1)
del xdata[:]
del ydata[:]
line.set_data(xdata, ydata)
return line,
# 创建图形对象以及子图对象
fig, ax = plt.subplots()
# 创建线条对象
line, = ax.plot([], [], lw=2)
# 创建文本对象用于显示 x 和 y 值
text = ax.text(0., 0., '', transform=ax.transAxes)
# 设置文本位置
text.set_position((0.7, 0.95))
# 将文本对象添加到图形中
ax.add_artist(text)
ax.grid()
xdata, ydata = [], []
# 更新函数,将新的数据添加到图形中
def run(data):
# 获取传入的数据
t, y = data
# 将时间和对应的y值添加到xdata和ydata中
xdata.append(t)
ydata.append(y)
# 获取当前坐标轴的范围
xmin, xmax = ax.get_xlim()
# 更新文本对象的值
text.set_text('x = {:.2f}, y = {:.2f}'.format(t, y))
# 如果时间t超过当前范围,更新坐标轴范围
if t >= xmax:
ax.set_xlim(xmin, 2*xmax)
# 重绘图形
ax.figure.canvas.draw()
# 更新线条的数据
line.set_data(xdata, ydata)
return line, text
# 创建动画对象
# fig:图形对象
# run:更新函数,用于更新图形中的数据
# data_gen(20):生成器函数,产生数据的最大范围为20
# interval=100:每帧动画的时间间隔为100毫秒
# init_func=init:初始化函数,用于设置图形的初始状态
# repeat=True:动画重复播放
ani = animation.FuncAnimation(fig, run, data_gen(20), interval=100, init_func=init, repeat=True)
# 显示图形
plt.show()

示例-创建动态散点图与折线图

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
# 创建一个图形窗口和坐标轴
fig, ax = plt.subplots()
# 创建时间数组
t = np.linspace(0, 3, 50)
# 自由落体加速度
g = -9.81
# 初始速度
v0 = 12
# 计算高度
z = g * t**2 / 2 + v0 * t
# 第二个初始速度
v02 = 5
# 计算第二个高度
z2 = g * t**2 / 2 + v02 * t
# 创建散点图
scat = ax.scatter(t[0], z[0], c="b", s=5, label=f'v0 = {v0} m/s')
# 创建线图
line2 = ax.plot(t[0], z2[0], label=f'v0 = {v02} m/s')[0]
# 设置坐标轴范围和标签
ax.set(xlim=[0, 3], ylim=[-4, 10], xlabel='Time [s]', ylabel='Z [m]')
# 添加图例
ax.legend()
def update(frame):
x = t[:frame]
y = z[:frame]
# 更新散点图
data = np.stack([x, y]).T
# 更新散点图中每个点的位置
scat.set_offsets(data)
# 更新线图
line2.set_xdata(t[:frame])
line2.set_ydata(z2[:frame])
return (scat, line2)
# 创建动画
# frames为数值表示动画的总帧数,即每次更新参数传入当前帧号
ani = animation.FuncAnimation(fig=fig, func=update, frames=40, interval=30)
# 显示图形
plt.show()

示例-贝叶斯更新动画

 
import math
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
# 定义分布概率密度函数
def beta_pdf(x, a, b):
return (x**(a-1) * (1-x)**(b-1) * math.gamma(a + b)
/ (math.gamma(a) * math.gamma(b)))
# 更新分布类,用于更新动态图
class UpdateDist:
def __init__(self, ax, prob=0.5):
self.success = 0
self.prob = prob
self.line, = ax.plot([], [], 'k-')
self.x = np.linspace(0, 1, 200)
self.ax = ax
# 设置图形参数
self.ax.set_xlim(0, 1)
self.ax.set_ylim(0, 10)
self.ax.grid(True)
# 这条竖直线代表了理论值,图中的分布应该趋近于这个值
self.ax.axvline(prob, linestyle='--', color='black')
def __call__(self, i):
# 这样图形可以连续运行,我们只需不断观察过程的新实现
if i == 0:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值