Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alpha ggplot2 not working when inside a for loop structure

Tags:

r

ggplot2

I have issues iteratively adding geom_ribbon layers over a ggplot2 and making sure ribbons are transparent.

If I follow the manual/hard-coded approach:

bla<-ggplot(plot_fcst)
bla<-bla+geom_line(aes(y=Optimal,x=week))
bla<-bla+geom_ribbon(aes(ymin=get(paste("lower",bands[1]*100,sep="")),ymax=get(paste("upper",bands[1]*100,sep="")),x=week),alpha=alp[1])
bla<-bla+geom_ribbon(aes(ymin=get(paste("lower",bands[2]*100,sep="")),ymax=get(paste("upper",bands[2]*100,sep="")),x=week),alpha=alp[2])
bla<-bla+geom_ribbon(aes(ymin=get(paste("lower",bands[3]*100,sep="")),ymax=get(paste("upper",bands[3]*100,sep="")),x=week),alpha=alp[3])

Example Plot1: alpha shading behaving as expected:

Example Plot1: alpha shading behaving as expected

But if I iteratively add the ribbon layers, alpha is set to match the darkest ribbon. Therefore I don't reckon the issue is the one answered here: Alpha transparency not working in ggplot2?

bands <- seq(0.5,0.9,by=0.1)
alp <- seq(.3,.1,by=-0.05)

ggplot_fcst<-ggplot(plot_fcst)+geom_line(aes(y=Optimal,x=week))     

for(i1 in 1:length(bands)){
     ggplot_fcst<-ggplot_fcst+geom_ribbon(aes(ymin=get(paste("lower",bands[i1]*100,sep="")),ymax=get(paste("upper",bands[i1]*100,sep="")),x=week),alpha=alp[i1])
}

Example plot2: Alpha wrongly set to match highest value:

Example plot2: Alpha wrongly set to match highest value

like image 770
Sven Schaffrath Avatar asked Jan 20 '26 04:01

Sven Schaffrath


2 Answers

Your problem, as far as I can tell, has nothing to do with the alpha value. I have used the economics dataset to create some dummy data. You can see I have replicated your code below:

library(ggplot2)

bands <- seq(0.5,0.9,by=0.1)
alp <- seq(.3,.1,by=-0.05)

data("economics")

plot_fcst <- data.frame(week = economics$date, 
                        Optimal = economics$pce, 
                        lower50 = economics$pce*0.8,
                        upper50 = economics$pce/0.8, 
                        lower60 = economics$pce*0.7,
                        upper60 = economics$pce/0.7, 
                        lower70 = economics$pce*0.6,
                        upper70 = economics$pce/0.6, 
                        lower80 = economics$pce*0.5,
                        upper80 = economics$pce/0.5, 
                        lower90 = economics$pce*0.4,
                        upper90 = economics$pce/0.4)

ggplot_fcst<-ggplot(plot_fcst)+geom_line(aes(y=Optimal,x=week))     

for(i1 in 1:length(bands)){
  ggplot_fcst <- ggplot_fcst+geom_ribbon(aes(ymin=get(paste("lower",bands[i1]*100,sep="")),ymax=get(paste("upper",bands[i1]*100,sep="")),x=week),alpha=alp[i1])
}

ggplot_fcst

Now, this plot looks much like yours, only one ribbon is visible and it is the wrong alpha level. ggplot2 gives us the ability to investigate the structure of the ggplot object using the ggplot_build command. I'll not go into too much detail here as to what the resulting object contains but we are interested in the data. This provides a table for each layer in the plot with all the information that ggplot need to create that layer. Since the first layer is the line, we are interested in the lines after 1:

built <- ggplot_build(ggplot_fcst)

head(built$data[[2]])
#>      x   ymin    ymax PANEL group colour   fill size linetype alpha
#> 1 -915 202.96 1268.50     1    -1     NA grey20  0.5        1   0.3
#> 2 -884 204.20 1276.25     1    -1     NA grey20  0.5        1   0.3
#> 3 -853 206.52 1290.75     1    -1     NA grey20  0.5        1   0.3
#> 4 -823 205.16 1282.25     1    -1     NA grey20  0.5        1   0.3
#> 5 -792 207.24 1295.25     1    -1     NA grey20  0.5        1   0.3
#> 6 -762 210.32 1314.50     1    -1     NA grey20  0.5        1   0.3
head(built$data[[3]])
#>      x   ymin    ymax PANEL group colour   fill size linetype alpha
#> 1 -915 202.96 1268.50     1    -1     NA grey20  0.5        1  0.25
#> 2 -884 204.20 1276.25     1    -1     NA grey20  0.5        1  0.25
#> 3 -853 206.52 1290.75     1    -1     NA grey20  0.5        1  0.25
#> 4 -823 205.16 1282.25     1    -1     NA grey20  0.5        1  0.25
#> 5 -792 207.24 1295.25     1    -1     NA grey20  0.5        1  0.25
#> 6 -762 210.32 1314.50     1    -1     NA grey20  0.5        1  0.25

We can see that, for layer 2 (i1 = 1), the alpha is correctly set at 0.3, while layer 3 (i1 = 2) has alpha of 0.25. The ymin and and ymax columns show the problem: both ribbons have the same values. Rather than the alpha being incorrectly set to the highest level, each layer has the right alpha but the ymin and ymax variables are set to the last value in the for loop.

ggplot's lazy evaluation means that the get functions aren't called until you plot the chart, at which point it looks for the value of i1 and assigns lower90 and upper90 to all layers.

What you can do, is get rid of the get calls altogether and use aes_string with the paste calls:

ggplot_fcst<-ggplot(plot_fcst)+geom_line(aes(y=Optimal,x=week))

for(i1 in 1:length(bands)){
  ggplot_fcst <- ggplot_fcst+geom_ribbon(aes_string(ymin=paste("lower",bands[i1]*100,sep=""),ymax=paste("upper",bands[i1]*100,sep=""),x="week"),alpha=alp[i1])
}

ggplot_fcst

like image 148
Eumenedies Avatar answered Jan 21 '26 18:01

Eumenedies


To piggyback on @Eumenedies's answer to use aes_string() instead of get(), consider Map (or its parent call, mapply) since you are passing equal length vectors, lower bands, upper bands, and alp into a same process. Notice two the use of paste0 and vectorizing it across bands.

ggplot_fcst <- ggplot(plot_fcst) + geom_line(aes(y=Optimal,x=week)) +
               Map(function(l, u, a) geom_ribbon(aes_string(ymin=l, ymax=u, x='week'), alpha=a),
                   paste0("lower",bands*100), paste0("upper",bands*100), alp)

ggplot_fcst
like image 41
Parfait Avatar answered Jan 21 '26 18:01

Parfait



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!