2

Given some data:

pt = pd.DataFrame({'alrmV':[000,000,000,101,101,111,111],
                   'he':[e,e,e,e,h,e,e],
                   'inc':[0,0,0,0,0,1,1]})

I would like to create a bar plot separated on row and col.

g = sns.FacetGrid(pt, row='inc', col='he', margin_titles=True)
g.map( sns.barplot(pt['alrmV']), color='steelblue')

This, works, but how do I also add:

  1. an ordered x-axis
  2. only display the top-two-by-count alrmV types

To get an ordered x-axis, that displays the top 2 count types, I played around with this grouping, but unable to get it into a Facet grid:

grouped = pt.groupby( ['he','inc'] )
grw= grouped['alrmV'].value_counts().fillna(0.) #.unstack().fillna(0.)
grw[:2].plot(kind='bar')

Using FacetGrid, slicing limits the total count displayed

g.map(sns.barplot(pt['alrmV'][:10]), color='steelblue')

So how can I get a bar graph, that is separated on row and col, and is ordered and displays only top 2 counts?

Serenity
  • 35,289
  • 20
  • 120
  • 115
syntax
  • 585
  • 1
  • 6
  • 17
  • Adding a comment here for others with >0.9.0 installed, you may find [this](https://stackoverflow.com/a/52672163/1379826) solution helpful – Sos Jul 01 '20 at 08:15

1 Answers1

4

I couldn't get the example to work with the data you provided, so I'll use one of the example datasets to demonstrate:

import seaborn as sns
tips = sns.load_dataset("tips")

We'll make a plot with sex in the columns, smoker in the rows, using day as the x variable for the barplot. To get the top two days in order, we could do

top_two_ordered = tips.day.value_counts().order().index[-2:]

Then you can pass this list to the x_order argument of barplot.

Although you can use FacetGrid directly here, it's probably easier to use the factorplot function:

g = sns.factorplot("day", col="sex", row="smoker",
               data=tips, margin_titles=True, size=3,
               x_order=top_two_ordered)

Which draws:

enter image description here

While I wouldn't recommend doing exactly what you proposed (plotting bars for different x values in each facet), it could be accomplished by doing something like

g = sns.FacetGrid(tips, col="sex", row="smoker", sharex=False)

def ordered_barplot(data, **kws):
    x_order = data.day.value_counts().order().index[-2:]
    sns.barplot(data.day, x_order=x_order)

g.map_dataframe(ordered_barplot)

to make

enter image description here

mwaskom
  • 46,693
  • 16
  • 125
  • 127
  • Ok, is it possible to get the subgroup value counts for each sex? So that if males smoke more on say Monday,Tuesday... ie can you pass a grouped value counts somehow ? – syntax Apr 23 '14 at 19:49
  • No, not through seaborn. I think that would be a confusing plot. – mwaskom Apr 23 '14 at 21:02
  • Well that's not completely true. You could accomplish this, but I would not recommend it. See edit to answer. – mwaskom Apr 23 '14 at 21:11
  • --thanks! (tried to set(rot=10) for the axis labels.. no luck, how can i rotate the axis labels? -sorry if its a simple question, but couldnt find it) – syntax Apr 24 '14 at 01:38
  • If you mean the xticklabels you can do `g.set_xticklabels(rotation=10)` although that will only do the bottom row (which I guess is a bug actually). For other things you'll need to loop through the array of axes at `g.axes` and change them as you normally would in matplotlib. – mwaskom Apr 24 '14 at 02:09
  • The solution is interesting: its like an inverted closure. Normal closure a function object is passed in new args; here the func object is the arg the function around it changes... cant say i would have figured that out, even if I was aware of map_dataframe(). Again, thanks! – syntax Apr 24 '14 at 16:49
  • Just to be clear, `map_dataframe` is a special method that assumes the plotting function takes a `data` argument, which should be a long-form DataFrame. So when plotting each facet the subset of the full DataFrame with just the `sex` and `smoker` values for that facet is passed to `ordered_barplot`. I don't think it's doing anything quite as complicated as you say. – mwaskom Apr 24 '14 at 17:56