Cómo animar la barra de colores en matplotlib

Tengo una animación donde el rango de los datos varía mucho. Me gustaría tener una colorbar que rastrea el máximo y el mínimo de los datos (es decir, me gustaría que no se corrija). La pregunta es cómo hacer esto.

Lo ideal sería que la colorbar de colorbar estuviera en su propio eje.

He intentado las siguientes cuatro cosas

1. enfoque ingenuo

El problema: una nueva barra de colores es plottet para cada cuadro

 #!/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. Añadiendo a las imágenes.

Cambiando el bucle for arriba a

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

El problema: esto plantea

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

3. Ploteo sobre su propio eje.

El problema: la colorbar no se actualiza.

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

Una combinación de 2. y 4.

El problema: la colorbar es constante.

Aquí se publica una pregunta similar, pero parece que el OP está satisfecho con una colorbar fija.

Si bien no estoy seguro de cómo hacer esto específicamente usando un ArtistAnimation , usar un FuncAnimation es bastante sencillo. Si hago las siguientes modificaciones a tu versión “ingenua” 1, funciona.

Versión modificada 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() 

La principal diferencia es que hago los cálculos de niveles y el contorno en una función en lugar de crear una lista de artistas. La barra de colores funciona porque puede borrar los ejes del cuadro anterior y rehacer cada cuadro.

Hacer este rehacer es necesario cuando se usa el contour o la contourf , porque no puedes simplemente cambiar dinámicamente los datos. Sin embargo, como ha trazado tantos niveles de contorno y el resultado se ve suave, creo que es mejor que use imshow en imshow lugar, lo que significa que en realidad puede usar el mismo artista y cambiar los datos, y la barra de colores se actualiza automáticamente. ¡También es mucho más rápido!

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