rggplot2gradientgeom-ribbonggdist

How do I make a gradient with geom_lineribbon if I don't have sample or distribution, but know y, ymin and ymax?


I'm trying to plot a predicted line with its uncertainty band around it, but I want the band to have a gradient (dark close to line, light at end of band). I found that geom_lineribbon() was developed for this, but it seems that this only works if I have sample data or an analytical distribution. I see that it accepts the aesthetics ymin and ymax, with which I managed to make the band, but I can't get the gradient in the band. Does anyone know how to do this?

The (example) data contains the following variables:

for example:

x <- seq(0.2, 2.5, by = 0.1)
y <- 2*x +1
ymin <- 1.5*x + 0.5
ymax <- 1.5*x + 2.5

df <- data.frame(x = x,
                 y = y,
                 ymin = ymin,
                 ymax = ymax)

Then I can make the line and uncertainty band using the following code, but it only gives me a grey band, no gradient:

ggplot(data = df,
         mapping = aes(x = x, y = y,
                       ymin = ymin, ymax = ymax)) + 
  geom_lineribbon() +
  scale_fill_brewer() 

I was hoping that the scale_fill_brewer() would include some colors, but it doesn't. I think I need to specify the .width, but I don't know how to do that if I don't have a sample/distribution and only the boundaries. I had expected that geom_lineribbon() could do this as it takes the aesthetics ymin and ymax, but I can't find examples of this, and various combinations of fill / fill_ramp that I tried did't work.


Solution

  • You could base the ribbon on the normal distribution, with the ymax and ymin values arbitrarily at 2 standard deviations. Then simply overlay many ribbons with a small transparency value:

    library(ggplot2)
    
    ggplot(df, aes(x, y)) +
      lapply(qnorm(seq(0.5, pnorm(2), length = 100))/2, function(i) {
        geom_ribbon(aes(ymin = y - i*(y - ymin), ymax = y + i*(ymax - y)),
                    fill = "blue4", alpha = 0.01)
      }) +
      geom_line(color = "blue4", linewidth = 1) +
      theme_minimal(base_size = 16)
    

    enter image description here

    This works with symmetrical limits:

    df <- data.frame(x = seq(0, 4*pi, length = 100),
                     y = sin(seq(0, 4*pi, length = 100)),
                     ymax = sin(seq(0, 4*pi, length = 100)) + 0.5,
                     ymin = sin(seq(0, 4*pi, length = 100)) - 0.5)
    

    enter image description here

    Or asymmetrical limits:

    df <- data.frame(x = seq(0, 4*pi, length = 100),
                     y = sin(seq(0, 4*pi, length = 100)),
                     ymax = 1.1, ymin = -1.1)
    

    enter image description here