添加误差线时如何注释条形图

How to annotate bar plots when adding error bars

我有一个这样的数据框字典

{'region': {0: 'R0',1: 'R1',2: 'R2',3: 'R3',4: 'R4',5: 'R5',6: 'R6'},
 'DT': {0: 0.765, 1: 0.694, 2: 0.778, 3: 0.694, 4: 0.629, 5: 0.67, 6: 0.668},
 'GB': {0: 0.714, 1: 0.741, 2: 0.752, 3: 0.741, 4: 0.683, 5: 0.706, 6: 0.656},
 'KNN': {0: 0.625, 1: 0.641, 2: 0.628, 3: 0.641, 4: 0.552, 5: 0.544, 6: 0.578},
 'LR': {0: 0.624, 1: 0.662, 2: 0.634, 3: 0.662, 4: 0.581, 5: 0.629, 6: 0.649},
 'lstm': {0: 0.803,1: 0.633,2: 0.845,3: 0.668,4: 0.717,5: 0.726,6: 0.674}}

格式整齐

    region DT   GB      KNN      LR     lstm
0   R0  0.765   0.714   0.625   0.624   0.803
1   R1  0.694   0.741   0.641   0.662   0.633
2   R2  0.778   0.752   0.628   0.634   0.845
3   R3  0.694   0.741   0.641   0.662   0.668
4   R4  0.629   0.683   0.552   0.581   0.717
5   R5  0.67    0.706   0.544   0.629   0.726
6   R6  0.668   0.656   0.578   0.649   0.674

我想绘制带有误差条的堆积条形图。该数据框没有关于标准差的信息,但我有另一个标准差数据框。

假设有两个dataframe mean和std

我试过这个代码

fig, ax = plt.subplots()
width=0.5
clfs=['DT', 'KNN', 'LR', 'GB', 'lstm']
ax.bar(mean_df['region'], mean_df[clfs[0]], width,yerr=std_df[clfs[0]], label=clfs[0])
for i in range(1,5):
    ax.bar(mean_df['region'], mean_df[clfs[i]], width,yerr=std_df[clfs[i]], label=clfs[i],bottom=mean_df[clfs[i-1]])

plt.xticks(rotation=90)
plt.legend()
plt.show()

但是金条没有正确堆叠。我也在寻找一种在每个条形段上写入值以增加绘图可读性的方法

编辑: 解决方案是在绘制第三个列表时在底部添加前两个列表。

fig, ax = plt.subplots()
ax.bar(mean_df['region'], mean_df[clfs[0]], width,yerr=std_df[clfs[0]], label=clfs[0])
ax.bar(mean_df['region'], mean_df[clfs[1]], width,yerr=std_df[clfs[1]], label=clfs[1],bottom=mean_df[clfs[0]])
ax.bar(mean_df['region'], mean_df[clfs[2]], width,yerr=std_df[clfs[2]], label=clfs[2],
       bottom=mean_df[clfs[0]]+mean_df[clfs[1]])

但我正在寻找一种优雅的方式来执行此操作以及如何在条形图的段上写入值

编辑 2: 我来到这里

ax = mean_df.plot(kind='bar', stacked=True, figsize=(8, 6),yerr=std_df, rot=0, xlabel='region', ylabel='DT')

但现在我正在寻找写文字的方法。 我试过这个

for c in ax.containers:
    ax.bar_label(c, label_type='center')

但我遇到了这个错误

AttributeError: 'ErrorbarContainer' object has no attribute 'patches'

编辑 3
这个错误是因为yerr=std_df,但我也想保留误差线

  • 堆积条形图不是呈现数据的理想方式。对于误差线,堆叠的条形更难阅读,可能与给定堆栈内的误差线重叠,并与注释重叠,这可能导致可视化混乱。
  • stacked=Truestacked=False 会出现此问题,它适用于使用 matplotlib.axes.Axes.bar followed by matplotlib.axes.Axes.errorbar
    • 这个答案也适用于单杠。
    • 这不适用于具有 ci=True
    • 的 seaborn 条形图
  • pandas.DataFrame.plot return是一个Axes,从中可以提取出containers
    • 添加 yerr 结果 containers 包含 ErrorbarContainer objectBarContainer object
  • 查看此 answer for a thorough explanation of using matplotlib.pyplot.bar_label 以及其他示例。
  • 测试于 python 3.10pandas 1.3.4matplotlib 3.5.0seaborn 0.11.2

