Euler_Salter
Euler_Salter

Reputation: 3561

R ggplot2 geom_ribbon: shade/coloring area bounded by two crossing lines on the sides when no line is below and no line is above

Problem: I want to color betwen the lines

Trick title, I know. I have three lines which look like this fig1 I want to shade/color the area between the green and the red one. Basically the red and the green lines represent boundaries of some sort of confidence interval for the blue line. I can only colour half of it for now, and that's because the blue is below the green before intersection, and the green is below the blue after intersection. However, I cannot color the other half because the blue and the red one are divergent.

Is there a simple way to color between two almost vertical lines like this?

I tried geom_ribbon, but didn't get anywhere. Here is my current code, coloring only half.

Minimal Working Example

library(ggplot2)
# Lines to plot
blue_line <- function(x) 28.6*x - 51
red_line <- function(x) -16*x + 28
green_line <- function(x) 5.5*x-10
# Data to plot
x <- seq(from=1, to=4, length.out=200)
df <- data.frame(x=x, yblue=blue_line(x), yred=red_line(x), ygreen=green_line(x))
ggplot(data=df, aes(x=x)) + 
  geom_ribbon(aes(x=x, ymin=ygreen, ymax=yblue), fill="grey80") +
  geom_line(aes(x=x, y=yblue), color="blue") + 
  geom_line(aes(x=x, y=yred), color="red") + 
  geom_line(aes(x=x, y=ygreen), color="green") + 
  coord_cartesian(xlim=c(-3.5, 8), ylim=c(-4, 12))

which produces fig2

Second Minimal Working Example

This code works, however notice how tedious, repetitive and just plain stupid it is. Hopefully there's a much better way.

library(ggplot2)
# Lines to plot
blue_line <- function(x) 28.6*x - 51
red_line <- function(x) -16*x + 28
green_line <- function(x) 5.5*x-10
# Data to plot
x <- seq(from=0.9, to=4.2, length.out=200)
# Below
x_below <- seq(from=0.9, to=2.06, length.out=200)
y_below <- rep(-5, 200)
y_below_above <- c(green_line(x_below[x_below <= 1.76744]), red_line(x_below[x_below > 1.76744]))
# Above
x_above <- seq(from=-2.5625, to=4.181818, length.out=200)
y_above <- rep(13, 200)
y_above_below <- c(red_line(x_above[x_above<=1.76744]), green_line(x_above[x_above>1.76744]))
df <- data.frame(x=x, yblue=blue_line(x), yred=red_line(x), ygreen=green_line(x),
                 ybelow=y_below, xbelow=x_below, y_below_above=y_below_above,
                 xabove=x_above, yabove=y_above, y_above_below=y_above_below)
ggplot(data=df, aes(x=x)) + 
  geom_ribbon(aes(x=xbelow, ymin=ybelow, ymax=y_below_above), fill="grey80") + 
  geom_ribbon(aes(x=xabove, ymin=y_above_below, ymax=yabove), fill="grey80") +
  geom_line(aes(x=x, y=yblue), color="blue") + 
  geom_line(aes(x=x, y=yred), color="red") + 
  geom_line(aes(x=x, y=ygreen), color="green") + 
  coord_cartesian(xlim=c(-3.5, 8), ylim=c(-4, 12))

producing the desired fig3

Upvotes: 2

Views: 440

Answers (1)

Ben
Ben

Reputation: 30474

Besides your example with data manipulation, I am not aware of how to fill using geom_ribbon from xmin to xmax without coord_flip as mentioned here.

However you can use geom_polygon to create a filled region between two lines as follows:

poly_df <- rbind(setNames(df[, c(1,3)],c('x','y')),
                 setNames(df[, c(1,4)],c('x','y')))

ggplot(data=df, aes(x=x)) + 
  geom_line(aes(y=yblue), color="blue") + 
  geom_line(aes(y=yred), color="red") + 
  geom_line(aes(y=ygreen), color="green") + 
  coord_cartesian(xlim=c(-3.5, 8), ylim=c(-4, 12)) +
  geom_polygon(data = poly_df, aes(x = x,y = y), fill = "lightblue", alpha = 0.25)

geom_polygon example to fill between two lines

Upvotes: 1

Related Questions