簡體   English   中英

動畫線和散點圖時如何避免“列表索引超出范圍”錯誤

[英]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')

標題問題很容易成為一個通用的“我如何改進此代碼”,但這里不鼓勵這樣做。 雖然代碼生成動畫,但此代碼存在幾個更精細的問題。 我按重要性降序列出了它們:

  1. “列表索引超出范圍”錯誤:這是由於繪制線和散點的方式造成的。 對於線,應該已經有兩個點了。 如何避免這種情況並擁有更清潔的動畫師功能? (它仍然在繪圖 - 只是這個錯誤仍然存​​在,這讓我很煩。)
  2. 線尖總是在點上(注意紅球上的藍色小線段)。 我試圖改變順序,但這無助於讓紅點始終在線上。 我該怎么做?
  3. 在 Spyder 中運行時,它似乎在繪圖窗口中對其進行了兩次動畫處理(速度不同)。 我不確定為什么。 創建的 gif 始終以較晚的速度(較慢)。 保存時可能會覆蓋速度較慢的幀,從而覆蓋觀察結果。 這意味着動畫器函數被調用了兩次? 我該如何解決? 如果我在 Linux python 環境中運行它就不會發生這種情況。
  4. Generic:有沒有更聰明的方法來做同樣的動畫? 例如,我覺得代碼太長了,有什么方法可以使它更緊湊,或許更高效?
  1. 將動畫中的幀數設置為等於您正在繪制的變量的長度。 例如: ani.FuncAnimation(fig, buildframes, frames=len(x), interval=500)
  2. 您可以使用zorder強制以適當的順序顯示事物。 在這里,我使用 ``zorder=1' for the line and使用 zorder=2' 作為散布,因此散布將在線條頂部可視化。
  3. 我無法對此發表評論,因為我不使用 Spyder。
  4. 使用動畫時,不要將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

# https://stackoverflow.com/a/64083631/2329968
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')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM