Colour area above y-lim in geom_ribbon

Viewed 531

How can I colour the area between y=0 and the datapoints in green? At the moment it is colouring the whole y [0:1]

Variable <- c(1,2,3,4,5,6,7)
    value <- c(-0.26, -0.10,0.98,0.52, 0.31, -0.63, -0.70)
    df <- data.frame(Variable, value)
    
ggplot(df, aes(x = Variable, y = value)) +
  geom_ribbon(aes(ymin=pmin(value,1), ymax=0), fill="red", col="red", alpha=0.5) +
  geom_ribbon(aes(ymin=0, ymax=pmax(df$value,1)), fill="green", col="green", alpha=0.5) +
  geom_hline(aes(yintercept=0), color="black") + 
  theme_bw() +
  geom_point()

enter image description here

2 Answers

There's really not a direct way to do this in ggplot2 as far as I know. If you didn't want the transparency, then it would be pretty easy just to draw a rectangle in the background and then draw the ribbon on top.

ggplot(df, aes(x = Variable, y = value)) +
  geom_rect(aes(xmin=min(Variable), xmax=max(Variable), ymin=0, ymax=1), fill="green") + 
  geom_ribbon(aes(ymin=pmin(value,1), ymax=0), fill="red", col="red") +
  geom_hline(aes(yintercept=0), color="black") + 
  theme_bw(base_size = 16) +
  geom_point()

enter image description here

But if you need the transparency, you're going to need to calculate the bounds of that region which is messy because the points where the line crosses the axis are not in your data, you would need to calculate those. Here's a function that finds the places where the region crosses the axis and keeps track of the top points

crosses <- function(x, y) {
  outx <- x[1]
  outy <- max(y[1],0)
  for(i in 2:length(x)) {
    if (sign(y[i-1]) != sign(y[i])) {
      outx <- c(outx, -y[i-1]*(x[i]-x[i-1])/(y[i]-y[i-1])+x[i-1])
      outy <- c(outy, 0)
    }
    if (y[i]>0) {
      outx <- c(outx, x[i])
      outy <- c(outy, y[i])
    } 
  }
  if (y[length(y)]<0) {
    outx <- c(outx, x[length(x)])
    outy <- c(outy, 0)
  }
  data.frame(x=outx, y=outy)
}

Basically it's just doing some two-point line formula stuff to calculate the intersection.

Then use this to create a new data frame of points for the top ribbon

top_ribbon <- with(df, crosses(Variable, value))

And plot it

ggplot(df, aes(x = Variable, y = value)) +
  geom_ribbon(aes(ymin=pmin(value,1), ymax=0), fill="red", col="red", alpha=0.5) +
  geom_ribbon(aes(ymin=y, ymax=1, x=x), fill="green", col="green", alpha=0.5, data=top_ribbon) +
  geom_hline(aes(yintercept=0), color="black") + 
  theme_bw(base_size = 16) +
  geom_point()

enter image description here

I found a related-post from 3 years ago How can I fill the space between values(geom_line) and an intercept with ggplot2? Different Colors for values over and under intercept

For which I ran the following code:

ggplot(df, aes(x=Variable,y=value)) +
geom_ribbon(aes(ymin=pmin(df$value,0), ymax=0), fill="blue", col="blue", alpha=0.5) +
geom_ribbon(aes(ymin=0, ymax=pmax(df$value,0)), fill="green", col="green", alpha=0.5) +
geom_line(aes(y=0))

And worked like this: Although note the areas are a bit odd at the intersect! enter image description here

Related