python动画教程|Animations using Matplotlib-官网教程程序解读

随着python学习的深入,我们不可避免进入画图模块matplotlib,也不可避免会遇到制作动画的需求。

【1】官网教程

如何学习python制作动画,最简单的就是直奔官网:

https://matplotlib.org/stable/users/explain/animations/animations.html#animations

它给出很长的代码,下面是除引入numpy、matplotlib模块之外的部分:

图1

但是官网版本并不具备中文注释,为方便大家快速学习,本期就直接对官网教程自带的代码进行一次添加注释工作,期待能对大家的学习带来便利。

【2】程序模块引入

打开【1】中官网教程会发现,动画制作的前置条件包括:

【a】用数学计算模块numpy生成函数;

【b】用函数绘图模块matplotlib为函数绘图;

【c】用animation模块将matplotlib为函数绘的图形变成动态输出。

官网程序如下:

import matplotlib.pyplot as plt
import numpy as np

import matplotlib.animation as animation

增加注释后变成:

import matplotlib.pyplot as plt   #引入 matplotlib模块,用于绘图
import numpy as np  #引入 numpy模块,用于生成函数

import matplotlib.animation as animation #引入 animation模块,用于生成动画

【3】函数定义代码解读

先查看第一段代码:

fig, ax = plt.subplots()
t = np.linspace(0, 3, 40)
g = -9.81
v0 = 12
z = g * t**2 / 2 + v0 * t

v02 = 5
z2 = g * t**2 / 2 + v02 * t

在这里,需要先说明fig,ax=plt.subplots()的含义:

追溯至官网的入门教程,我们将会看到:

Quick start guide — Matplotlib 3.9.2 documentation

这里的fig,ax=plt.subplots()就是指画一个图,有一个坐标轴。

官网教程还给出了一些测试案例,我们尝试把这段代码直接输出,可参考下输代码:

import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()             # an empty figure with no Axes
fig, ax = plt.subplots()       # a figure with a single Axes
plt.show() #输出图片

运行后:

图2

此时可将初始部分代码解读如下:

fig, ax = plt.subplots()   #定义一个带坐标轴的空图形
t = np.linspace(0, 3, 40)  #定义自变量t的范围是(0,3),中间去40个数,但不包含3
g = -9.81 #定义常数g=-9.81
v0 = 12 #定义常数v0=12
z = g * t**2 / 2 + v0 * t #定义函数z = g * t**2 / 2 + v0 * t ,这是匀加速运动位移时间公式

v02 = 5 #定义常数v02 = 5
z2 = g * t**2 / 2 + v02 * t #定义函数z2 = g * t**2 / 2 + v02 * t ,这是匀加速运动位移时间公式

【4】函数图形输出代码解读

继续解读:

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()

式中,第一行scat = ax.scatter(t[0], z[0], c="b", s=5, label=f'v0 = {v0} m/s')的意思是绘制散点图,t[0]代表t的第0个取值0,z[0]代表z的第0个取值0,c指颜色(具体的,“b”代表blue即蓝色),s指散点的大小,label就是指图例。

看不懂的最快解决办法,就是尝试把代码输出。我们先改写为下述代码:

import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()       # a figure with a single Axes
t = np.linspace(0, 3, 40)  #定义自变量t的范围是(0,3),中间去40个数,但不包含3
g = -9.81 #定义常数g=-9.81
v0 = 12 #定义常数v0=12
z = g * t**2 / 2 + v0 * t #定义函数z = g * t**2 / 2 + v0 * t ,这是匀加速运动位移时间公式

v02 = 5 #定义常数v02 = 5
z2 = g * t**2 / 2 + v02 * t #定义函数z2 = g * t**2 / 2 + v02 * t ,这是匀加速运动位移时间公式
scat = ax.scatter(t[0], z[0], c="b", s=5, label=f'v0 = {v0} m/s')
ax.legend() #输出图例
plt.show() #输出图形

运行后的输出结果为:

 

图3

可见输出点(0,0)对应(t[0],z[0])。

类似的,在上述代码中间补充一行

