Skip to contents

ggbraid provides a new stat, stat_braid(), that extends the functionality of geom_ribbon() to correctly fill the area between two alternating lines (or steps) with two different colors. ggbraid also provides a geom, geom_braid(), that wraps geom_ribbon() and uses stat_braid() by default.

Installation

You can install the development version of ggbraid from GitHub with:

# install.packages("remotes")
remotes::install_github("nsgrantham/ggbraid")

Usage

To demonstrate, let’s generate a long dataset with two alternating series.

library(ggplot2)
library(ggbraid)
library(tidyr)

set.seed(42)  # for reproducibility

n <- 21

df_long <- tibble(
  x = c(1:n, 1:n),
  y = c(rnorm(n), rnorm(n, mean = 0.5)),
  z = c(rep("a", n), rep("b", n))
)

df_long
#> # A tibble: 42 × 3
#>        x       y z    
#>    <int>   <dbl> <chr>
#>  1     1  1.37   a    
#>  2     2 -0.565  a    
#>  3     3  0.363  a    
#>  4     4  0.633  a    
#>  5     5  0.404  a    
#>  6     6 -0.106  a    
#>  7     7  1.51   a    
#>  8     8 -0.0947 a    
#>  9     9  2.02   a    
#> 10    10 -0.0627 a    
#> # … with 32 more rows

And let’s pivot the dataset wider so we can use it with geom_ribbon() and geom_braid().

df_wide <- pivot_wider(df_long, names_from = z, values_from = y)

df_wide
#> # A tibble: 21 × 3
#>        x       a       b
#>    <int>   <dbl>   <dbl>
#>  1     1  1.37   -1.28  
#>  2     2 -0.565   0.328 
#>  3     3  0.363   1.71  
#>  4     4  0.633   2.40  
#>  5     5  0.404   0.0695
#>  6     6 -0.106   0.243 
#>  7     7  1.51   -1.26  
#>  8     8 -0.0947  0.960 
#>  9     9  2.02   -0.140 
#> 10    10 -0.0627  0.955 
#> # … with 11 more rows

Now let’s draw the two series as lines and fill the area between them with a single color using geom_ribbon().

ggplot() +
  geom_line(aes(x, y, linetype = z), data = df_long) +
  geom_ribbon(aes(x, ymin = a, ymax = b), data = df_wide, alpha = 0.2) +
  guides(linetype = "none")

Can we fill the area between the two lines with two different colors? One color when the solid line is above the dashed line, and a different color when the solid line is below the dashed line?

That shouldn’t be hard. Let’s map a < b to the fill aesthetic in geom_ribbon() and…

ggplot() +
  geom_line(aes(x, y, linetype = z), data = df_long) +
  geom_ribbon(aes(x, ymin = a, ymax = b, fill = a < b), data = df_wide, alpha = 0.6) +
  guides(linetype = "none", fill = "none")

Chaos.

What happened? Is this a bug in geom_ribbon()?

No, it’s not a bug. The problem is that we haven’t dealt with line intersections properly. I call this the Unbraided Ribbon Problem.

To fix it, replace geom_ribbon() with geom_braid() from ggbraid.

ggplot() +
  geom_line(aes(x, y, linetype = z), data = df_long) +
  geom_braid(aes(x, ymin = a, ymax = b, fill = a < b), data = df_wide, alpha = 0.6) +
  guides(linetype = "none", fill = "none")
#> `geom_braid()` using method = 'line'

Articles