ggplot in reactive

768 views
Skip to first unread message

Jia Kang

unread,
Mar 13, 2014, 3:28:34 PM3/13/14
to shiny-...@googlegroups.com
Hi

I am trying to create several plots using ggolot2. These plots work well if I put the ggplot code in renderPlot({ }). However, if i put the ggplot code in reactive, the final app only shows the last plot (which is the last if statement) and others are "NULL". Here is the simplified version of my code. Any suggestion on this? Thanks.

    PLOT1<-reactive({
       p<-ggplot(dt,aes_string(x=input$x, y=input$y))+geom_point(colour="blue") ## only scatter plot if x and y don't fit any of following "if" statements
         
         if(input$y %in% c('a','b','c')& input$x %in% c('g','f')){
             p+ geom_smooth(se=FALSE,linetype="dashed",colour="red", method="lm")
          }
    if(input$x %in% c('g','h') & input$y=='d'){
       p+ geom_abline(slop=1,linetype="solid",colour="black")
       }   
     })

 output$plot1 <- renderPlot({  
       print(PLOT1())  
     })  

Mike C

unread,
Mar 13, 2014, 3:54:36 PM3/13/14
to shiny-...@googlegroups.com
The last time I worked on such a "variable number of plots" problem, the solution was a bit of a workaround, sorry if it is not the most up to date solution-

max_plots=100 # before shinyServer(function(input, output) {

# I tried to trim out everything that wasn't relevant, so let me know if I clipped out something critical

output$box <- renderUI({


 input$goButtonBox # action button
 isolate({
plot_output_box <- lapply(1:boxN(), function(i) {

plotname <- paste("box", i, sep="")

plotOutput(plotname, height = 400)

})
 
# Convert the list to a tagList - this is necessary for the list of items
# to display properly.
do.call(tagList, plot_output_box)
})

})

