18
df <- structure(list(ID = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 
2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 5L, 
5L, 5L, 5L, 5L, 5L, 6L, 6L, 6L, 6L, 6L, 6L, 7L, 7L, 7L), .Label = c("1", 
"2", "3", "4", "5", "6", "7"), class = "factor"), TYPE = structure(c(1L, 
2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 
1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L, 4L, 
5L, 6L, 1L, 2L, 3L), .Label = c("1", "2", "3", "4", "5", "6", 
"7", "8"), class = "factor"), TIME = structure(c(2L, 2L, 2L, 
2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 
1L, 1L, 1L), .Label = c("1", "5", "15"), class = "factor"), VAL = c(0.937377670081332, 
0.522220720537007, 0.278690102742985, 0.967633064137772, 0.116124767344445, 
0.0544306698720902, 0.470229141646996, 0.62017166428268, 0.195459847105667, 
0.732876230962574, 0.996336271753535, 0.983087373664603, 0.666449476964772, 
0.291554537601769, 0.167933790013194, 0.860138458199799, 0.172361251665279, 
0.833266809117049, 0.620465772924945, 0.786503327777609, 0.761877260869369, 
0.425386636285111, 0.612077651312575, 0.178726130630821, 0.528709076810628, 
0.492527724476531, 0.472576208412647, 0.0702785139437765, 0.696220921119675, 
0.230852259788662, 0.359884874196723, 0.518227979075164, 0.259466265095398, 
0.149970305617899, 0.00682218233123422, 0.463400925742462, 0.924704828299582, 
0.229068386601284)), .Names = c("ID", "TYPE", "TIME", "VAL"), row.names = c(NA, 
-38L), class = "data.frame")

If I create the following plot:

ggplot(df, aes(x=ID, y=VAL, fill=TYPE)) +
  facet_wrap(~ TIME, ncol=1) +
  geom_bar(position="stack") +
  coord_flip()

plot

I then decide I would ideally like to supress any factors from being shown in a facet where they don't have any data. I have referenced various questions and answers that say the scale="free" method is the way to go (as opposed to drop=TRUE which would drop empty facets corresponding to unused values in TIME), so next:

ggplot(df, aes(x=ID, y=VAL, fill=TYPE)) +
  facet_wrap(~TIME, ncol=1, scale="free") +
  geom_bar(position="stack") +
  coord_flip()

plot

My question is how to prevent the rescaling of the bars that occurs for the facet that has 4 bars vs. the facet with 3 bars. The effect is subtle in this contrived example, much worse with my actual data. The ideal output would have the bottom facet with ID factors 1,4, and 6 on the vertical axis with bars having the same width as the top facet, and so the overall vertical dimension of the facet would be reduced.

Bonus points if you can help me with why the counts are stacked instead of the numeric values (Fixed now)

Bounty update:

As mentioned in my followup question it looks like a better solution could involve the use of ggplot_build and ggplot_table and modifying the gtable object. I'm pretty sure I could figure it out given time, but I'm hoping a bounty might motivate someone else to help me out. Koshke has posted some examples of this.

Community
  • 1
  • 1
mindless.panda
  • 4,014
  • 4
  • 35
  • 57
  • 2
    For the bonus point: maybe a lower case `y` instead of `Y`, along with `stat = "identity"`? – joran Jul 02 '12 at 22:36
  • Thanks! The identity arg was not needed though. – mindless.panda Jul 02 '12 at 22:40
  • there is also the `space='free'` argument to `facet_wrap` that might be what you're looking for. There was a perfect question regarding this earlier but the user deleted it... – Justin Jul 02 '12 at 22:47
  • Unfortunately `space="free"` is not available for `facet_wrap` according to documentation as well the comments to joran's answer. – mindless.panda Jul 03 '12 at 17:31

3 Answers3

16

How about this:

df$w <- 0.9
df$w[df$TIME == 5] <- 0.9 * 3/4
ggplot(df, aes(x=ID, y=VAL, fill=TYPE)) +
   facet_wrap(~TIME, ncol=1, scale="free") +
   geom_bar(position="stack",aes(width = w),stat = "identity") +
   coord_flip()

enter image description here

Not sure if I got the arithmetic right there, but you get the idea.

joran
  • 169,992
  • 32
  • 429
  • 468
  • Clever! Visually that looks right and I'm following what you did. The problem I see is that the width of the bars are manually adjusted to compensate for the different amount of factors between the facets. However this seems problematic once that different is much greater, e.g., what would happen when its 7 facets vs. 2? In this case I'm imagining that the bottom facet would have two bars with the right width, but a lot of whitespace. This gets at wanting to adjust the overall facet size, but this seems impossible. – mindless.panda Jul 02 '12 at 23:29
  • @mindless.panda Yeah, it sounds like you're looking for a `space` argument but for `facet_wrap` which I don't believe exists (yet). – joran Jul 02 '12 at 23:47
  • Thanks - with manual tweaking this will do for now, hopefully `facet_wrap` gets `space` as well as `labeller`. For perfection I would probably generate separate plots and align them (via grid or inkscape I assume, since I've never actually done that). – mindless.panda Jul 05 '12 at 03:11
12

If you are ok with vertical bars, facet_grid works perfectly:

ggplot(df, aes(x=ID, y=VAL, fill=TYPE)) +
  facet_grid(.~TIME, scale="free_x", space = "free_x") +
  geom_bar(position="stack")

enter image description here

To go a bit further, you can then rotate all the elements of the chart. Now you have what you want if you turn your head by 90° :-)

ggplot(df, aes(x=ID, y=VAL, fill=TYPE)) +
  facet_grid(.~TIME, scale="free_x", space = "free_x") +
  geom_bar(position="stack") +
  opts(legend.text = theme_text(angle=90),
       legend.title = theme_text(angle=90),
       strip.text.x = theme_text(angle=90),
       axis.text.x = theme_text(angle=90),
       axis.text.y = theme_text(angle=90),
       axis.title.x = theme_text(angle=90)
       )

enter image description here

nassimhddd
  • 8,340
  • 1
  • 29
  • 44
7

Over five years since the original question, so thought it should provide a more up to date and cleaner version of doing this. This broadly follows the advice given here: http://ggplot2.tidyverse.org/reference/facet_grid.html

The two key arguments are space="free_y" which allows the height of each panel to be proportional to the length of the scale and scales="free_y" which allows the scales to vary for each column.

coord_flip() allows the barplots to be plotted horizontally, and as a personal preference the label to the facet is rotated within the theme options.

ggplot(df, aes(x=ID, y=VAL, fill=TYPE)) +
  geom_bar(stat="identity") +
  coord_flip() +
  facet_grid(TIME~., scales = "free_y", space = "free_y") +
  theme(strip.text.y = element_text(angle = 0))

enter image description here

Michael Harper
  • 14,721
  • 2
  • 60
  • 84