在 matplotlib 中使用 for 循环定义要动画的多个绘图

Posted

技术标签:

【中文标题】在 matplotlib 中使用 for 循环定义要动画的多个绘图【英文标题】:Defining multiple plots to be animated with a for loop in matplotlib 【发布时间】:2014-03-23 04:14:45 【问题描述】:

Thanks to 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([], []) 有什么区别?

谢谢

【问题讨论】:

【参考方案1】:

在下面的解决方案中,我展示了一个更大的示例(还有条形图),它可以帮助人们更好地理解其他情况下应该做什么。在代码之后,我解释了一些细节并回答了奖金问题。

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

说明

这个想法是绘制您需要的内容,然后重用 matplotlib 返回的艺术家(查看更多 here)。这是通过首先绘制您想要的虚拟草图并保留matplotlib 给您的对象来完成的。然后在您的 initanimate 函数上,您可以更新需要动画的对象。

请注意,在plt.plot([], [])[0] 中,我们有一个线条艺术家,因此我使用[plt.plot([], [])[0] for _ in range(N)] 收集它们。另一方面,plt.bar([0.5,1,1.5],[50,40,90],width=0.1) 返回一个可以为 矩形艺术家 迭代的容器。 list(rectangles) 只需将此容器转换为要与lines 连接的列表。

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

额外问题的答案:

    line, = plt.plot([], [])plt.plot 返回的列表的第一个元素分配给可验证的lineline = plt.plot([], []) 只需分配整个列表(只有一个元素)。

【讨论】:

非常感谢。请问plt.plot列表的第一个元素是什么? plt.plot 返回matplotlib.artist.Artists 的列表,因此当您仅绘制一行时,它会返回仅包含一项的列表:matplotlib.lines.Line2D 艺术家。但是,如果您像lines = plt.plot( *([[], []]*N) ) 那样绘制多行,它会返回列表中的所有行。 谢谢。最后一个问题。 animatereturnline后面的逗号不是强制的吗?如果我正确理解您的解决方案,则没有这样的逗号。 没有逗号,但请检查我是否这样做plt.plot([], [])[0] 以获得第一个值。 line, = plt.plot([], []) 语法是使用 a,b = [1,2] 上的 python 赋值的解包功能使 line = plt.plot([], [])[0] 这一行更简洁 嗨,我可以问一个问题来扩展这个吗?在您的示例中,您有多个 lines 对象,但是如果我想更新 linesimage 对象会发生什么(例如更新点的位置并更新动画中的 matshow() 数组。我可以把这两个在同一个元组中?【参考方案2】:

这是一个更易读的修改示例。 这只是 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

【讨论】:

以上是关于在 matplotlib 中使用 for 循环定义要动画的多个绘图的主要内容,如果未能解决你的问题,请参考以下文章

For 循环中的 Matplotlib 图例

在 For 循环 Matplotlib 中创建子图

在 for 循环中调整 matplotlib 散点图大小

如何从 for 循环中为 matplotlib 图设置动画

Python使用matplotlib来for循环实时画图为啥会出现多个图出来,循环几次就出来几个图,啥原因?

Python使用matplotlib可视化连续(数值)变量的堆叠的直方图自定义堆叠直方图中不同分组条形的色彩(Histogram for Continuous Variable)