Matplotlib animation.FuncAnimation() 动画错过第一帧?

Posted

技术标签:

【中文标题】Matplotlib animation.FuncAnimation() 动画错过第一帧?【英文标题】:Matplotlib animation.FuncAnimation() animation miss first frame? 【发布时间】:2022-01-12 02:06:03 【问题描述】:

我是 Python 和 Matplotlib 的新手。我有这段代码可以在 3d Matplotlib 子图中绘制轨迹:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs(self.x: xx, self.y: yy).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]




fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video

# trajectory =  [(2.5, 3.5, 27.5),(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))


i = islice(trajectory, 0 , len(trajectory), 1)


pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo
    
    try: 
        coord = next(i)
        
    except:
        print('coord empty')
        return
    
    if pippo:
        pippo.remove()
    
    print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)  # e' necessario



plt.show()
plt.close('all')


fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


print('#####################')

i = islice(trajectory, 0 , len(trajectory), 1)

ani2 = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)

# writervideo = animation.FFMpegWriter(fps=1, codec="h264") 
# ani2.save('video_steeper.mp4', writer=writervideo)

ani2.save("movie.gif", writer=animation.PillowWriter(fps=1))

我很难理解为什么要有一个正确的动画和保存的动画我需要复制我的trajectory 列表的第一个元素 ((2.5, 3.5, 27.5))。

如果我不这样做,动画和我的 gif 只显示 9 个点而不是 10 个点,请查看结果输出:

10 元素列表中的电影trajectory

来自 11 元素列表 trajectory 的电影(第一个重复):

我已经尝试了很多来弄清楚发生了什么但无法理解,奇怪的是我在 animate 函数中的 print 语句为两个不同的列表打印了 11 和 10 分,但两者都是他们错过了动画和文件中的一帧。有什么线索吗?

【问题讨论】:

【参考方案1】:

在我看来,您的问题来自您正在创建的可迭代 i 和函数 next 的使用。我不确定为什么会发生这种情况,但如果不是创建可迭代的i,而是直接索引你的轨迹,那么你的动画会按预期工作。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from itertools import islice
import sympy as sym
from sympy.parsing.sympy_parser import parse_expr
from sympy import Eq, solve

class FunZ:    
    def __init__(self, f):  
        self.x, self.y  = sym.symbols('x y')
        self.f = parse_expr(f)
        
    def evalu(self, xx, yy):   
        return float(self.f.subs(self.x: xx, self.y: yy).evalf())
  
    def derX(self, xx, yy):        
        self.dx = sym.diff(self.f, self.x)
        return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
    
    def derY(self, xx, yy):
        self.dy = sym.diff(self.f, self.y)
        return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
    
    def derXY(self, xx, yy):
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):     
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)      
        eq1 = Eq(self.dx ,0)   
        eq2 = Eq(self.dy ,0)
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        print(solu, type(solu))
        return solu

XX = np.linspace(-4, 4, 100)
YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')
ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]
arr = np.array(ij, dtype=float)

der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 
derX = np.array(der_x)
der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 
derY = np.array(der_y)

x = arr[:, 0]
y = arr[:, 1]
data = arr[:, 2]

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]
print('len trajectory : ', len(trajectory))

pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo

    x = trajectory[j][0]
    y = trajectory[j][1]
    z = trajectory[j][2]

    if pippo is not None:
        pippo.remove()
    pippo = ax.scatter(x,y,z)          
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory), repeat=False)  # e' necessario
plt.show()

输出给出:

【讨论】:

可能是 print(coord) 也来自迭代器,它实际上打印了所有 10 或 11 个点 我怀疑在全局范围内声明的迭代器 i 会在动画中搞砸。 可能与:ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128), init_func=init, blit=True) --> init_funct有关=init 参见示例:matplotlib.org/stable/api/animation_api.html 不是真的,但明白了,确实感谢您的时间,我应该开始尝试阅读文档(行话对我来说仍然是很大的陡峭墙,而不是寻找教程 好收获!感谢分享!【参考方案2】:

FuncAnimation 加上init_func 稍微摆弄了一下:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs(self.x: xx, self.y: yy).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))
i = islice(trajectory, 0 , len(trajectory), 1)

def init_animate():
    ax.plot_trisurf(x, y, data, color="green", alpha=0.5)
    
pippo = None
def animate(j):  #funziona senza stop iteration with animation.save
    
    global pippo
    
    try: 
        coord = next(i)
        
    except:
        print('coord empty')
        return
    
    if pippo:
        pippo.remove()
    
    print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo

ani = animation.FuncAnimation(fig, animate, interval=1000, frames=len(trajectory),init_func=init_animate, repeat=False)  

plt.show()
plt.close('all')

print('#####################')

能够在 gif 上移动所有 10 个点:

也能够删除 frames 参数,但只是为了可视化而不是保存我的动画,请参阅:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from itertools import islice

import sympy as sym
from sympy.parsing.sympy_parser import parse_expr

from sympy import Eq, solve




