简体   繁体   English

如何在matplotlib中为colorbar设置动画

[英]How to animate the colorbar in matplotlib

I have an animation where the range of the data varies a lot. 我有一个动画,其中数据的范围变化很​​大。 I would like to have a colorbar which tracks the max and the min of the data (ie I would like it not to be fixed). 我想有一个跟踪数据的最大值和最小值的colorbar条(即我希望它不被修复)。 The question is how to do this. 问题是如何做到这一点。

Ideally I would like the colorbar to be on its own axis. 理想情况下,我希望colorbar在自己的轴上。

I have tried the following four things 我尝试了以下四件事

1. Naive approach 1.天真的方法

The problem: A new colorbar is plottet for each frame 问题:新的颜色条是每帧的绘图

#!/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. Adding to the images 2.添加到图像

Changing the for loop above to 将上面的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])

The problem: This raises 问题:这引起了提升

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

3. Plotting on its own axis 3.在自己的轴上绘图

The problem: The colorbar is not updated. 问题: colorbar未更新。

 #!/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()

A combination of 2. and 4. 2和4的组合。

The problem: The colorbar is constant. 问题: colorbar是不变的。

A similar question is posted here , but it looks like the OP is satisfied with a fixed colorbar . 这里发布一个类似的问题,但看起来OP对固定的colorbar感到满意。

While I'm not sure how to do this specifically using an ArtistAnimation , using a FuncAnimation is fairly straightforward. 虽然我不确定如何使用ArtistAnimation专门执行此ArtistAnimation ,但使用FuncAnimation非常简单。 If I make the following modifications to your "naive" version 1 it works. 如果我对您的“天真”版本1进行以下修改,则可以正常工作。

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

The main difference is that I do the levels calculations and contouring in a function instead of creating a list of artists. 主要区别在于我在函数中进行级别计算和轮廓修改,而不是创建艺术家列表。 The colorbar works because you can clear the axes from the previous frame and redo it every frame. 颜色栏有效,因为您可以清除前一帧中的轴并每帧重做一次。

Doing this redo is necessary when using contour or contourf , because you can't just dynamically change the data. 使用contourcontour contourf ,必须执行此重做,因为您不能仅动态更改数据。 However, as you have plotted so many contour levels and the result looks smooth, I think you may be better off using imshow instead - it means you can actually just use the same artist and change the data, and the colorbar updates itself automatically. 但是,由于您绘制了如此多的轮廓级别并且结果看起来很流畅,我认为您可能最好使用imshow - 这意味着您实际上可以只使用相同的艺术家并更改数据,并且imshow会自动更新。 It's also much faster! 它也快得多!

Better Version 更好的版本

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM