Labeling boxplot in seaborn with median value

后端 未结 3 1896
庸人自扰
庸人自扰 2020-11-30 06:50

How can I label each boxplot in a seaborn plot with the median value?

E.g.

import seaborn as sns
sns.set_style(\"whitegrid\")
tips = sns.load_dataset         


        
相关标签:
3条回答
  • 2020-11-30 07:10

    This can also be achieved by deriving median from the plot itself without exclusively computing median from data

    box_plot = sns.boxplot(x="day", y="total_bill", data=tips)
    
    ax = box_plot.axes
    lines = ax.get_lines()
    categories = ax.get_xticks()
    
    for cat in categories:
        # every 4th line at the interval of 6 is median line
        # 0 -> p25 1 -> p75 2 -> lower whisker 3 -> upper whisker 4 -> p50 5 -> upper extreme value
        y = round(lines[4+cat*6].get_ydata()[0],1) 
    
        ax.text(
            cat, 
            y, 
            f'{y}', 
            ha='center', 
            va='center', 
            fontweight='bold', 
            size=10,
            color='white',
            bbox=dict(facecolor='#445A64'))
    
    box_plot.figure.tight_layout()
    

    0 讨论(0)
  • 2020-11-30 07:12

    I love when people include sample datasets!

    import seaborn as sns
    
    sns.set_style("whitegrid")
    tips = sns.load_dataset("tips")
    box_plot = sns.boxplot(x="day",y="total_bill",data=tips)
    
    medians = tips.groupby(['day'])['total_bill'].median()
    vertical_offset = tips['total_bill'].median() * 0.05 # offset from median for display
    
    for xtick in box_plot.get_xticks():
        box_plot.text(xtick,medians[xtick] + vertical_offset,medians[xtick], 
                horizontalalignment='center',size='x-small',color='w',weight='semibold')
    

    0 讨论(0)
  • 2020-11-30 07:12

    Based on ShikjarDua's excellent approach, I created a version which works independent of tick positions. This comes in handy when dealing with grouped data in seaborn (i.e. hue=parameter). Additionally, I added an explicit switch for fliers, which changes the lines per drawn box.

    import seaborn as sns
    import matplotlib.pyplot as plt
    import matplotlib.patheffects as path_effects
    
    
    def main():
        sns.set_style("whitegrid")
        tips = sns.load_dataset("tips")
        # optionally disable fliers
        showfliers = False
        # plot data and create median labels
        box_plot = sns.boxplot(x="day", y="total_bill", data=tips,
                               showfliers=showfliers, hue="sex")
        create_median_labels(box_plot.axes, showfliers)
        plt.show()
    
    
    def create_median_labels(ax, has_fliers):
        lines = ax.get_lines()
        # depending on fliers, toggle between 5 and 6 lines per box
        lines_per_box = 5 + int(has_fliers)
        # iterate directly over all median lines, with an interval of lines_per_box
        # this enables labeling of grouped data without relying on tick positions
        for median_line in lines[4:len(lines):lines_per_box]:
            # get center of median line
            mean_x = sum(median_line._x) / len(median_line._x)
            mean_y = sum(median_line._y) / len(median_line._y)
            # print text to center coordinates
            text = ax.text(mean_x, mean_y, f'{mean_y:.1f}',
                           ha='center', va='center',
                           fontweight='bold', size=10, color='white')
            # create small black border around white text
            # for better readability on multi-colored boxes
            text.set_path_effects([
                path_effects.Stroke(linewidth=3, foreground='black'),
                path_effects.Normal(),
            ])
    
    
    if __name__ == '__main__':
        main()
    
    

    0 讨论(0)
提交回复
热议问题