40

I'm having a problem with geom_bars wherein the bars are not rendered when I specify limits on the y-axis. I believe the following should reproduce the problem:

data <- structure(list(RoleCond = structure(c(1L, 1L, 2L, 2L), .Label = c("Buyer", "Seller"), class = "factor"), 
                   ArgCond = structure(c(1L, 2L, 1L, 2L), .Label = c("No Argument", "Argument"), class = "factor"), 
                   mean = c(2210.71428571429, 2142.70833333333, 2282.40740740741, 2346.2962962963), 
                   se = c(20.1231042081511, 16.7408757749718, 20.1471554637891, 15.708092540868)), 
                   .Names = c("RoleCond", "ArgCond", "mean", "se"), row.names = c(NA, -4L), class = "data.frame")

library(ggplot2)    
ggplot(data=data, aes(fill=RoleCond, y=mean, x=ArgCond)) + 
      geom_bar(position="dodge", stat="identity") + 
      geom_errorbar(limits, position=dodge, width=0.1, size=.75) + 
      scale_y_continuous(limits=c(2000,2500))

which gives me this

no bars

The same code without the limits specified works fine. The geom_errorbar() doesn't seem to be related to the problem, but it does illustrate where the bars should be showing up.

I've tried using coord_cartesian(ylim=c(2000,2500)) which works for limiting the yaxis and getting the bars to display, but the axis labels get messed up and I don't understand what I'm doing with it.

