0

I'm writing an R shiny app which should allow the user to create customisable plots of some data. The idea is that my app offers a "create new plot" button, which renders the plot and stores it in a reactive. A renderUI function "watches" this list and renders all plots in that reactive.

I found a couple of related questions r-markdown-shiny-renderplot-list-of-plots-from-lapply or shiny-r-renderplots-on-the-fly which however did not really help in my case. I hope I didn't miss a good answer somewhere (which I would assume there is because I think this is not a rare use case).

When implementing, I noticed a strange behaviour: When there is only one plot to be shown, everything works well. However, when I have n (n>1) plots, instead of rendering plot 1, plot 2, ..., plot n, the app only showed n times the plot n.

See my example app below. I simplified the problem by just letting the user choose the number of plots to be displayed. The renderUI function then has a loop creating thees plots in a variable p and then calls renderPlot(p). I assume shiny does some caching and for some reason fails to recognise that p changes in the loop?!

I found a workaround by replacing the renderPlot(p) by do.call("renderPlot", list(expr = p). This does the job but I'm still curious to learn why the direct renderPlot does not work.

Here is my example app:

library(shiny)
library(ggplot2)


# Define UI
ui <- shinyUI(fluidPage(
  titlePanel("renderPlot Test"),

  sidebarLayout(
    sidebarPanel(
      numericInput(inputId = "n", label = "Number of Plots", value = 1L, min = 1L, max = 5L, step = 1L),
      checkboxInput(inputId = "use_do.call", label = "use 'do.call'", value = FALSE)
    ),

    mainPanel(
      uiOutput("show_plots")
    )
  )
))

# Define server logic
server <- shinyServer(function(input, output) {

  output$show_plots <- renderUI({
    ui <- tags$div(tags$h4("Plots"))

    for( i in 1:input$n ) {
      p <- ggplot() + ggtitle(paste("plot", i))
      if( input$use_do.call ) { # this works
        ui <- tagAppendChild(ui, do.call("renderPlot", args=list(expr=p, width = 200, height = 200)))
      } else {                  # this doesn't ...
        ui <- tagAppendChild(ui, renderPlot(p, width = 200, height = 200))
      }
    }
    return(ui)
  })
})

# Run the application
shinyApp(ui = ui, server = server)
  • I think this is a pre-Shiny issue. The approach likely overwrites each graphical device over the previous one. Each device may in a sense be rendered, but only for a fraction of a second as it's replaced by the subsequent call, and only the last call is not overwritten. – JonMinton Aug 17 '18 at 10:58
  • For example: `png("example.png"); for (p in seq_along(graphs)) {print(p)}; dev.off()` - I suspect `example.png` would only contain the final graph in the list of graphs. – JonMinton Aug 17 '18 at 10:59
  • Hi Jon, thanks for your comments. I don't think your explanations apply to this problem because ggplot does not draw anything (unlike png()) but creates a ggplot object which has to be rendered by renderPlot. Also I would not understand why the do.call version should work then? – Axel Facius Aug 19 '18 at 13:39

1 Answers1

0

I agree with @JonMinton, and I've had the same problem. I've found that when I reuse the same variable to save the plots and render them (such as what you do with p), the plots get overwritten by the next plot and only the final plot is copied n times like you said.

To get around this, I define a new variable for each plot, which may not be sustainable for your project, but it is a workaround.

Glen Smith
  • 146
  • 5