动画线和散点图时如何避免 'list index out of range' 错误
How do I avoid the 'list index out of range' error when animating line as well as scatter plot
我只是在玩 matplotlib 的动画模块。我想创建一个弹丸运动的动画图。我设法使用以下代码创建它:
import matplotlib.animation as ani
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['figure.dpi'] = 150
## Inputs
angle = 30 # deg
angleRad = angle*np.pi/180
vel = 2 # m/s
deltat = 0.005 # s (timestep)
acc = -9.81 # m/s^2 (gravitational acc)
## Calculations
# Initialisations
ux0 = vel*np.cos(angleRad)
uy0 = vel*np.sin(angleRad)
U = [ux0]
V = [uy0]
# Acceleration
ax = 0
ay = acc
# Velocity
def calcVel(time):
Uvel = ux0 + ax*time
Vvel = uy0 + ay*time
return (Uvel, Vvel)
# Location
x0 = 0
y0 = 0
x = [x0]
y = [y0]
def location(time):
locx = U[0]*time + 0.5*ax*time**2
locy = V[0]*time + 0.5*ay*time**2
return (locx, locy)
yn = y0
t0 = 0
n = 1
count = [0]
while yn>=0:
t = t0 + n*deltat
xVel, yVel = calcVel(t)
posx, posy = location(t)
yn = posy
if yn>=0:
U.append(xVel)
V.append(yVel)
x.append(posx)
y.append(posy)
count.append(t)
n +=1
def buildframes(i=int):
line.set_data(x[:i], y[:i])
points.set_offsets([x[i-1], y[i-1]])
index.set_text('time = %.3f s' % count[i])
plt.pause(0.001)
## Plotting
fig = plt.figure()
axes = fig.add_subplot(1,1,1)
line, = axes.plot([], []) # , 'bo', lw=2, scalex = True, scaley = True
points = plt.scatter([], [], c='red', s=15)
axes.set_ylim(min(y), np.true_divide(np.ceil(max(y) * 10**2), 10**2))
axes.set_xlim(min(x), np.true_divide(np.ceil(max(x) * 10**2), 10**2))
plt.ylabel('Height')
plt.xlabel('Distance')
index = axes.annotate('', xy=(0.75, 0.95), xycoords='axes fraction')
animator = ani.FuncAnimation(fig, buildframes, interval = 500)
#animator.save("filename")
plt.show()
animator.save('animation.gif')
标题问题很可能是笼统的“我如何改进这段代码”,但这里不鼓励这样做。虽然代码生成动画,但此代码存在几个更细微的问题。我按重要性递减顺序列出了它们:
- “列表索引超出范围”错误:这是由于线条和散点的绘制方式所致。对于线应该已经有两个点。我如何避免这种情况并拥有更清晰的动画师功能? (它仍然在绘制 - 只是这个错误仍然存在,这让我很烦。)
- 线尖始终在点上方(注意红球上方的微小蓝色线段)。我曾尝试更改顺序,但这无助于让红点始终在线上方。我该怎么做?
- 当在 Spyder 中 运行 时,它似乎在绘图 window 中动画了两次(速度不同)。我不确定为什么。创建的 gif 始终具有较晚的速度(较慢)。保存时可能会覆盖速度较慢的帧,从而覆盖观察结果。这意味着动画函数被调用了两次?我该如何解决?如果我在 Linux python 环境中 运行 它不会发生这种情况。
- 通用:有没有更聪明的方法来制作相同的动画?例如感觉代码太长了,请问有什么办法可以让它更紧凑更高效?
- 将动画中的帧数设置为等于要绘制的变量的长度。例如:
ani.FuncAnimation(fig, buildframes, frames=len(x), interval=500)
- 您可以使用
zorder
强制以适当的顺序显示内容。在这里,我使用 ``zorder=1for the line and
zorder=2` 作为散点,因此散点将在行的顶部可视化。
- 我不能对此发表评论,因为我不使用 Spyder。
- 使用动画时,不要将
plt.pause
放在动画函数中。调用 ani.FuncAnimation
时设置适当的 interval
。动画功能很好。在下面的代码中,我使用 Numpy 的“矢量化”进行了相同的计算,因此无需使用循环(这样可以节省一些代码行)。
import matplotlib.animation as ani
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['figure.dpi'] = 150
## Inputs
angle = 30 # deg
angleRad = angle*np.pi/180
vel = 2 # m/s
deltat = 0.005 # s (timestep)
acc = -9.81 # m/s^2 (gravitational acc)
# Initialisations
x0 = 0
y0 = 0
ux0 = vel*np.cos(angleRad)
uy0 = vel*np.sin(angleRad)
ax = 0
ay = acc
#
def linspace(start, stop, step=1.):
"""
Like np.linspace but uses step instead of num
This is inclusive to stop, so if start=1, stop=3, step=0.5
Output is: array([1., 1.5, 2., 2.5, 3.])
"""
return np.linspace(start, stop, int((stop - start) / step + 1))
t0 = 0
tf = 1 # arbitrary value
# discretize the time with a given deltat
time = linspace(t0, tf, deltat)
# compute x-y locations
x = ux0*time + 0.5*ax*time**2
y = uy0*time + 0.5*ay*time**2
# now there is a change that y < 0 for some x.
# We need to only keep the locations and times where y >= 0.
idx = y >= 0
x = x[idx]
y = y[idx]
time = time[idx]
# compute velocities
U = ux0 + ax*time
V = uy0 + ay*time
def buildframes(i):
line.set_data(x[:i+1], y[:i+1])
points.set_offsets([x[i], y[i]])
index.set_text('time = %.3f s' % time[i])
## Plotting
fig = plt.figure()
axes = fig.add_subplot(1,1,1)
line, = axes.plot([], [], zorder=1) # , 'bo', lw=2, scalex = True, scaley = True
points = plt.scatter([], [], c='red', s=15, zorder=2)
axes.set_ylim(min(y), np.true_divide(np.ceil(max(y) * 10**2), 10**2))
axes.set_xlim(min(x), np.true_divide(np.ceil(max(x) * 10**2), 10**2))
plt.ylabel('Height')
plt.xlabel('Distance')
index = axes.annotate('', xy=(0.75, 0.95), xycoords='axes fraction')
animator = ani.FuncAnimation(fig, buildframes, frames=len(x), interval=500)
# plt.show()
animator.save('animation.gif')
我只是在玩 matplotlib 的动画模块。我想创建一个弹丸运动的动画图。我设法使用以下代码创建它:
import matplotlib.animation as ani
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['figure.dpi'] = 150
## Inputs
angle = 30 # deg
angleRad = angle*np.pi/180
vel = 2 # m/s
deltat = 0.005 # s (timestep)
acc = -9.81 # m/s^2 (gravitational acc)
## Calculations
# Initialisations
ux0 = vel*np.cos(angleRad)
uy0 = vel*np.sin(angleRad)
U = [ux0]
V = [uy0]
# Acceleration
ax = 0
ay = acc
# Velocity
def calcVel(time):
Uvel = ux0 + ax*time
Vvel = uy0 + ay*time
return (Uvel, Vvel)
# Location
x0 = 0
y0 = 0
x = [x0]
y = [y0]
def location(time):
locx = U[0]*time + 0.5*ax*time**2
locy = V[0]*time + 0.5*ay*time**2
return (locx, locy)
yn = y0
t0 = 0
n = 1
count = [0]
while yn>=0:
t = t0 + n*deltat
xVel, yVel = calcVel(t)
posx, posy = location(t)
yn = posy
if yn>=0:
U.append(xVel)
V.append(yVel)
x.append(posx)
y.append(posy)
count.append(t)
n +=1
def buildframes(i=int):
line.set_data(x[:i], y[:i])
points.set_offsets([x[i-1], y[i-1]])
index.set_text('time = %.3f s' % count[i])
plt.pause(0.001)
## Plotting
fig = plt.figure()
axes = fig.add_subplot(1,1,1)
line, = axes.plot([], []) # , 'bo', lw=2, scalex = True, scaley = True
points = plt.scatter([], [], c='red', s=15)
axes.set_ylim(min(y), np.true_divide(np.ceil(max(y) * 10**2), 10**2))
axes.set_xlim(min(x), np.true_divide(np.ceil(max(x) * 10**2), 10**2))
plt.ylabel('Height')
plt.xlabel('Distance')
index = axes.annotate('', xy=(0.75, 0.95), xycoords='axes fraction')
animator = ani.FuncAnimation(fig, buildframes, interval = 500)
#animator.save("filename")
plt.show()
animator.save('animation.gif')
标题问题很可能是笼统的“我如何改进这段代码”,但这里不鼓励这样做。虽然代码生成动画,但此代码存在几个更细微的问题。我按重要性递减顺序列出了它们:
- “列表索引超出范围”错误:这是由于线条和散点的绘制方式所致。对于线应该已经有两个点。我如何避免这种情况并拥有更清晰的动画师功能? (它仍然在绘制 - 只是这个错误仍然存在,这让我很烦。)
- 线尖始终在点上方(注意红球上方的微小蓝色线段)。我曾尝试更改顺序,但这无助于让红点始终在线上方。我该怎么做?
- 当在 Spyder 中 运行 时,它似乎在绘图 window 中动画了两次(速度不同)。我不确定为什么。创建的 gif 始终具有较晚的速度(较慢)。保存时可能会覆盖速度较慢的帧,从而覆盖观察结果。这意味着动画函数被调用了两次?我该如何解决?如果我在 Linux python 环境中 运行 它不会发生这种情况。
- 通用:有没有更聪明的方法来制作相同的动画?例如感觉代码太长了,请问有什么办法可以让它更紧凑更高效?
- 将动画中的帧数设置为等于要绘制的变量的长度。例如:
ani.FuncAnimation(fig, buildframes, frames=len(x), interval=500)
- 您可以使用
zorder
强制以适当的顺序显示内容。在这里,我使用 ``zorder=1for the line and
zorder=2` 作为散点,因此散点将在行的顶部可视化。 - 我不能对此发表评论,因为我不使用 Spyder。
- 使用动画时,不要将
plt.pause
放在动画函数中。调用ani.FuncAnimation
时设置适当的interval
。动画功能很好。在下面的代码中,我使用 Numpy 的“矢量化”进行了相同的计算,因此无需使用循环(这样可以节省一些代码行)。
import matplotlib.animation as ani
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['figure.dpi'] = 150
## Inputs
angle = 30 # deg
angleRad = angle*np.pi/180
vel = 2 # m/s
deltat = 0.005 # s (timestep)
acc = -9.81 # m/s^2 (gravitational acc)
# Initialisations
x0 = 0
y0 = 0
ux0 = vel*np.cos(angleRad)
uy0 = vel*np.sin(angleRad)
ax = 0
ay = acc
#
def linspace(start, stop, step=1.):
"""
Like np.linspace but uses step instead of num
This is inclusive to stop, so if start=1, stop=3, step=0.5
Output is: array([1., 1.5, 2., 2.5, 3.])
"""
return np.linspace(start, stop, int((stop - start) / step + 1))
t0 = 0
tf = 1 # arbitrary value
# discretize the time with a given deltat
time = linspace(t0, tf, deltat)
# compute x-y locations
x = ux0*time + 0.5*ax*time**2
y = uy0*time + 0.5*ay*time**2
# now there is a change that y < 0 for some x.
# We need to only keep the locations and times where y >= 0.
idx = y >= 0
x = x[idx]
y = y[idx]
time = time[idx]
# compute velocities
U = ux0 + ax*time
V = uy0 + ay*time
def buildframes(i):
line.set_data(x[:i+1], y[:i+1])
points.set_offsets([x[i], y[i]])
index.set_text('time = %.3f s' % time[i])
## Plotting
fig = plt.figure()
axes = fig.add_subplot(1,1,1)
line, = axes.plot([], [], zorder=1) # , 'bo', lw=2, scalex = True, scaley = True
points = plt.scatter([], [], c='red', s=15, zorder=2)
axes.set_ylim(min(y), np.true_divide(np.ceil(max(y) * 10**2), 10**2))
axes.set_xlim(min(x), np.true_divide(np.ceil(max(x) * 10**2), 10**2))
plt.ylabel('Height')
plt.xlabel('Distance')
index = axes.annotate('', xy=(0.75, 0.95), xycoords='axes fraction')
animator = ani.FuncAnimation(fig, buildframes, frames=len(x), interval=500)
# plt.show()
animator.save('animation.gif')