Thanks for any suggestions! (I'm using R 2.15.0 and ggplot2 0.9.0)

Sam Swift
  • 913
  • 1
  • 11
  • 17
  • related https://stackoverflow.com/questions/35324892/ggplot2-setting-geom-bar-baseline-to-1-instead-of-zero – tjebo May 26 '22 at 20:54

4 Answers4

64

You could try, with library(scales):

+ scale_y_continuous(limits=c(2000,2500),oob = rescale_none)

instead, as outlined here.

Matifou
  • 7,968
  • 3
  • 47
  • 52
joran
  • 169,992
  • 32
  • 429
  • 468
  • 6
    See also Hadley's response: "I think using squish would be canonical. And this is a valid use, but it does create a deceiving graph." (To expand on the second point: it is generally *not* considered a good idea to use bars when the graph baseline is not at zero ... – Ben Bolker Apr 28 '12 at 16:10
  • 6
    Thanks joran and Ben. Note for anyone finding this, I had to load 'library(scale)' in order to use the oob parameter. I definitely share the concerns with baselines other than 0 on bar graphs in general, but in this case the only possible range of responses was 2000-2500, so I think it illustrates the differences fairly (also, it's what people expect and I'm not feeling brave enough to "do the right thing" at this moment) – Sam Swift Apr 28 '12 at 16:35
  • 4
    @SamSwift, I believe it's `library(scales)` (with an "s"). – A5C1D2H2I1M1N2O1R2T1 Apr 28 '12 at 16:42
  • Well, "right thing" could be as simple as using `geom_point` instead of `geom_bar`, in which case there is less conventional expectation that the baseline is at zero ... – Ben Bolker Apr 28 '12 at 17:42
  • I believe another option would be to use `oob = squish` – tjebo Feb 16 '22 at 11:12
12

Adding an answer for my case which was slightly different in case someone comes across this:

When using position="dodge", the bars get horizontally resized automatically to fill space that is often well beyond the limits of the data itself. As a result, even if both your x-axis and y-axis limits are limits=c(min-1, max+1, for certain data sets, the position="dodge" might resize it beyond that limit range, causing the bars to not appear. This might even occur if your limit floor is 0, unlike the case above.

Using oob=rescale_none in both scale_y_continous() AND scale_x_continuous() fixes this issue by simply cutting off the resizing done by position="dodge".

As per earlier comment, it requires package:scales so run library(scales) first.

Hope this helps someone else where the above answers only get you part way.

Mekki MacAulay
  • 1,727
  • 2
  • 12
  • 23
12

This worked for me based on the link shared previously.

p + coord_cartesian(ylim=c(5,15))
gcamargo
  • 3,683
  • 4
  • 22
  • 34
2

This is a community wiki essentially copying user teunbrand's canonical answer to that topic - for more visibility added to this larger thread.

Consider the following plot (geom_col() is equivalent to geom_bar(stat = "identity")):

df <- data.frame(x = letters[1:7],
                 y = 1:7)

g <- ggplot(df, aes(x, y)) +
  geom_col()
g

enter image description here

You can clearly see that the bars look like rectangles. Checking the underlying plot data, confirms that the bars are parameterised as rectangles with xmin/xmax/ymin/ymax parametrisation:

> layer_data(g)
  x y PANEL group ymin ymax xmin xmax colour   fill size linetype alpha
1 1 1     1     1    0    1 0.55 1.45     NA grey35  0.5        1    NA
2 2 2     1     2    0    2 1.55 2.45     NA grey35  0.5        1    NA
3 3 3     1     3    0    3 2.55 3.45     NA grey35  0.5        1    NA
4 4 4     1     4    0    4 3.55 4.45     NA grey35  0.5        1    NA
5 5 5     1     5    0    5 4.55 5.45     NA grey35  0.5        1    NA
6 6 6     1     6    0    6 5.55 6.45     NA grey35  0.5        1    NA
7 7 7     1     7    0    7 6.55 7.45     NA grey35  0.5        1    NA

Now consider the following plot:

g2 <- ggplot(df, aes(x, y)) +
  geom_col() +
  scale_y_continuous(limits = c(1, 7))

enter image description here

This one is empty, and reflects the case you have posted. Inspecting the underlying data yields the following:

> layer_data(g2)
  y x PANEL group ymin ymax xmin xmax colour   fill size linetype alpha
1 1 1     1     1   NA    1 0.55 1.45     NA grey35  0.5        1    NA
2 2 2     1     2   NA    2 1.55 2.45     NA grey35  0.5        1    NA
3 3 3     1     3   NA    3 2.55 3.45     NA grey35  0.5        1    NA
4 4 4     1     4   NA    4 3.55 4.45     NA grey35  0.5        1    NA
5 5 5     1     5   NA    5 4.55 5.45     NA grey35  0.5        1    NA
6 6 6     1     6   NA    6 5.55 6.45     NA grey35  0.5        1    NA
7 7 7     1     7   NA    7 6.55 7.45     NA grey35  0.5        1    NA

You can see that the ymin column is replaced by NAs. This behaviour depends on the oob (out-of-bounds) argument of scale_y_continuous(), which defaults to the scales::censor() function. This censors (replaces with NA) any values that are outside the axis limits, which includes the 0 which should be the ymin column. As a consequence, the rectangles can't be drawn.

There are two ways to work around this. One candidate is indeed as Magnus suggested to use the ylim argument in the coord_cartesian() function:

ggplot(df, aes(x, y)) +
  geom_col() +
  coord_cartesian(ylim = c(1, 7))

enter image description here

Specifying the limits inside a coord_* function causes the graphical objects to be clipped. You can see this in action when you turn the clipping off:

ggplot(df, aes(x, y)) +
  geom_col() +
  coord_cartesian(ylim = c(1, 7), clip = "off")

enter image description here

The other option is to use an alternative oob argument in the scale_y_continuous, for example scales::squish:

g3 <- ggplot(df, aes(x, y)) +
  geom_col() +
  scale_y_continuous(limits = c(1, 7), 
                     oob = scales::squish)
g3

enter image description here

What this does, is that it replaces any value outside the limits by the nearest limit, e.g. the ymin of 0 becomes 1:

> layer_data(g3)
  y x PANEL group ymin ymax xmin xmax colour   fill size linetype alpha
1 1 1     1     1    1    1 0.55 1.45     NA grey35  0.5        1    NA
2 2 2     1     2    1    2 1.55 2.45     NA grey35  0.5        1    NA
3 3 3     1     3    1    3 2.55 3.45     NA grey35  0.5        1    NA
4 4 4     1     4    1    4 3.55 4.45     NA grey35  0.5        1    NA
5 5 5     1     5    1    5 4.55 5.45     NA grey35  0.5        1    NA
6 6 6     1     6    1    6 5.55 6.45     NA grey35  0.5        1    NA
7 7 7     1     7    1    7 6.55 7.45     NA grey35  0.5        1    NA

Another thing you could do is provide a custom function to the oob argument, that simply returns it's input. Since by default, clipping is on, this reflects the coord_cartesian(ylim = c(1,7)) case:

ggplot(df, aes(x, y)) +
  geom_col() +
  scale_y_continuous(limits = c(1, 7), 
                     oob = function(x, ...){x})
tjebo
  • 21,977
  • 7
  • 58
  • 94