用条形图制作标签动画-matplotlib



下面的代码为条形图和相关的标签值设置动画。我遇到的问题是在整数为负数时定位标签。具体来说,我希望标签位于条形图的顶部,而不是内部。它适用于第一帧,但随后的动画帧将恢复为在条形图中绘制负整数的标签。

def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
ts = []
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = 0
if rect.get_y() < 0:
height = rect.get_y()
else:
height = rect.get_height()
p_height = (height / y_height)
if p_height > 0.95: 
label_position = height - (y_height * 0.05) if (height > -0.01) else height + (y_height * 0.05) 
else:
label_position = height + (y_height * 0.01) if (height > -0.01) else height - (y_height * 0.05) 
t = ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')

ts.append(t)

return ts
def gradientbars(bars, ax, 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
vmin = -6
vmax = 6
cmap = 'PRGn'
data = np.random.randint(-5,5, size=(10, 4))
x = [chr(ord('A')+i) for i in range(4)]
fig, ax = plt.subplots()
ax.grid(False)
ax.set_ylim(vmin, vmax)
rects = ax.bar(x,data[0])
labels = autolabel(rects, ax)
imgs = gradientbars(rects, ax, 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()

它适用于第一帧。

您在第一个绘图中调用autolabel(rects, ax),因此标签放置得很好。

动画的后续帧恢复为在条形图内绘制负整数的标签。

后续帧的标签位置由label.set_y(yi)设置。yi来自data[i],这里没有考虑负值。

我创建了一个名为get_label_position(height)的函数来计算给定高度的右标签位置。它使用全局变量y_height。并在label.set_y()之前调用此函数。

import matplotlib.pyplot as plt
from matplotlib import animation
import pandas as pd
import numpy as np

def get_label_position(height):
p_height = (height / y_height)
label_position = 0
if p_height > 0.95: 
label_position = height - (y_height * 0.05) if (height > -0.01) else height + (y_height * 0.05)
else:
label_position = height + (y_height * 0.01) if (height > -0.01) else height - (y_height * 0.05)
return label_position

def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
ts = []
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = 0
if rect.get_y() < 0:
height = rect.get_y()
else:
height = rect.get_height()
p_height = (height / y_height)
if p_height > 0.95: 
label_position = height - (y_height * 0.05) if (height > -0.01) else height + (y_height * 0.05) 
else:
label_position = height + (y_height * 0.01) if (height > -0.01) else height - (y_height * 0.05) 
t = ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')

ts.append(t)

return ts
def gradientbars(bars, ax, 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
vmin = -6
vmax = 6
cmap = 'PRGn'
data = np.random.randint(-5,5, size=(10, 4))
x = [chr(ord('A')+i) for i in range(4)]
fig, ax = plt.subplots()
ax.grid(False)
ax.set_ylim(vmin, vmax)
rects = ax.bar(x,data[0])
labels = autolabel(rects, ax)
imgs = gradientbars(rects, ax, cmap=cmap, vmin=vmin, vmax=vmax)
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
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(get_label_position(yi))
img.set_clip_path(rect)

anim = animation.FuncAnimation(fig, animate, frames = len(data), interval = 500)
plt.show()

最新更新