前言
在日常数据可视化中,很多柱状图看起来都是“光秃秃”的,仅展示柱体高度而缺乏数值标注,这会大大削弱图表的可读性与表达力。例如下图所示:
为了提升可视化效果,我们通常会为柱状图添加数值标签,使读者能够一目了然地理解每个柱子的具体数值。
解决方案
解决方案一
从 matplotlib 3.4.1 起,pyplot 提供了内置的 bar_label 方法用于柱状图标注,其函数原型如下:
def bar_label(container,labels = None,fmt = ‘%g’,label_type = ‘edge’,padding = 0,**kwargs)
- 参数 1:
container
:指定包含所有柱状图和错误条形图的容器,基本从 bar 函数或者barh
函数返回的 BarContainer 的 patches 属性得来。 - 参数 2:
labels
:指定应显示的标签文本列表。如果未给出,则标签文本将是使用 fmt 格式化的数据值 - 参数 3:
fmt
:字符型,指定标签的格式字符串 - 参数 4:
label_type
:限定字符串,指定标签的类型,可选:- edge:标签放置在柱体的端点,显示的值将是该端点的位置
- center:标签位于柱体的中心,显示的值将是该段的长度
- 参数 5:
padding
:浮点型,指定标签到柱状图末端的距离; kwargs
:接收到的关键字参数都会传递给 Axes. annotate- 返回值:包含 Text 实例的列表
- fmt 参数格式跟其他字符串显示格式是一致的。
- container 参数不一定是 bar 函数的返回值,他可以是所有 Patch 类的子类实例
bar_label
支持灵活的格式和位置控制,是目前推荐的标准方式。
示例一:基础柱状图标注
#添加标签等函数
def label(ax):
ax.set_xlabel('X', fontsize=15)
ax.set_ylabel('Y', fontsize=15)
ax.legend(loc='best')
figure = plt.figure(figsize=(8, 6))
ax1 = figure.add_subplot(2, 1, 1)
ax2 = figure.add_subplot(2, 1, 2)
#初始化数据
x = np.random.randint(0, 10, 10)
#画图1label_type='center'
Rectangle1 = ax1.bar(range(10), x, width=0.8, label='有标注,label_type=center')
ax1.bar_label(Rectangle1, label_type='center') #进行标注
label(ax1)
ax1.set_title('bar画图', fontsize=25)
#画图2label_type='edge'
Rectangle2 = ax2.bar(range(10), x, width=0.8, label='有标注,label_type=edge')
ax2.bar_label(Rectangle2, label_type='edge') #进行标注
label(ax2)
plt.show()
画图结果如下:
从上图我们可以看出对于单一的柱状图,label_type
参数没有多大实质影响,知识简单的改变了标注的位置。label_type
参数真正的影响在于叠加柱状图时对两种不同的柱状图进行标注。
示例二:叠加柱状图标注
#初始化数据
N = 5
Y1 = np.random.randint(0, 20, N)
Y2 = np.random.randint(0, 20, N)
fig, ax = plt.subplots()
#绘制叠加柱状图
p1 = ax.bar(range(N), Y1, width=0.6, label='Y1')
p2 = ax.bar(range(N), Y2, width=0.6, bottom=Y1, label='Y2')
#设置标签等
ax.set_ylabel('Y', fontsize=15)
ax.set_xlabel('X', fontsize=15)
ax.set_title('叠加柱状图', fontsize=25)
ax.legend() #打开图例
#使用bar_label方法对柱状图进行标注,默认是在顶端进行标注
ax.bar_label(p1, label_type='center', fontsize=13) #label_type='center'
ax.bar_label(p2, label_type='center', fontsize=13) #label_type='center'
ax.bar_label(p2, fontsize=13) #label_type='edge'
plt.show()
从上图可以看到柱体中心的数字代表该柱体的高度,柱体顶端的数字代表两个柱体加在一起的高度,即定点的高度。这样我们就完成了两个叠加柱状图的标注。
解决方案二
如果你的 matplotlib 版本低于 3.4.1,则无法使用 bar_label,这时可以自定义标注函数实现类似效果:
def auto_label(rects):
"""
自动标定柱状图柱体高度
rects:要进行标定的Rectangle类列表
"""
for rect in rects:
height = rect.get_height()
self.fig1.axes.annotate(format(height, '.2f'),
xy=(rect.get_x() +
rect.get_width() / 2, height),
xytext=(0, 2), # 垂直偏移两格
textcoords="offset points",
ha='center',
va='bottom')
self.fig1.draw()
普通柱状图画图结果如下:
叠加柱状图画图结果如下:
由上两图可知,单一的柱状图标注使用该函数没有什么问题,但是叠加柱状图就出现了很大的问题,其原因在于 height = rect.get_height()
这句话,标注位置的高度使用的是柱体的高度而不是柱体顶点的值,那么对于叠加柱状图中的 bottom 参数则无法很好的响应,故而对叠加柱状图进行标注的效果很糟糕。
总结
- 如果你使用的是
matplotlib ≥ 3.4.1
,推荐使用内置的bar_label()
方法,简单高效,支持多种场景; - 如果版本较旧,可以使用
annotate()
方法自定义标注,但在叠加图中需特别注意处理 bottom 的累计值。
为了更好的柱状图可读性,标注是不可或缺的一环,合理地选择方法将显著提升图表的质量与专业度。