如何制作条形图动画

6

我想在Python中制作一个动画条形图,并将此动画保存为mp4格式。 我的问题是,保存的视频帧会重叠,尽管我使用“blit = True”告诉动画只绘制从一帧到下一帧变化的内容。 令人惊讶的是,在Python的内置预览中不会出现此问题。 这是反映我的情况的最小化示例:

import matplotlib.pyplot as plt
from matplotlib import animation

def barlist(n): #That's the list of bars I want to display
    C=[]
    for k in range(1,6):
        C.append(1/float(n*k))
    return C

fig=plt.figure()

n=100 #Number of frames

def animate(i):
    x=range(1,6)
    y=barlist(i+1)
    return plt.bar(x,y)

anim=animation.FuncAnimation(fig,animate,repeat=False,blit=True,frames=n,
                             interval=50)
anim.save('barchart_animated_'+str(n)+'.mp4')
plt.show()

我必须承认我不太确定该如何消除这个缺陷。我知道的唯一一个在框架中没有重叠条的示例在这里(更确切地说,我是指以下链接的第一个答案的代码): 动态更新matplotlib中的条形图 似乎我需要以某种方式告诉动画如何使用set_height方法在每个帧上设置每个条的高度。但正如我所说,我真的不知道上面的示例有什么问题。谢谢任何帮助!
马丁

1
你为什么要在保存动画时使用blit呢?除此之外,问题并不清楚。看起来你对程序的结果感到不满意。但这可能有很多不同的原因。你能提供一个明确的问题描述和相关的实际问题吗? - ImportanceOfBeingErnest
我现在已经编辑了我的问题。这对你有帮助吗? - Martin Monath
1个回答

19
您遇到的问题在于您在每个动画迭代中创建了一个新的条形图。它们将逐个添加到图表中,但由于它们的高度随着时间而缩小,因此可能看起来只有第一个条形图存在。
有两种方法可以克服这个问题。第一种选择是在绘制新的条形图之前清除轴。但是,这将重新调整轴限制,因此应该始终设置为相同的值。
另一个选择是操作轴中唯一的条形图,并针对每个帧调整其高度。下面的代码展示了这种方法。
import matplotlib.pyplot as plt
from matplotlib import animation

def barlist(n): 
    return [1/float(n*k) for k in range(1,6)]

fig=plt.figure()

n=100 #Number of frames
x=range(1,6)
barcollection = plt.bar(x,barlist(1))

def animate(i):
    y=barlist(i+1)
    for i, b in enumerate(barcollection):
        b.set_height(y[i])

anim=animation.FuncAnimation(fig,animate,repeat=False,blit=False,frames=n,
                             interval=100)

anim.save('mymovie.mp4',writer=animation.FFMpegWriter(fps=10))
plt.show()
从评论中回答的问题:

抹去是一种技术,其中所有不变的图形部分被存储为背景。然后针对每个动画帧,只重新绘制变化的部分。这避免了需要从头开始重新绘制背景,并因此允许更快的动画。抹去只会影响屏幕上的动画,因为将动画保存到文件中不是即时执行的(也没有必要)。
这里使用blit=False可以使代码更加简单,因为我们不需要关心屏幕上的动画和保存的动画之间的差异 - 它们是完全相同的。

enumerate函数同时产生枚举序列中的索引和对象。在这里我使用它,因为这是一种方便的方法来在同一循环中获得两者。这在这里并不重要,您也可以使用其他方式进行操作。

for i in range(len(barcollection)):
    barcollection[i].set_height(y[i])

在此输入图片描述


哇!谢谢你的快速回答!不过,我还有以下两个问题: - Martin Monath
  1. 那么什么时候使用“blit=True”有帮助呢?我看到你甚至将blit=False设置为了所需的结果,那么我在blit变量的语义上理解错了什么吗?
  2. enumerate函数是否具有内置的return命令?就我所看到的这里,似乎enumerate在最后提供了一个yield命令。这在这里很重要吗?
- Martin Monath
1
好的,也许我应该更精确地提出我的第二个问题:为什么你的animate函数末尾没有“return”或“yield”命令?这是因为barcollection是全局名称吗? - Martin Monath
无论如何,非常感谢您的回答。您的解决方案解决了我的问题^^。 - Martin Monath
动画函数需要使用从其作用域外部可以访问的对象。当使用不可变类型时,可以使用“global”来实现,但在这里并不必要,因为我们正在使用PatchCollection的实例,在动画函数内部是可以访问的。所有这些都与“return”或“yield”完全无关。如果使用了 blitting,则动态函数需要返回要重新绘制的艺术家列表。由于我们在这里没有使用blitting,因此不需要返回值。 - ImportanceOfBeingErnest
显示剩余4条评论

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