Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Centre bars in barplots if category is missing in hue

If one uses hue in seaborn's barplot and one of the categories is missing, it leads to this strange effect of "empty columns" where seaborn plots nothing at the place where it expects the hue to be (see below at the second position, Fri; or in the second picture). How can one centre such cases so that it is at the place of the tick? In the first case, it would be in the middle of the orange bar, in the second it would remove the empty space between the blue and green and place the tick in the middle of the green bar. Thanks.

import seaborn

tips = sns.load_dataset("tips")
tips.loc[(tips["sex"]=="Male")&(tips["day"]=="Fri"), "total_bill"]=np.nan

sns.barplot(x="day", y="total_bill", hue="sex", data=tips)

sns.barplot(x="sex", y="total_bill", hue="day", data=tips)

two categories

enter image description here

Side note, it is unrelated to this post.

like image 838
My Work Avatar asked Oct 14 '25 05:10

My Work


1 Answers

As mentioned in the comments by @TrentonMcKinney, the following post has some manual workaround (changed and adapted, notice also that the colours get preserved since it does not touch the graph, just the positions):

import seaborn
tips = sns.load_dataset("tips")
tips.loc[(tips["sex"]=="Male")&(tips["day"]=="Fri"), "total_bill"]=np.nan

plot1 = sns.barplot(x="day", y="total_bill", hue="sex", data=tips)

for i, bar in enumerate(plot1.axes.patches): 

    # move the missing to the centre
    current_width = bar.get_width()
    current_pos = bar.get_x()
    if i == 5:
        bar.set_x(current_pos-(current_width/2))
        # move also the std mark
        plot1.axes.lines[i].set_xdata(current_pos)


plot2 = sns.barplot(x="sex", y="total_bill", hue="day", data=tips)

for i, bar in enumerate(plot2.axes.patches): 

    # move the missing to the centre
    current_width = bar.get_width()
    current_pos = bar.get_x()
    if i == 0:
        bar.set_x(current_pos+(current_width/2))
        # get also the std mark
        plot2.axes.lines[i].set_xdata(current_pos+(current_width))
    
    if i == 4:
        bar.set_x(current_pos-(current_width/2))
        # get also the std mark
        plot2.axes.lines[i].set_xdata(current_pos)
    
    if i == 6:
        bar.set_x(current_pos-(current_width/2))
        # get also the std mark
        plot2.axes.lines[i].set_xdata(current_pos)    

There is more work (eg. taking also care of the std or finding the right index) and the problem remains -- it fills the space so that it looks better but does not remove the space. However, I believe that this is sufficient. Thanks a lot to @JohanC and @TrentonMcKinney.

enter image description here

enter image description here

like image 198
My Work Avatar answered Oct 16 '25 21:10

My Work