【问题标题】: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,但我也想保留错误栏

【问题讨论】:

    标签: python pandas matplotlib annotations bar-chart


    【解决方案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')
    

    【讨论】:

      猜你喜欢
      • 2016-01-04
      • 2020-12-31
      • 2020-08-26
      • 1970-01-01
      • 2017-10-30
      • 2017-06-20
      • 2019-09-17
      • 2020-12-27
      相关资源
      最近更新 更多