ax.containers

[<ErrorbarContainer object of 3 artists>,
 <BarContainer object of 2 artists>,
 <ErrorbarContainer object of 3 artists>,
 <BarContainer object of 2 artists>,
 <ErrorbarContainer object of 3 artists>,
 <BarContainer object of 2 artists>]
  • .bar_label在使用label_type='center'时会标注补丁值,在label_type='edge'
  • 时会标注补丁的累计值

pandas.DataFrame.plotyerr

  • BarContainer个对象在奇数索引处,可以用ax.containers[1::2]
  • 提取
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

# load same dataframe
pen = sns.load_dataset('penguins')

# create the aggregated dataframe (mean)
pen_mean = pen.pivot_table(index='sex', columns='species', values='bill_depth_mm', aggfunc='mean')

# create the dataframe for the error bars with (std)
pen_std = pen.pivot_table(index='sex', columns='species', values='bill_depth_mm', aggfunc='std')

# plot the dataframe and add yerr
ax = pen_mean.plot(kind='bar', stacked=True, figsize=(9, 6), rot=0, yerr=pen_std)

# move the legend
ax.legend(bbox_to_anchor=(1, 1.02), loc='upper left')

# iterate through every other container; the even containers are ErrorbarContainer
for c in ax.containers[1::2]:

    # add the annotation
    ax.bar_label(c, label_type='center')

单杠

# plot the dataframe and add yerr
ax = pen_mean.plot(kind='barh', stacked=True, figsize=(9, 6), rot=0, xerr=pen_std)

# move the legend
ax.legend(bbox_to_anchor=(1, 1.02), loc='upper left')

# iterate through every other container; the even containers are ErrorbarContainer
for c in ax.containers[1::2]:

    # add the annotation
    ax.bar_label(c, label_type='center')


Axes.barAxes.errorbar

  • BarContainer对象在偶数索引处,可以用ax.containers[0::2]
  • 提取
data = pen_mean

cols = pen_mean.columns
rows = pen_mean.index

# Get some pastel shades for the colors
colors = ['tab:blue', 'tab:green']
n_rows = len(data)

index = np.arange(len(cols))
bar_width = 0.4

# Initialize the vertical-offset for the stacked bar chart.
y_offset = np.zeros(len(cols))

# Plot bars and create text labels for the table
fig, ax = plt.subplots(figsize=(8, 5))

for i, row in enumerate(rows):
    ax.bar(cols, data.loc[row], bar_width, bottom=y_offset, color=colors[i])
    ax.errorbar(cols, y_offset+data.loc[row], pen_std.loc[row], color='k', ls='none')
    y_offset = y_offset + data.loc[row]
    
# note the order of the container objects is different
for c in ax.containers[0::2]:
    ax.bar_label(c, label_type='center')

plt.show()


seaborn 酒吧

  • 带有默认 ci=True 的 seaborn 条形图在 containers.
  • 中不 return ErrorbarContainer objects

sns.catplotkind='bar'

  • 有关注释 seaborn 图形级条形图的其他示例,请参阅此
p = sns.catplot(kind='bar', data=pen, x='sex', y='bill_depth_mm', hue='species', height=4.5, aspect=1.5)

# since this is a single subplot of a figure
ax = p.axes.flat[0]

for c in ax.containers:

    # add the annotation
    ax.bar_label(c, label_type='center')

sns.barplot

fig = plt.figure(figsize=(9, 6))
p = sns.barplot(data=pen, x='sex', y='bill_depth_mm', hue='species')

p.legend(bbox_to_anchor=(1, 1.02), loc='upper left')

for c in p.containers:

    # add the annotation
    p.bar_label(c, label_type='center')