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