for (i in 1:max_plots) {
local({
my_i <- i
plotname <- paste("box", my_i, sep="")
 
output[[plotname]] <- renderPlot({

dat=data() # reactive containing the data

boxplot(dat[my_i,]) #using my_i for the index

})

})

Jia Kang

unread,
Mar 13, 2014, 4:07:07 PM3/13/14
to shiny-...@googlegroups.com
The main reason I use the "reactive" instead of "renderPlot" is that I want to use the downloadHandler, which could download the reactive plot (PLOT1()). Otherwise, putting the ggplot code in renderPlot without reactive works well, where all plots were shown as I expected. I am not sure why the reactive one only shows the plot from the last statement. It seems next statement overwrote the previous statement in the reactive().

Thanks.

Jia Kang

unread,
Mar 13, 2014, 5:10:27 PM3/13/14
to shiny-...@googlegroups.com
OK, I got it fixed. The code is like

PLOT1<-reactive({
       p<-ggplot(dt,aes_string(x=input$x, y=input$y))+geom_point(colour="blue") ## only scatter plot if x and y don't fit any of following "if" statements
         
         if(input$y %in% c('a','b','c')& input$x %in% c('g','f')){
             p<-p+ geom_smooth(se=FALSE,linetype="dashed",colour="red", method="lm")
          }
    if(input$x %in% c('g','h') & input$y=='d'){
       p<-p+ geom_abline(slop=1,linetype="solid",colour="black")
       }   
    print(p) ## I think this is the key to get p update with if statements and show in the app.
     })

 output$plot1 <- renderPlot({  
   PLOT1()   ### didn't use print(PLOT1) since every time it plot, the information regarding the resulting plot (e.g. $data) is printed out in the R console.
     })  

###To download
 output$downloadPlot1<-downloadHandler(
        filename = function() {
          paste('plot', '.png', sep='')
        },
        content=function(file){
          png(file)
          print(PLOT1()) ### have to print out all the details when download the plot. I tried PLOT1() alone, and it gave an error msg: error opening file and error reading, not sure why this.
          dev.off()
        },
        contentType='image/png')

I also tried using function() instead of reactive({}). The code is the same except the downloadHandler () part, i use PLOT1() instead of print(PLOT1()).

I am not sure what is the difference between function and reactive. Any comments?


Stéphane Laurent

unread,
Mar 13, 2014, 5:15:41 PM3/13/14
to shiny-...@googlegroups.com
I am not sure what is the difference between function and reactive. Any comments?

Yes, see the Fibonacci example in the shiny tutorial: http://rstudio.github.io/shiny/tutorial/#reactivity-overview

 

Jia Kang

unread,
Mar 13, 2014, 5:28:23 PM3/13/14
to shiny-...@googlegroups.com
I will. Thanks. Just wondering, whether you could give me some common in this case.  When I use print(PLOT1()) in renderPlot() (PLOT1 is reactive conductor), every time a plot shows in the app, all details regarding plot is printed out (e.g. $data) in R console. I don't remember it happens before.

Thanks.

Stéphane Laurent

unread,
Mar 13, 2014, 7:30:22 PM3/13/14
to shiny-...@googlegroups.com
Don't use print() as the output of the reactive conductor. There's a programming error in the first version of your code. Do:

    PLOT1<-reactive({
       p<-ggplot(dt,aes_string(x=input$x, y=input$y))+geom_point(colour="blue") ## only scatter plot if x and y don't fit any of following "if" statements
        if(input$y %in% c('a','b','c')& input$x %in% c('g','f')){
             return(p+ geom_smooth(se=FALSE,linetype="dashed",colour="red", method="lm"))
          }
    if(input$x %in% c('g','h') & input$y=='d'){
       return(p+ geom_abline(slop=1,linetype="solid",colour="black"))
       }else{
         return(NULL)
        }   
     })

Stéphane Laurent

unread,
Mar 13, 2014, 7:32:12 PM3/13/14
to shiny-...@googlegroups.com
No sorry. Do:

   PLOT1<-reactive({
       p<-ggplot(dt,aes_string(x=input$x, y=input$y))+geom_point(colour="blue") ## only scatter plot if x and y don't fit any of following "if" statements
        if(input$y %in% c('a','b','c')& input$x %in% c('g','f')){
             p <- p+ geom_smooth(se=FALSE,linetype="dashed",colour="red", method="lm")
          }
    if(input$x %in% c('g','h') & input$y=='d'){
       p<- p+ geom_abline(slop=1,linetype="solid",colour="black")
       }
      return(p)
     })

Jia Kang

unread,
Mar 13, 2014, 9:51:10 PM3/13/14
to shiny-...@googlegroups.com
Yes, that's what I did in my previous post (the 4th one in this thread). However, in this way, if I want to download the plot, I have to use print(PLOT1()) instead of PLOT1() in downloadHandler(). Please see my comment below. Any thought why PLOT1() didn't work here? For other part of code, please refer to my previous post (the 4th one). Instead of using reactive(), I also tried function(). The codes are same except in the downloadHandler() part. I can use PLOT1() alone to download the plot. That's why I asked about the difference between function() and reactive(). Thanks.

 output$downloadPlot1<-downloadHandler(
        filename = function() {
          paste('plot', '.png', sep='')
        },
        content=function(file){
          png(file)
          print(PLOT1()) ### when click the download, all details was printed out in R console. I also tried PLOT1() alone, and it gave an error msg: error opening file and error reading, not sure why this.
          dev.off()
        },
        contentType='image/png')

Stéphane Laurent

unread,
Mar 14, 2014, 3:06:31 AM3/14/14
to shiny-...@googlegroups.com
Yes, that's what I did in my previous post

No, you did print(p) at the end of Plot1() instead of return(p). 

Joe Cheng

unread,
Mar 14, 2014, 3:33:09 AM3/14/14
to Stéphane Laurent, shiny-...@googlegroups.com
content=function(file){
          png(file)
          print(PLOT1())

Try instead doing

content = function(file) {
  ggsave(file, plot=PLOT1(), device=png, width=800, height=800, limitsize=FALSE)
}


On Fri, Mar 14, 2014 at 12:06 AM, Stéphane Laurent <lauren...@yahoo.fr> wrote:
Yes, that's what I did in my previous post

No, you did print(p) at the end of Plot1() instead of return(p). 

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jia Kang

unread,
Mar 14, 2014, 10:11:09 AM3/14/14
to shiny-...@googlegroups.com
Sorry for missing that. Yes, it works. Thanks. According to your comment and my workaround, here is a brief summary about this case.

In a reactive conductor (Plot1<-reactive()), use return (p) to get plot updated. And in renderPlot(), use print(Plot1()) to print out the plot and also use it in downloadHandler. If use function() instead of reactive(), since my function included print(p), in the renderPlot and download Handler, just call the function: Plot1() to print out the plot.

Thank for your help.

Jia Kang

unread,
Mar 14, 2014, 10:13:51 AM3/14/14
to shiny-...@googlegroups.com, Stéphane Laurent
Thanks. The code works. Actually, print(PLOT1()) also works when I use return(p) in the reactive conductor PLOT1(). But if I use print(p) instead, it doesn't work (i mean, print out all details of the plot when download). 

Joe Cheng

unread,
Mar 14, 2014, 10:34:31 AM3/14/14
to Jia Kang, shiny-...@googlegroups.com, Stéphane Laurent

In general, plot rendering is not something you want to do in a reactive expression; the reason is because reactive expressions cache their calculated values and only actually re-execute when one of the reactive inputs they depend on change. So if you are calling the same reactive that does a plot from two places in your code, then only one of those will have the behavior you want and the other will not get a plot rendered.

The changes that Stéphane recommended ensure that the "side effect free" calculations happen inside the reactive, while the operations that have side effects (printing a ggplot object) only occurs in the output and content functions where they always belong.

Jia Kang

unread,
Mar 14, 2014, 10:45:14 AM3/14/14
to shiny-...@googlegroups.com, Jia Kang, Stéphane Laurent
Thank you for the explanation. Quite helpful.
Reply all
Reply to author
Forward
0 new messages