vpipkt
vpipkt

Reputation: 1707

ggplot ribbon with constant `ymin` and `ymax`

I have the following simple line graph with ribbons denoting thresholds of interest along the y-axis.

library(ggplot2)
main.df <- data.frame(time = c(1:20, 1:20), 
                level = runif(40), 
                type = c(rep('A', 20), rep('B', 20)))

gg <- ggplot(main.df, aes(x = time, y = level, colour = type)) 
gg + geom_ribbon(ymin = 0.1, ymax = 0.25, fill = 'green') + 
    geom_ribbon(ymin = 0.25, ymax = 0.5, fill = 'yellow') + 
    geom_ribbon(ymin = 0.5, ymax = 0.95, fill = 'red') + 
    geom_line()

Desired ggplot but not desired method

I have made many attempts to set the data property of geom_ribbon to give me more flexibility and clean up my code. Here is one such example.

rib.df <- data.frame(low = c(0.1, 0.25, 0.5), high = c(0.25, 0.50, 0.95), 
                     lab = c('green', 'yellow', 'red'))

gg + geom_ribbon(data = rib.df,   
      aes(ymin = low, ymax = high, fill = lab), 
      inherit.aes=FALSE) + geom_line()

This particular one has the error that the aesthetic x is missing. Which interestingly was not a problem in the prior example. I have tried setting the x and y in it seems every combination of inside the aes and not. I am also unclear as to why sometimes the geom_ribbon wants a y when it is not a required aesthetic.

My question in summary: how to call geom_ribbon setting the data argument to achieve a result similar to the above manual calls?

I will also be glad to learn about efficient ways to achieve a similar visual result using other geoms.

Upvotes: 1

Views: 3147

Answers (1)

Gregor Thomas
Gregor Thomas

Reputation: 145775

I would do it like this:

rib2 = rbind(rib.df, rib.df)
rib2$x = rep(c(-Inf, Inf), each = nrow(rib.df))

gg + geom_ribbon(data = rib2,   
      aes(x = x, ymin = low, ymax = high, fill = lab),
      inherit.aes=FALSE) + 
   geom_line()

Ribbons are made to have ymin and ymax values that change for various x values. In this case where you're just drawing rectangles, you could simplify by using geom_rect, in which case you could set xmin = -Inf, xmax = Inf outside of aes() and use the rib.df from your question. This way you don't have to double the data to show the ymin and ymax are the same at the min and max x values.

gg + geom_rect(data = rib.df,   
          aes(ymin = low, ymax = high, fill = lab),
          xmin = -Inf, xmax = Inf,
          inherit.aes=FALSE) +
    geom_line()

Upvotes: 5

Related Questions