78

When I plot a bar graph in ggplot2 I would like to reduce the space between the bottom of the bars and the x-axis to 0, yet keep the space above the bars and the plot box. I have a hack to do it below. It's dirty and I want to be clean again. Is there a way to achieve this behavior without the dirty little hack?

Default (desired space above but don't want space below bars):

ggplot(mtcars, aes(x=as.factor(carb))) + 
    geom_bar()

enter image description here

Use expand (undesired 0 space above but got the 0 space below bars):

ggplot(mtcars, aes(x=as.factor(carb))) + 
    geom_bar() + 
    scale_y_continuous(expand = c(0,0)) 

enter image description here

Dirty Hack (I like it but its.. well, dirty):

ggplot(mtcars, aes(x=as.factor(carb))) + 
    geom_bar() + 
    scale_y_continuous(expand = c(0,0)) +
    geom_text(aes(x=1, y=10.3, label="Stretch it"), vjust=-1)

enter image description here

zx8754
  • 52,746
  • 12
  • 114
  • 209
Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
  • 1
    I assume you consider using `coord_cartesian` to much hardcoding as well? – Henrik Nov 26 '13 at 15:08
  • @baptise can you add as a solution for future searchers. That works as well. – Tyler Rinker Nov 26 '13 at 15:12
  • Good question, this also annoys me in ggplot2 graphs that y axis does not start from the bottom of the picture frame. – jrara Nov 27 '13 at 11:41
  • 2
    There's a new(er) question that points back to this one, with a [really great solution](http://stackoverflow.com/q/22480052/903061) generalizing the `expand()` into a list of upper limit expansion and lower limit expansion. – Gregor Thomas Dec 17 '15 at 21:10

6 Answers6

49

I might be missing what you really want, but without using geom_text hack you can still set the limits

ggplot(mtcars, aes(x = as.factor(carb))) + 
    geom_bar() + 
    scale_y_continuous(expand = c(0, 0), limits = c(0, 10.3)) 

# marginally cleaner
Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
user1317221_G
  • 15,087
  • 3
  • 52
  • 78
  • 1
    Yes exactly what I wanted. I tried to use: `ylim(c(0,10.3))` after the `scale_y_continuous` and it was a no go. I get it now. Pretty simple. Thanks. – Tyler Rinker Nov 26 '13 at 15:09
  • 1
    I removed the check and awarded to a better answer at this time. I awared a bounty to your answer so no points were lost. Thank you. Your original solution was very useful until it was addressed by the ggplot2 team. – Tyler Rinker Aug 27 '20 at 19:10
39

The R documentation includes a new convenience function called expansion for the expand argument as the expand_scale() became deprecated as of ggplot2 v3.3.0 release.

ggplot(mtcars) +
  geom_bar(aes(x = factor(carb))) + 
  scale_y_continuous(expand = expansion(mult = c(0, .1)))
bee5911
  • 516
  • 4
  • 9
19

You can expand the limits manually, e.g. with expand_limits(y=10.1), or use this trick to add an invisible layer with scaled up data,

ggplot(mtcars, aes(x=as.factor(carb))) + 
    geom_bar() + 
    scale_y_continuous(expand = c(0,0)) +
    geom_blank(aes(y=1.1*..count..), stat="bin")
baptiste
  • 75,767
  • 19
  • 198
  • 294
  • 1
    Ahaa, those `..` variables...+1! – Henrik Nov 26 '13 at 15:32
  • Is there a way to do this without hard coding (1.1)? – jrara Nov 27 '13 at 11:43
  • I mean that this multiplication by 1.1 works in this case but if you have dynamically changing y-axis and you do not know the scale beforehand. In these kind of cases you somehow have to use a parameter instead of hardcoding this multiplication factor. It would be nice if ggplot could do this automatically based on data (y axis scale). – jrara Nov 27 '13 at 14:04
  • 2
    i still don't understand - the `..count..` variable takes care of this automatic adjustment of the y-axis, but at some point it is up to the user to define what margin should be added above the bar (here 10% of the full data range) and that's what this multiplication factor does. It's the same with default axes in base R and ggplot2. – baptiste Nov 27 '13 at 15:42
15

Starting in ggplot2 3.0.0 there is an expand_scale() function that can be used with the expand argument to do exactly this. You define the top and bottom expansion separately.

ggplot(mtcars, aes(x=factor(carb))) + 
     geom_bar() +
     scale_y_continuous(expand = expand_scale(mult = c(0, .1)))
aosmith
  • 34,856
  • 9
  • 84
  • 118
9

Because you seem comfortable with some hardcoding...

ggplot(mtcars, aes(x = as.factor(carb))) + 
  geom_bar() +
  coord_cartesian(ylim = c(0, 10.3))
Henrik
  • 65,555
  • 14
  • 143
  • 159
3

This is an automatic way to produce the spacing at the top, yet remove the bottom spacing. I use 3 % padding since that's what you hard-coded.

plot1 <- ggplot(mtcars, aes(x=as.factor(carb))) +
    geom_bar()

plotInfo <- print(plot1)
yMax <- max(plotInfo$data[[1]]$ymax)
yLimitMax <- 1.03 * yMax

plot2 <- plot1 +
    scale_y_continuous(expand = c(0,0),
                       limits = c(0,yLimitMax))

If you want to remove the three lines between the plots, just write this in plot2 instead:

limits = c(0, 1.03 * max(print(plot1)$data[[1]]$ymax))
Christian
  • 932
  • 1
  • 7
  • 22