【matplotlib】可视化解决方案——柱状图标注问题


前言

在日常数据可视化中,很多柱状图看起来都是“光秃秃”的,仅展示柱体高度而缺乏数值标注,这会大大削弱图表的可读性与表达力。例如下图所示:

无标注柱状图

为了提升可视化效果,我们通常会为柱状图添加数值标签,使读者能够一目了然地理解每个柱子的具体数值。

解决方案

解决方案一

从 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 实例的列表
  1. fmt 参数格式跟其他字符串显示格式是一致的。
  2. container 参数不一定是 bar 函数的返回值,他可以是所有 Patch 类的子类实例
  3. 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()

普通柱状图画图结果如下:

annotate柱状图

叠加柱状图画图结果如下:

annotate叠加柱状图

由上两图可知,单一的柱状图标注使用该函数没有什么问题,但是叠加柱状图就出现了很大的问题,其原因在于 height = rect.get_height() 这句话,标注位置的高度使用的是柱体的高度而不是柱体顶点的值,那么对于叠加柱状图中的 bottom 参数则无法很好的响应,故而对叠加柱状图进行标注的效果很糟糕。

总结

  • 如果你使用的是 matplotlib ≥ 3.4.1,推荐使用内置的 bar_label() 方法,简单高效,支持多种场景;
  • 如果版本较旧,可以使用 annotate() 方法自定义标注,但在叠加图中需特别注意处理 bottom 的累计值。
    为了更好的柱状图可读性,标注是不可或缺的一环,合理地选择方法将显著提升图表的质量与专业度。

文章作者: pancx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 pancx !
评论
 本篇
【matplotlib】可视化解决方案——柱状图标注问题 【matplotlib】可视化解决方案——柱状图标注问题
本文介绍了如何在matplotlib 3.4.1中使用内置的bar_label方法高效标注柱状图,包括边缘和中心标注,并讨论了低版本下自定义annotate方法的局限性。重点讲解了叠加柱状图的标注技巧,建议更新库以提升可视化效果。
下一篇 
  目录