Search code examples
rfor-loopggplot2vectorization

ggplot2 - shade several areas under the curve using a loop


I want to shade different areas under a curve with different colors. Trying a vectorized version or a for() loop did not work in this case (it worked with plotting the accompanying annotation, not shown here).

Instead of the syntax of the six function calls shown below, I used the line

highlight_area(tib, 1:6) and got the following error message:

Error in `ggplot2::geom_area()`:
! Problem while setting up geom aesthetics.
ℹ Error occurred in the 2nd layer.
Caused by error in `check_aesthetics()`:
! Aesthetics must be either length 1 or the same as the data (3003)
✖ Fix the following mappings: `fill`

Here is my Reprex:

library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(tibble)

x_lower <-  seq(0.006, 0.0085, 0.0005)
x_upper <-  seq(0.0065, 0.009, 0.0005)
area_colors <- c("gray90", "gray80", "gray70", "gray50", "gray40", "black") 

highlight_area <- function(df, i) {
        geom_area(data = df |>
           filter(x >= x_lower[i] & x <  x_upper[i]),
           fill = area_colors[i])
}

tib <- 
    tibble(x = seq(0.006, 0.009, length.out = 6000),
                   y = dbeta(x, 300, 39700))

ggplot(tib, aes(x = x, y = y)) +
geom_line() +
highlight_area(tib, 1) +
highlight_area(tib, 2) +
highlight_area(tib, 3) +
highlight_area(tib, 4) +
highlight_area(tib, 5) +
highlight_area(tib, 6)

Created on 2023-09-26 with reprex v2.0.2


Solution

  • Using lapply you could do:

    library(ggplot2)
    
    ggplot(tib, aes(x = x, y = y)) +
      geom_line() +
      lapply(1:6, highlight_area, df = tib)
    

    And as a second option here is an approach which does requires any loops but instead uses cut to bin your variable:

    library(dplyr)
    
    tib <- tib |>
      mutate(
        x_cut = cut(x,
          breaks = seq(.006, .009, .0005),
          right = FALSE, labels = area_colors
        )
      )
    
    ggplot(tib, aes(x = x, y = y)) +
      geom_line() +
      geom_area(aes(fill = x_cut)) +
      scale_fill_identity()