How to animate the colorbar in matplotlib

匿名 (未验证) 提交于 2019-12-03 02:56:01

问题:

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 (i.e. I would like it not to be fixed). The question is how to do this.

Ideally I would like the colorbar to be on its own axis.

I have tried the following four things

1. Naive approach

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

Changing the for loop above to

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

The problem: The colorbar is not updated.

 #!/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.

The problem: The colorbar is constant.

A similar question is posted here, but it looks like the OP is satisfied with a fixed colorbar.

回答1:

While I'm not sure how to do this specifically using an ArtistAnimation, using a FuncAnimation is fairly straightforward. If I make the following modifications to your "naive" version 1 it works.

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


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!