在Matplotlib中使用for循环定义多个图表进行动画制作

18

感谢Jake Vanderplas, 我知道如何使用matplotlib开始编写动画图的代码。以下是示例代码:

from matplotlib import pyplot as plt
from matplotlib import animation

fig = plt.figure()

ax = plt.axes(xlim=(0, 2), ylim=(0, 100))

line, = plt.plot([], [])

def init():
    line.set_data([], [])
    return line,

def animate(i):
    line.set_data([0, 2], [0,i])
    return line,

anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=100, interval=20, blit=True)

plt.show()

假设现在我想绘制大量函数(这里假设有四个),并使用循环定义它们。我进行了一些巫术编程,试图理解如何模仿逗号随后的行,这是我得到的结果(不用说它不起作用:AttributeError: 'tuple' object has no attribute 'axes')。
from matplotlib import pyplot as plt
from matplotlib import animation

fig = plt.figure()

ax = plt.axes(xlim=(0, 2), ylim=(0, 100))

line = []
N = 4

for j in range(N):
    temp, = plt.plot([], [])
    line.append(temp)

line = tuple(line)

def init():
    for j in range(N):
        line[j].set_data([], [])
    return line,

def animate(i):
    for j in range(N):
        line[j].set_data([0, 2], [10 * j,i])
    return line,

anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=100, interval=20, blit=True)

plt.show()

我的问题是:如何使它工作?奖励(可能有关联):在line, = plt.plot([], [])line = plt.plot([], [])之间的区别是什么?

谢谢

2个回答

33
在下面的解决方案中,我展示了一个更大的示例(包括条形图),这可能有助于人们更好地理解其他情况应该做什么。在代码之后,我会解释一些细节并回答额外的问题。
import matplotlib
matplotlib.use('Qt5Agg') #use Qt5 as backend, comment this line for default backend

from matplotlib import pyplot as plt
from matplotlib import animation

fig = plt.figure()

ax = plt.axes(xlim=(0, 2), ylim=(0, 100))

N = 4
lines = [plt.plot([], [])[0] for _ in range(N)] #lines to animate

rectangles = plt.bar([0.5,1,1.5],[50,40,90],width=0.1) #rectangles to animate

patches = lines + list(rectangles) #things to animate

def init():
    #init lines
    for line in lines:
        line.set_data([], [])

    #init rectangles
    for rectangle in rectangles:
        rectangle.set_height(0)

    return patches #return everything that must be updated

def animate(i):
    #animate lines
    for j,line in enumerate(lines):
        line.set_data([0, 2], [10 * j,i])

    #animate rectangles
    for j,rectangle in enumerate(rectangles):
        rectangle.set_height(i/(j+1))

    return patches #return everything that must be updated

anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=100, interval=20, blit=True)

plt.show()

解释

这个想法是先绘制所需的东西,然后重复使用艺术家(在此处了解更多:here),它们是由matplotlib返回的。这是通过首先绘制您想要的虚拟草图并保留matplotlib给您的对象来完成的。然后,在initanimate函数中,您可以更新需要动画化的对象。

请注意,在plt.plot([], [])[0]中,我们获得单个line artist,因此我使用[plt.plot([], [])[0] for _ in range(N)]将它们收集起来。另一方面,plt.bar([0.5,1,1.5],[50,40,90],width=0.1)返回一个容器,该容器可以用于迭代rectangle artistslist(rectangles)只是将此容器转换为列表,并与lines连接起来。

我将线条与矩形分开,因为它们的更新方式不同(并且是不同的艺术家),但initanimate都会返回它们。

奖励问题的答案:

  1. line, = plt.plot([], [])plt.plot返回的列表的第一个元素赋值给变量line
  2. line = plt.plot([], [])只是分配整个列表(只有一个元素)。

非常感谢。我可以问一下 plt.plot 列表的第一个元素是什么吗? - cjorssen
1
plt.plot 返回一个 matplotlib.artist.Artist 对象列表,因此当您绘制一条线时,它将返回仅包含一个元素的列表:即一个 matplotlib.lines.Line2D 对象。但是,如果您像这样绘制多条线 lines = plt.plot( *([[], []]*N) ),它将返回所有线条的列表。 - Alvaro Fuentes
谢谢。最后一个问题。在animatereturn中,line后面的逗号是否是必须的?如果我理解正确你的解决方案,是没有这样的逗号的。 - cjorssen
谢谢。请查看我的后续问题 - cjorssen
2
你好,我能问一个关于扩展这个的问题吗?在你的例子中,你有多个 lines 对象,但是如果我想要更新一个 linesimage 对象(比如更新一个点的位置,和更新动画中的 matshow() 数组),那会发生什么呢?我能把这两者放在同一个元组里面吗? - Marses
显示剩余4条评论

0
这里有一个更易读的示例。这只是从 matplotlib 网站上的代码添加了另一部分。让我感到困惑的是,起初我没有意识到 plt 函数正在返回列表,忽略了尾随逗号,甚至没有注意到如何将事物联系在一起,似乎这些都被库本身相当隐式地处理了。但关键是创建几个可更新对象,并将它们作为同一列表的一部分从两个关键函数中返回,它们将在动画运行时同步。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xdata, ydata = [], []
xdata2,ydata2= [], []
ln, = plt.plot([], [], 'ro') 
ln2, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,ln2

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)

    xdata2.append(frame)
    ydata2.append(np.cos(frame))
    ln2.set_data(xdata2,ydata2)
    
    return ln,ln2

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)


plt.show()

为了更有趣,将动画回调函数更改为以下内容:

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)

    delta = 2*np.pi/128
    xdata2.append([frame*2,frame*2+delta])
    ydata2.append([np.cos(frame*2), np.cos(frame*2+delta)])
    ln2.set_data(xdata2,ydata2)
    
    return ln,ln2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接