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

Posted

技术标签:

【中文标题】添加误差线时如何注释条形图【英文标题】:How to annotate bar plots when adding error bars 【发布时间】:2022-01-16 20:46:37 【问题描述】:

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

'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

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

假设有两个数据框的均值和标准差

我试过这段代码

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,但我也想保留错误栏

【问题讨论】:

【参考方案1】: 堆积条形图并不是呈现数据的理想方式。使用误差线时,堆叠的条形图更难阅读,可能与给定堆栈中的误差线和注释重叠,这可能会导致可视化混乱。 stacked=Truestacked=False 会出现此问题,它适用于使用matplotlib.axes.Axes.bar 后跟matplotlib.axes.Axes.errorbar。 此答案也适用于水平条。 这不适用于 ci=True 的 seaborn 条形图 pandas.DataFrame.plot 返回一个Axes,可以从中提取containers。 添加yerr 会导致containers 包含ErrorbarContainer objectBarContainer object 有关使用 matplotlib.pyplot.bar_label 的详细说明以及其他示例,请参阅此 answer。 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' 时会标注patch 值,使用label_type='edge' 时会使用patch 的cumsum

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()


海生酒吧

默认ci=True 的seaborn 条形图不会在containers 中返回ErrorbarContainer objects

sns.catplotkind='bar'

请参阅此answer,了解有关注释 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')

【讨论】:

以上是关于添加误差线时如何注释条形图的主要内容,如果未能解决你的问题,请参考以下文章

如何在列的分组条形图上添加误差线

如何向条形图添加多个注释

R语言ggplot2可视化条形图(bar plot)并为条形图添加误差条(error bar)自定义设置误差条(error bar)的颜色/色彩( Barplots with Error bar)

如何增加子图文本大小并添加自定义条形图注释

如何使用 mplcursors 在条形图上添加悬停注释

Pandas,条形图注释