line2 = ax.plot(t[0], z2[0], label=f'v0 = {v02} m/s')[0] #输出(t[0],z2[0])

变成:

import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()       # a figure with a single Axes
t = np.linspace(0, 3, 40)  #定义自变量t的范围是(0,3),中间去40个数,但不包含3
g = -9.81 #定义常数g=-9.81
v0 = 12 #定义常数v0=12
z = g * t**2 / 2 + v0 * t #定义函数z = g * t**2 / 2 + v0 * t ,这是匀加速运动位移时间公式

v02 = 5 #定义常数v02 = 5
z2 = g * t**2 / 2 + v02 * t #定义函数z2 = g * t**2 / 2 + v02 * t ,这是匀加速运动位移时间公式
scat = ax.scatter(t[0], z[0], c="b", s=5, label=f'v0 = {v0} m/s') #输出(t[0],z[0])
line2 = ax.plot(t[0], z2[0], label=f'v0 = {v02} m/s')[0] #输出(t[0],z2[0])
ax.legend() #输出图例
plt.show() #输出图形

运行后的输出结果为:

图4

虽然只看见一个点,但实际上的输出点(0,0),同时对应(t[0],z[0])和(t[0],z2[0])。 

剩下部分的as.set部分为对坐标轴进行属性设置,对此不再单独输出,将整个函数图形输出代码增加注释如下:

fig, ax = plt.subplots()       # a figure with a single Axes
t = np.linspace(0, 3, 40)  #定义自变量t的范围是(0,3),中间去40个数,但不包含3
g = -9.81 #定义常数g=-9.81
v0 = 12 #定义常数v0=12
z = g * t**2 / 2 + v0 * t #定义函数z = g * t**2 / 2 + v0 * t ,这是匀加速运动位移时间公式

v02 = 5 #定义常数v02 = 5
z2 = g * t**2 / 2 + v02 * t #定义函数z2 = g * t**2 / 2 + v02 * t ,这是匀加速运动位移时间公式
scat = ax.scatter(t[0], z[0], c="b", s=5, label=f'v0 = {v0} m/s') #输出(t[0],z[0])
line2 = ax.plot(t[0], z2[0], label=f'v0 = {v02} m/s')[0] #输出(t[0],z2[0])
ax.set(xlim=[0, 3], ylim=[-4, 10], xlabel='Time [s]', ylabel='Z [m]') #设置坐标轴大小和名称
ax.legend() #输出图例
plt.show() #输出图形

【5】update函数代码解读

继续解读:

def update(frame):
    # for each frame, update the data stored on each artist.
    x = t[:frame]
    y = z[:frame]
    # update the scatter plot:
    data = np.stack([x, y]).T
    scat.set_offsets(data)
    # update the line plot:
    line2.set_xdata(t[:frame])
    line2.set_ydata(z2[:frame])
    return (scat, line2)

这里的自定义函数update,引入了一个参数frame,因为这个参数此时第一次出现。它的赋值将在外部调用update函数的时候完成。

此外定义了矩阵x,它是一个由t组成的矩阵;定义了矩阵y,是z矩阵。

然后定义了data矩阵,它调用了numpy中的stack函数,让矩阵x和y先叠加在一起,然后再通过.T做了一次转置。为了形象理解np.stack([x,y]).T,在编辑器尝试输入以下代码:

import matplotlib.pyplot as plt #引入matplotlib模块
import numpy as np #引入numpy模块
x=np.linspace(0,3,3,dtype=int) #定义x矩阵
y=np.linspace(5,8,3,dtype=int) #定义y矩阵
data=np.stack([x,y]) #将x矩阵和y矩阵叠放在一起
dataT=np.stack([x,y]).T #将x矩阵和y矩阵叠放在一起,然后转置
print('x=',x) #输出x矩阵
print('y=',y) #输出y矩阵
print('data=',data) #输出data矩阵
print('dataT=',dataT) #输出dataT矩阵

运行后输出结果为:

图5

然后散点量scat,它通过调用scat.set_offsets(data)将data里面的数据按照顺序输出。