class FunZ:
    
    def __init__(self, f):  
  
        self.x, self.y  = sym.symbols('x y')
        
        self.f = parse_expr(f)
        
        # print('f : ', self.f)
        
    def evalu(self, xx, yy):
    
        return float(self.f.subs(self.x: xx, self.y: yy).evalf())
    

    def derX(self, xx, yy):        
        
        self.dx = sym.diff(self.f, self.x)
        
        # print('dx : ', self.dx)
        
        return float(self.dx.subs(self.x: xx, self.y: yy).evalf())
    
    def derY(self, xx, yy):
        
        self.dy = sym.diff(self.f, self.y)
        
        # print('dy :', self.dy)
        
        return float(self.dy.subs(self.x: xx, self.y: yy).evalf())
    
    def derXY(self, xx, yy):
        
        return [float(self.derX(xx, yy)), float(self.derY(xx, yy))]
    
    def minim(self):
        
        self.dx = sym.diff(self.f, self.x)
        self.dy = sym.diff(self.f, self.y)
        print('dx : ', self.dx)
        print('dy : ', self.dy)
        
        eq1 = Eq(self.dx ,0)
        
        eq2 = Eq(self.dy ,0)
        
        solu = solve((eq1,eq2), (self.x, self.y), dict= False)
        
        print(solu, type(solu))
        
        return solu

XX = np.linspace(-4, 4, 100)

YY = np.linspace(-4, 4, 100)

funz = FunZ('x**2 + y**2 + 2*y + 2')

ij = [(x, y, funz.evalu(x, y)) for x in XX for y in YY]


arr = np.array(ij, dtype=float)

# print(arr, arr.size, arr.shape, arr.dtype)


der_x = [(a, b, funz.derX(a, b)) for a in XX for b in YY] 

derX = np.array(der_x)

# print(derX, derX.size, derX.shape, derX.dtype)


der_y = [(a, b, funz.derY(a, b)) for a in XX for b in YY] 

derY = np.array(der_y)

# print(derY, derY.size, derY.shape, derY.dtype)


x = arr[:, 0]
y = arr[:, 1]

data = arr[:, 2]




fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


### devo duplicare primo elemento della lista altrimenti skippa a visualizzare i primi due sul video

trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

# trajectory =  [(2.5, 3.5, 27.5), (2.0, 3.0, 21.0), (1.5, 2.5, 15.5), (1.0, 2.0, 11.0), (0.5, 1.5, 7.5), (0.0, 1.0, 5.0), (0.0, 0.5, 3.25), (0.0, 0.0, 2.0), (0.0, -0.5, 1.25), (0.0, -1.0, 1.0)]

print('len trajectory : ', len(trajectory))


i = islice(trajectory, 0 , len(trajectory), 1)





def init_animate():
    ax.plot_trisurf(x, y, data, color="blue", alpha=0.5)
    
pippo = None
def animate(j):  #funziona senza stop iteration with animation.save

    global pippo

    try: 
        
        coord = next(i)
        print(coord)
        
    except:
        print('coord empty')
        global ani
        global ani2
        ani = None
        ani2 = None
        return 
    
    if pippo:
        pippo.remove()
    
    # print(coord)
                
    x = coord[0]
    y = coord[1]
    z = coord[2]
      
    pippo = ax.scatter(x,y,z)          
    
    return pippo


# ani = animation.FuncAnimation(fig, animate ,frames=len(trajectory),init_func=init_animate, interval=1000, repeat=False) 
ani = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False) 

plt.show()

print('test to see if everything is fine')

fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.set_xlim([-4,4])
ax.set_ylim([-4,4])
ax.set_zlim([-3,50])
ax.plot_trisurf(x, y, data, color="red", alpha=0.5)


print('#####################')

i = islice(trajectory, 0 , len(trajectory), 1)

ani2 = animation.FuncAnimation(fig, animate, init_func=init_animate, interval=1000, repeat=False).save("movie_init_no-frames.gif", writer=animation.PillowWriter(fps=1)) 

不知道发生了什么,但算法一直在运行,输出:

len trajectory :  10
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
test to see if everything is fine
#####################
(2.5, 3.5, 27.5)
(2.0, 3.0, 21.0)
(1.5, 2.5, 15.5)
(1.0, 2.0, 11.0)
(0.5, 1.5, 7.5)
(0.0, 1.0, 5.0)
(0.0, 0.5, 3.25)
(0.0, 0.0, 2.0)
(0.0, -0.5, 1.25)
(0.0, -1.0, 1.0)
coord empty
coord empty
coord empty
^Z
[9]+  Stopped 

我想它与 FuncAnimation 的内部有关。 尽管如此,我应该花时间尝试理解j文档 根据help(matplotlib.animation)

......

class FuncAnimation(TimedAnimation)
     |  FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)
     |  
     |  Makes an animation by repeatedly calling a function *func*.
     |  
     |  Parameters
     |  ----------
     |  fig : `~matplotlib.figure.Figure`
     |      The figure object used to get needed events, such as draw or resize.
     |  
     |  func : callable
     |      The function to call at each frame.  The first argument will
     |      be the next value in *frames*.   Any additional positional
     |      arguments can be supplied via the *fargs* parameter.
     |  
     |      The required signature is::
     |  
     |          def func(frame, *fargs) -> iterable_of_artists
     |  
     |      If ``blit == True``, *func* must return an iterable of all artists
     |      that were modified or created. This information is used by the blitting
     |      algorithm to determine which parts of the figure have to be updated.
     |      The return value is unused if ``blit == False`` and may be omitted in
     |      that case.

......

【讨论】:

以上是关于Matplotlib animation.FuncAnimation() 动画错过第一帧?的主要内容,如果未能解决你的问题,请参考以下文章

Matplotlib 基本概念

Matplotlib快速入门

matplotlib绘图入门详解

matplotlib 绘图常用函数

Matplotlib 教程

安装matplotlib?