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

Posted

技术标签:

【中文标题】如何从 for 循环中为 matplotlib 图设置动画【英文标题】:How can I animate a matplotlib plot from within for loop 【发布时间】:2022-01-23 00:05:31 【问题描述】:

我想用在 for 循环的每次迭代中计算的值来更新我的 matplotlibplot。这个想法是我可以实时查看计算了哪些值,并在我的脚本运行时通过迭代观察进度迭代。我不想先遍历循环,存储值然后执行绘图。

这里有一些示例代码:

from itertools import count
import random

from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt


def animate(i, x_vals, y_vals):
    plt.cla()
    plt.plot(x_vals, y_vals)

if __name__ == "__main__":
    x_vals = []
    y_vals = []
    fig = plt.figure()
    index = count()

    for i in range(10):
        print(i)
        x_vals.append(next(index))
        y_vals.append(random.randint(0, 10))
        ani = FuncAnimation(fig, animate, fargs=(x_vals, y_vals))
        plt.show()

我在网上看到的大多数示例都处理动画的所有内容都是全局变量的情况,我想避免这种情况。当我使用调试器逐行遍历我的代码时,该图确实出现并且它是动画的。当我只在没有调试器的情况下运行脚本时,会显示图形但没有任何内容,并且我可以看到我的循环没有经过第一次迭代,首先等待图形窗口关闭然后继续。

【问题讨论】:

你不应该在循环中使用plt.show()。毕竟你只想显示一次窗口。 我应该在哪里调用 plt.show()?如果我把它放在 for 循环之外,那么它只会在 for 循环完成执行后显示图形窗口。相反,如果我将 show() 命令放在 for 循环之前,它将阻止执行,直到图形窗口关闭。使用这两个选项,在 for 循环执行期间看不到绘图,我是否在这里遗漏了一些东西以使其正常工作? 在循环外使用 plt.show(),在循环的每次迭代中清除坐标轴并使用循环内部的新值重新绘制它,不要使用动画。 【参考方案1】:

在 matplotlib 中制作动画时,您永远不应该使用循环。

animate 函数会根据您的时间间隔自动调用。

这样的东西应该可以工作

def animate(i, x=[], y=[]):
    plt.cla()
    x.append(i)
    y.append(random.randint(0, 10))
    plt.plot(x, y)


if __name__ == "__main__":
    fig = plt.figure()
    ani = FuncAnimation(fig, animate, interval=700)
    plt.show()

【讨论】:

谢谢,这看起来很有帮助,但我很困惑,因为现在 for 循环消失了(并由 animate 函数控制)。我的循环是程序中主要工作发生的地方,并且计算了许多事情。使用这种方法,看起来我必须将我所有的内循环逻辑放入这个动画函数中,我认为它应该只用于实际的可视化。有没有很好的方法来分离和组合这些东西?还是认为可以将所有内容转储到动画函数中?这里的最佳做法是什么? 您可以创建一个对象并在另一个函数中改变它的状态(实际工作)。然后将该对象传递给animate 函数。再说一次,这正是我会做的。我想创建另一个函数并在 animate 内部调用它也可以。 @dumbPotato21 您能否发布一个“将该对象传递给动画函数”的示例。我试过了,但无法让它作为参数传递(fargs) @dumbPotato21 从***.com/questions/38531076/… 得到它我传递的是fargs 而不是frame @pippo1980 我的意思是这个例子是人为的,我怀疑它是否有任何现实意义,但here 确实如此。但是,如果您在实际应用程序中执行此操作,则更有可能是从 Internet 获取数据 - 而不是生成随机数。在这种情况下,您将必须运行 2 个线程 - 一个用于输出图形,第二个用于更新必须绘制的数据。如果 matplotlib 支持异步,则不需要两个线程,但 afaik 不支持。【参考方案2】:

试图详细说明@dumbpotato21 的答案,这是我的尝试:

import random

from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt



def data():
    cnt = 0
    x = []
    y = []
        
    for i in  range(1,10):
        # x = []
        # y = []
        

        x.append(cnt*i)
        y.append(random.randint(0, 10))

        
        cnt += 1
        
        
        yield  x, y, cnt
    
    input('any key to exit !!!')
    quit()


def init_animate():
    pass

def animate( data, *fargs) :
    
    print('data : ', data, '\n data type : ', type(data), ' cnt : ', data[2])
    
    plt.cla()
    
    x = [i*k for i in data[0]]
    
    y = [i*p for i in data[1]]
    
    plt.plot(x,y)


if __name__ == "__main__":
    fig = plt.figure()
    
    k = 3
    p = 5
    
    ani = FuncAnimation(fig, animate, init_func=init_animate, frames=data,  interval=700, fargs = [k,p])
    plt.show()

【讨论】:

【参考方案3】:

有许多替代方案在不同的情况下可能会派上用场。这是我用过的一个:

import matplotlib.pyplot as plt
import numpy as np
from time import sleep



x = np.linspace(0, 30, 51)
y = np.linspace(0, 30, 51)
xx, yy = np.meshgrid(x, y)


# plt.style.use("ggplot")
plt.ion()
fig, ax = plt.subplots()
fig.canvas.draw()

for n in range(50):
    # compute data for new plot
    zz = np.random.randint(low=-10, high=10, size=np.shape(xx))

    # erase previous plot
    ax.clear()

    # create plot
    im = ax.imshow(zz, vmin=-10, vmax=10, cmap='RdBu', origin='lower')

    # Re-render the figure and give the GUI event loop the chance to update itself
    # Instead of the two lines one can use "plt.pause(0.001)" which, however gives a
    # decepracted warning.
    # See https://github.com/matplotlib/matplotlib/issues/7759/ for an explanation.
    fig.canvas.flush_events()
    sleep(0.1)


# make sure that the last plot is kept
plt.ioff()
plt.show()

此外,线图或 imshow 对象的 set_data(...) 方法在仅数据更改并且您不想重新绘制整个图形(因为这非常耗时)时很有用。

【讨论】:

以上是关于如何从 for 循环中为 matplotlib 图设置动画的主要内容,如果未能解决你的问题,请参考以下文章

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

如何在 Matplotlib 中为子图添加标题

如何在 matplotlib 中为 3D 条形图创建图例?

如何在 matplotlib 中为交互式绘图的 onclick(event) 函数创建类?

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

如何在 For 循环中为多个类别有条件地更新值