此后的line2.set_xdata(t[:frame])和line2.set_ydata(z2[:frame])相对简单,将t矩阵作为x输出,z2矩阵最为y输出。

综上所述有:

def update(frame):   #定义update函数
    # for each frame, update the data stored on each artist.
    x = t[:frame]  #定义矩阵x,由t构成的一维数组
    y = z[:frame]  #定义矩阵y,由z构成的一维数组
    # update the scatter plot:
    data = np.stack([x, y]).T #定义矩阵data,将矩阵x,y叠加后转置
    scat.set_offsets(data)  #将矩阵data输出为散点图
    # update the line plot:
    line2.set_xdata(t[:frame]) #将矩阵t设置为x
    line2.set_ydata(z2[:frame]) #将矩阵z2设置为y
    return (scat, line2) #返回scat和line2

【6】animation.FuncAnimation函数代码解读

继续解读,此时只剩最后一行代码:       

ani = animation.FuncAnimation(fig=fig, func=update, frames=40, interval=30)
plt.show()

这里就是使用animation.FuncAnimation模块,反复调用update函数,给frames赋值40,但是我们从官网的animation.FuncAnimation模块中,并没有发现interval应该是哪一个参数。

matplotlib.animation.FuncAnimation — Matplotlib 3.9.2 documentation

为进一步理解interval的意义,最简单的办法就是改变interval的值,看输出结果如何变化。

首先,直接按照interval=30输出,然后,设置interval=0.0030,现实测试发现不会输出结果,只不过是输出变快了。

图6

至此,最后一行代码增加注释后为:

ani = animation.FuncAnimation(fig=fig, func=update, frames=40, interval=30) #调用undape函数输出动画
plt.show() #画图

【7】完整版注释

综上所述,完整版代码注释为:

import matplotlib.pyplot as plt  #引入 matplotlib模块,用于绘图
import numpy as np  #引入 numpy模块,用于生成函数
import matplotlib.animation as animation #引入 animation模块,用于生成动画

fig, ax = plt.subplots()  #定义一个带坐标轴的空图形
t = np.linspace(0, 3, 40) #定义自变量t的范围是(0,3),中间去40个数,但不包含3
g = -9.81 #定义常数g=-9.81
v0 = 12 #定义常数v0=12
z = g * t**2 / 2 + v0 * t #定义函数z = g * t**2 / 2 + v0 * t ,这是匀加速运动位移时间公式

v02 = 5 #定义常数v02 = 5
z2 = g * t**2 / 2 + v02 * t#定义函数z2 = g * t**2 / 2 + v02 * t ,这是匀加速运动位移时间公式

scat = ax.scatter(t[0], z[0], c="b", s=5, label=f'v0 = {v0} m/s')#输出(t[0],z[0])
line2 = ax.plot(t[0], z2[0], label=f'v0 = {v02} m/s')[0] #输出(t[0],z2[0])
ax.set(xlim=[0, 3], ylim=[-4, 10], xlabel='Time [s]', ylabel='Z [m]') #设置坐标轴大小和名称
ax.legend() #输出图例


def update(frame): #定义update函数
    # for each frame, update the data stored on each artist.
    x = t[:frame]  #定义矩阵x,由t构成的一维数组
    y = z[:frame]  #定义矩阵y,由z构成的一维数组
    # update the scatter plot:
    data = np.stack([x, y]).T #定义矩阵data,将矩阵x,y叠加后转置
    scat.set_offsets(data)  #将矩阵data输出为散点图
    # update the line plot:
    line2.set_xdata(t[:frame]) #将矩阵t设置为x
    line2.set_ydata(z2[:frame]) #将矩阵z2设置为y
    return (scat, line2) #返回scat和line2


ani = animation.FuncAnimation(fig=fig, func=update, frames=40, interval=300) #调用undape函数输出动画
plt.show() #画图

【8】总结

本文对matplotlib官网中的animation动画制作模块进行了示例程序解读,解读过程中对不理解的部分进行了展开探索,非常适用于动画制作初学者学习,对新手小白友好。

  • 19
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值