Reputation: 366
The following code animates a bar chart. I'm trying to apply a gradient and text labels to this animation though. The autolabel()
function aims to add the text label and the gradientbars()
functions aims to add the gradient. When applying here functions however, the animation just produces a still image.
I'm also hoping to adjust the gradient to a fixed point. Specifically, rather than applying the gradient to the extent of the individual bar height, I'm hoping to apply a max level of 6. Please see figure below regarding this.
I've commented out the specific functions below so the code works but applying them will produced the figure attached.
fig, ax = plt.subplots()
ax.grid(False)
data = np.random.randint(5, size=(10, 5))
x = ['A','B','C','D','E']
plt.ylim(0, 6)
rects = plt.bar(x, data[0])
def autolabel(rects):
'''
Attach a text label above each bar displaying its height
'''
for rect in rects:
height = rect.get_height()
ax.text(rect.get_x() + rect.get_width()/2., 1.05 * height,
'%d' % int(height),
ha = 'center', va = 'bottom')
def gradientbars(bars):
grad = np.atleast_2d(np.linspace(0,5,256)).T
cmap = 'Blues'
ax = bars[0].axes
lim = ax.get_xlim() + ax.get_ylim()
for bar in bars:
bar.set_zorder(1)
bar.set_facecolor("none")
x,y = bar.get_xy()
w,h = bar.get_width(), bar.get_height()
ax.imshow(grad, extent = [x, x + w, y, y + h], aspect = "auto", zorder = 0, cmap = cmap)
ax.axis(lim)
#autolabel(rects)
#gradientbars(rects)
def animate(i):
for rect, yi in zip(rects, data[i]):
rect.set_height(yi)
anim = animation.FuncAnimation(fig, animate, frames = len(data), interval = 100)
plt.show()
Upvotes: 0
Views: 169
Reputation: 40727
This is how I would do this:
def autolabel(rects):
'''
Attach a text label above each bar displaying its height
'''
ts = []
for rect in rects:
height = rect.get_height()
t = ax.text(rect.get_x() + rect.get_width()/2., 1.05 * height,
'%d' % int(height),
ha = 'center', va = 'bottom')
ts.append(t)
return ts
def gradientbars(bars, cmap, vmin, vmax):
g = np.linspace(vmin,vmax,100)
grad = np.vstack([g,g]).T
xmin,xmax = ax.get_xlim()
ymin,ymax = ax.get_ylim()
ims = []
for bar in bars:
bar.set_facecolor('none')
im = ax.imshow(grad, aspect="auto", zorder=0, cmap=cmap, vmin=vmin, vmax=vmax, extent=(xmin,xmax,ymin,ymax))
im.set_clip_path(bar)
ims.append(im)
return ims
Nbars=5
Nframes=10
vmin=0
vmax=6
cmap = 'Blues'
data = np.random.choice([0,1,2],size=(Nframes,Nbars))
data = data.cumsum(axis=1)
data[data>6] = 6
x=[chr(ord('A')+i) for i in range(Nbars)]
print(x)
print(data)
fig, ax = plt.subplots()
ax.grid(False)
plt.ylim(vmin, vmax)
rects = plt.bar(x,data[0])
labels = autolabel(rects)
imgs = gradientbars(rects, cmap=cmap, vmin=vmin, vmax=vmax)
def animate(i):
for rect,label,img,yi in zip(rects, labels, imgs, data[i]):
rect.set_height(yi)
label.set_text('%d'%int(yi))
label.set_y(yi)
img.set_clip_path(rect)
anim = animation.FuncAnimation(fig, animate, frames = len(data), interval = 500)
plt.show()
EDIT Same with negative numbers
Nbars = 5
Nframes = 50
dt=50
cmap='coolwarm_r'
data = np.zeros(shape=(Nframes,Nbars))
for i in range(Nbars):
d = np.sin(np.arange(0,Nframes*(dt*1e-3),dt*1e-3)*0.5*np.pi+np.random.uniform(low=-1., high=1.))
data[:,i] = d
x = [chr(ord('A')+i) for i in range(Nbars)]
vmin = -1.
vmax = 1.
def gradientbars(bars, cmap, vmin, vmax):
g = np.linspace(vmin,vmax,100)
grad = np.vstack([g,g]).T
xmin,xmax = ax.get_xlim()
ymin,ymax = ax.get_ylim()
ims = []
for bar in bars:
bar.set_facecolor('none')
im = ax.imshow(grad, aspect="auto", zorder=0, cmap=cmap, vmin=vmin, vmax=vmax, extent=(xmin,xmax,ymin,ymax))
im.set_clip_path(bar)
ims.append(im)
return ims
def autolabel(rects):
'''
Attach a text label above each bar displaying its height
'''
ts = []
for rect in rects:
height = rect.get_height()
va = 'bottom' if height>=0 else 'top'
t = ax.text(rect.get_x() + rect.get_width()/2., height,
f'{height:.2f}',
ha ='center', va=va)
ts.append(t)
return ts
with plt.style.context('dark_background'):
fig, ax = plt.subplots()
ax.set_ylim(vmin,vmax)
rects = ax.bar(x=x, height=data[0,:])
imgs = gradientbars(rects, cmap, vmin, vmax)
labels = autolabel(rects)
sns.despine(ax=ax, left=True, right=True, bottom=True, top=True)
ax.set_yticks([])
def animate(i, data, rects, imgs, labels):
for j,(rect,img,label) in enumerate(zip(rects,imgs,labels)):
rect.set_height(data[i,j])
img.set_clip_path(rect)
label.set_y(data[i,j])
label.set_va('bottom' if data[i,j]>=0 else 'top')
label.set_text(f'{data[i,j]:.2f}')
ani = animation.FuncAnimation(fig, animate, frames=Nframes, fargs=(data, rects, imgs, labels), interval=dt)
Upvotes: 2