如何在matplotlib中为颜色条添加动画效果

16

我有一个动画,其中数据范围变化很大。我想要一个跟踪数据最大值和最小值的 colorbar(即我希望它不是固定的)。问题是如何实现。

理想情况下,我希望 colorbar 在自己的轴上。

我尝试了以下四种方法

1. 幼稚的方法

问题:为每个帧绘制新的颜色条

#!/usr/bin/env python
"""
An animated image
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig = plt.figure()
ax = fig.add_subplot(111)


def f(x, y):
    return np.exp(x) + np.sin(y)

x = np.linspace(0, 1, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

frames = []

for i in range(10):
    x       += 1
    curVals  = f(x, y)
    vmax     = np.max(curVals)
    vmin     = np.min(curVals)
    levels   = np.linspace(vmin, vmax, 200, endpoint = True)
    frame    = ax.contourf(curVals, vmax=vmax, vmin=vmin, levels=levels)
    cbar     = fig.colorbar(frame)
    frames.append(frame.collections)

ani = animation.ArtistAnimation(fig, frames, blit=False)

plt.show()

2. 添加到图片

将上面的for循环更改为:

initFrame = ax.contourf(f(x,y)) 
cbar      = fig.colorbar(initFrame)
for i in range(10):
    x       += 1
    curVals  = f(x, y)
    vmax     = np.max(curVals)      
    vmin     = np.min(curVals)      
    levels   = np.linspace(vmin, vmax, 200, endpoint = True)
    frame    = ax.contourf(curVals, vmax=vmax, vmin=vmin, levels=levels)
    cbar.set_clim(vmin = vmin, vmax = vmax)
    cbar.draw_all()
    frames.append(frame.collections + [cbar])

问题:这引起了

AttributeError: 'Colorbar' object has no attribute 'set_visible'

3. 在自己的坐标轴上绘图

问题:色条未更新。

 #!/usr/bin/env python
 """
 An animated image
 """
 import numpy as np
 import matplotlib.pyplot as plt
 import matplotlib.animation as animation

 fig = plt.figure()
 ax1 = fig.add_subplot(121)
 ax2 = fig.add_subplot(122)


 def f(x, y):
     return np.exp(x) + np.sin(y)

 x = np.linspace(0, 1, 120)
 y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

 frames = []

 for i in range(10):
     x       += 1
     curVals  = f(x, y)
     vmax     = np.max(curVals)
     vmin     = np.min(curVals)
     levels   = np.linspace(vmin, vmax, 200, endpoint = True)
     frame    = ax1.contourf(curVals, vmax=vmax, vmin=vmin, levels=levels)
     cbar     = fig.colorbar(frame, cax=ax2) # Colorbar does not update
     frames.append(frame.collections)

 ani = animation.ArtistAnimation(fig, frames, blit=False)

 plt.show()

第二和第四种方法的结合

问题:颜色栏是固定的。

类似的问题已经在这里发布,但看起来OP对固定的colorbar感到满意。


你对ArtistAnimation的绑定程度有多高?我不确定如何以那种方式实现,但我可以举一个使用FuncAnimation的例子给你。 - Ajean
据我记得,我开始使用ArtistAnimation是因为在使用FuncAnimation对一些充当标题的文本进行动画处理时遇到了问题。除此之外,我也可以考虑切换回FuncAnimation。如果有示例的话,那就更好了 :) - Løiten
1个回答

22

虽然我不确定如何使用ArtistAnimation来完成这个任务,但使用FuncAnimation则相对简单。如果我对你的“naive”版本1进行以下修改,它就可以工作了。

修改后的版本1:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.axes_grid1 import make_axes_locatable

fig = plt.figure()
ax = fig.add_subplot(111)

# I like to position my colorbars this way, but you don't have to
div = make_axes_locatable(ax)
cax = div.append_axes('right', '5%', '5%')

def f(x, y):
    return np.exp(x) + np.sin(y)

x = np.linspace(0, 1, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

frames = []
for i in range(10):
    x       += 1
    curVals  = f(x, y)
    frames.append(curVals)

cv0 = frames[0]
cf = ax.contourf(cv0, 200)
cb = fig.colorbar(cf, cax=cax)
tx = ax.set_title('Frame 0')

def animate(i):
    arr = frames[i]
    vmax     = np.max(arr)
    vmin     = np.min(arr)
    levels   = np.linspace(vmin, vmax, 200, endpoint = True)
    cf = ax.contourf(arr, vmax=vmax, vmin=vmin, levels=levels)
    cax.cla()
    fig.colorbar(cf, cax=cax)
    tx.set_text('Frame {0}'.format(i))

ani = animation.FuncAnimation(fig, animate, frames=10)

plt.show()

主要区别在于我在一个函数中进行级别计算和轮廓绘制,而不是创建艺术家列表。颜色条之所以有效是因为您可以清除先前帧的轴并在每个帧中重新绘制。
当使用contourcontourf时,这样做是必要的,因为您无法动态更改数据。然而,由于您绘制了如此多的等高线级别,并且结果看起来很平滑,我认为您最好使用imshow - 这意味着您实际上只需使用相同的艺术家并更改数据,颜色栏会自动更新。速度也快得多!
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.axes_grid1 import make_axes_locatable

fig = plt.figure()
ax = fig.add_subplot(111)

# I like to position my colorbars this way, but you don't have to
div = make_axes_locatable(ax)
cax = div.append_axes('right', '5%', '5%')

def f(x, y):
    return np.exp(x) + np.sin(y)

x = np.linspace(0, 1, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

# This is now a list of arrays rather than a list of artists
frames = []
for i in range(10):
    x       += 1
    curVals  = f(x, y)
    frames.append(curVals)

cv0 = frames[0]
im = ax.imshow(cv0, origin='lower') # Here make an AxesImage rather than contour
cb = fig.colorbar(im, cax=cax)
tx = ax.set_title('Frame 0')

def animate(i):
    arr = frames[i]
    vmax     = np.max(arr)
    vmin     = np.min(arr)
    im.set_data(arr)
    im.set_clim(vmin, vmax)
    tx.set_text('Frame {0}'.format(i))
    # In this version you don't have to do anything to the colorbar,
    # it updates itself when the mappable it watches (im) changes

ani = animation.FuncAnimation(fig, animate, frames=10)

plt.show()

谢谢,这解决了我的问题。在这个例子中,文字重叠也没有问题(这也是我更改为ArtistAnimation的原因)。当然,使用imshow会更好,但由于使用了非矩形网格,我在这种情况下被限制使用contourf - Løiten
@Ajean,你知道如何使用对数颜色刻度(norm=matplotlib.colors.LogNorm())和固定的vmin/vmax值(固定的颜色条)来完成相同的操作吗? - TheoryX
@ TheoryX 我认为,如果您希望LogNorm(因此也是colormapping)具有固定的vmin和vmax,则只需将其传递给imshow的初始调用,然后不要在动画函数内调整'clim'。这会导致固定的色条,因此并不完全符合这个特定问题的要求。 - Ajean

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