12

I'm trying to create bar chart using seaborn.factorplot. My code looks like this:

 import seaborn
 import matplotlib.pyplot as plt 

df=pd.read_csv('data.csv')

 fg = seaborn.factorplot(x='vesselID', y='dur_min', hue='route', size=6,aspect=2    ,kind='bar', data=df)

my data.csv looks like this

 ,route,vesselID,dur_min
 0,ANA-SJ,13,39.357894736842105
 1,ANA-SJ,20,24.747663551401867
 2,ANA-SJ,38,33.72142857142857
 3,ANA-SJ,69,37.064516129032256
 4,ED-KING,30,22.10062893081761
 5,ED-KING,36,21.821428571428573
 6,ED-KING,68,23.396551724137932
 7,F-V-S,1,13.623239436619718
 8,F-V-S,28,14.31294964028777
 9,F-V-S,33,16.161616161616163
 10,MUK-CL,18,13.953191489361702
 11,MUK-CL,19,14.306513409961687
 12,PD-TAL,65,12.477272727272727
 13,PT-COU,52,27.48148148148148
 14,PT-COU,66,28.24778761061947
 15,SEA-BI,25,30.94267515923567
 16,SEA-BI,32,31.0
 17,SEA-BI,37,31.513513513513512
 18,SEA-BR,2,55.8
 19,SEA-BR,13,57.0
 20,SEA-BR,15,54.05434782608695
 21,SEA-BR,17,50.43859649122807

please click here to see the output

Now my question is how to change the width of the bar and I'm not able to achieve this by changing size and aspect.

kaleshanagineni
  • 165
  • 1
  • 3
  • 12
  • 2
    This is a much more complicated example than you need, and *also* I can't just copy and run it. Minimal, complete! – cphlewis Jan 19 '16 at 23:15
  • Thanks for reply. I've edited my post. Hope this helps to solve my problem – kaleshanagineni Jan 19 '16 at 23:37
  • not simple enough. Put the data in the sample code so it's copy-and-paste, or use a seaborn sample dataset (better idea). – cphlewis Jan 19 '16 at 23:45
  • I hope this helps to solve my problem. Thanks in advance. – kaleshanagineni Jan 20 '16 at 00:24
  • 1
    well, your indentation is wrong, you use pd without importing it, and creating a csv file is a lot more annoying than having the data in the code snippet, but. More work on the MVCE next time: http://stackoverflow.com/help/mcve – cphlewis Jan 20 '16 at 01:21

7 Answers7

35

In my case, I didn't have to define a custom function to change the width as suggested above (which btw didn't work for me as all the bars were unaligned). I simply added the attribute dodge=False to the argument of the seaborn plotting function and this made the trick! e.g.

sns.countplot(x='x', hue='y', data=data, dodge=False);

See additional reference here: https://github.com/mwaskom/seaborn/issues/871

My bar plot looks now like this:

enter image description here

tsando
  • 4,004
  • 2
  • 29
  • 34
  • 2
    This answer helped me to grasp the roots of an issue with `hue` usage and "thin" bars. Before that I tried `width` and `height` parameters without success. It only gave errors like `barh() got multiple values for argument 'height'`. – Dmitriy Work May 05 '20 at 07:47
  • 1
    Perfect answer! Especially when it comes to the thin bars due to hue – Adit Sanghvi Sep 13 '21 at 21:16
30

In fact, you can do it using directly the patches attributes with the function set_width. However if you only do that, you will just modify your patches width but not the position on the axe, so you have to change the x coordinates too.

import pylab as plt
import seaborn as sns

tips = sns.load_dataset("tips")
fig, ax = plt.subplots()

sns.barplot(data=tips, ax=ax, x="time", y="tip", hue="sex")

def change_width(ax, new_value) :
    for patch in ax.patches :
        current_width = patch.get_width()
        diff = current_width - new_value

        # we change the bar width
        patch.set_width(new_value)

        # we recenter the bar
        patch.set_x(patch.get_x() + diff * .5)

change_width(ax, .35)
plt.show()

And here is the result : barplot result

jsgounot
  • 644
  • 6
  • 11
  • I am adding this function to my local utilities module! – sgt_pats Apr 22 '19 at 22:16
  • This didn't work for me - I had about 50 bars on my plot and they did change width but the x-coordinate fix didn't work. – tsando Jan 10 '20 at 14:38
  • well, maybe there is a change with new versions of matlplotlib or seaborn. Could you gave me which version do you use ? – jsgounot Jan 15 '20 at 09:39
0

I don't think seaborn will do this, but it's possible mwaskom will come verify.

First, the general way to tweak matplotlib calls in seaborn is to pass through more kwargs (or in some cases a dict thereof), which would change your code like this:

fg = seaborn.factorplot(x='vesselID', y='dur_min', hue='route',
                        size=6,  aspect=2,
                        kind='bar', 
                        width=10, # Factorplot passes arguments through
                        data=df)

but when I run that the error is:

TypeError: bar() got multiple values for keyword argument 'width'

and, yes, it turns out all the seaborn categorical comparisons define width and build a lot of the aesthetics around it. You can check the draw_bars function in categorical.py directly, and of course you could edit your own copy of categorical.py, but that part of seaborn's style is currently baked in.

cphlewis
  • 14,486
  • 3
  • 43
  • 51
0

seaborn is a higher level library above matplotlib. While seaborn doesn't have the flexibility to control bar width, matplotlib can do it with one line of code:

plt.bar(data.xcol,data.ycol,4)

Yuchao Jiang
  • 2,944
  • 27
  • 20
0

This is a slight modification of @jsgounot's answer, which I found very instructive. The modification helps to center the bars on the appropriate xtick.

def change_width(ax, new_value) :
    locs = ax.get_xticks()
    for i,patch in enumerate(ax.patches):
        current_width = patch.get_width()
        diff = current_width - new_value

        # we change the bar width
        patch.set_width(new_value)

        # we recenter the bar
        patch.set_x(locs[i//4] - (new_value * .5))
Jed
  • 578
  • 1
  • 7
  • 17
0

like the answer given for jsgounot but for changing width for horizontal barplots:

def change_width_horizontal(ax, new_value) :
    
    for patch in ax.patches :
        
        current_height = patch.get_height()
        diff = current_height - new_value

        # we change the bar width
        patch.set_height(new_value)

        # we recenter the bar
        patch.set_y(patch.get_y() + diff * .5)
-1

Another solution is to modify the box_aspect:

import pylab as plt
import seaborn as sns

tips = sns.load_dataset("tips")
fig, ax = plt.subplots()

ax = sns.barplot(data=tips, ax=ax, x="time", y="tip", hue="sex")

ax.set_box_aspect(10/len(ax.patches)) #change 10 to modify the y/x axis ratio
plt.show()

enter image description here