2

I am writing an shiny app in which contains an stochastic function generates four objects - one plot and three tables. However, I want to render each object in different tabs without being executing the function four times since this stochastic function will generates four different versions. I have been researched online and find a lot people recommend "reactive()" but I still don't quite understand how to apply it to my problem. How can I use those four objects on rendering with only one execution on the function?

My "server.R" structure basically looks like the below:

shinyServer(function(input, output) {

     stochastic_function() {
          ...
       plot1 <- ...
       table1 <- ...
       table2 <- ...
       table3 <- ...
       result <- list(plot, table1, table2, table3)
     return(result)
     }

 output$plot <- renderPlot({

})

 output$table1 <- renderTable({

})

 output$table2 <- renderTable({

})

 output$table3 <- renderTable({

})

...

So, I have tried something like below for the stochastic function:

model <- eventReactive(input$goButton, {
        reactive(WG_Model(cdata = cdata(), # load data from outside env
                          sdata = sdata(), # load data from outside env
                          N = input$n, 
                          end_date = input$end_date,
                          cpx_goal = input$cpx,
                          N_new = input$n2, 
                          end_date_new = input$end_date2,
                          spend_range = input$s_range,
                          spend_incr = input$s_incr
                          ) 
                )
})

The idea is to add an "GoButton" to initiate the function and then save all outputs in a reactive fun(). So I can render each output with:

 output$plot <- renderPlot({
   model$gplot
})

 output$table <- renderTable({
   model$table
})

# Render UI section 
 output$tb <- renderUI({
  tabsetPanel(tabPanel("About Model", plotOutput("plot")), 
              tabPanel("About Model", tableOutput("table")))

  })

However, I only got "Error: object of type 'closure' is not subsettable" in the UI output. Which part did I miss?

Mark Li
  • 429
  • 1
  • 7
  • 21
  • Hey @warmoverflow :) I haven't tried anything yet but I am thinking using "model <- reactive(function() {..})" to store all outputs into a list. Then, render each output with list indexing like "render...(model()$plot)". I am trying it. – Mark Li May 06 '16 at 15:37
  • @warmoverflow I have tested it and got error message. I updated the post with the result. Please let me know if you have an idea. Thanks!:) – Mark Li May 06 '16 at 16:15
  • Within `reactiveEvent` you don't need anymore `reactive`. `model` should return a value which you can access with `model()`. You forgot to add brackets within your `render*` functions. (it should be `model()$table`) – Michal Majka May 06 '16 at 16:23
  • @UnnamedUser is correct, you need to use `model()`, but also this won't get what you want, as I think every time you call `model()` it runs the code again. – Xiongbing Jin May 06 '16 at 16:57
  • @warmoverflow I see. I followed the solution provided by "@UnnamedUser". Though the function is successfully initiated and finished, I didn't get any plot or table on the UI. ;( – Mark Li May 06 '16 at 17:03
  • @warmoverflow after calling `model()` it won't re-run the code. @MarkLi does model()$table returns data for a table? – Michal Majka May 06 '16 at 17:23
  • @UnnamedUser Yes, the model()$table does return! Thanks! Yet, the model()$plot didn't. The "plot" which is an ggplot2 object seems not work well with "renderPlot()". – Mark Li May 06 '16 at 17:48
  • @MarkLi Do you print your ggplot object with `print` function? – Michal Majka May 06 '16 at 17:50
  • @MarkLi I'll assume that you saved your ggplot in the variable `p`. In the `renderPlot` function don't use `print(model()$p)` because it may be the cause. Return the plot just with `model()$p`. – Michal Majka May 06 '16 at 17:53

1 Answers1

3

If your model() is a list and contains data for all tables and a plot, it should work as in my example.

In this app, after pressing a button, a random number and data for a table and a plot are generated. Then the number, data for table and a plot are returned as a list and rendered with appropriate render* functions.

This app illustrates that the model function won't be re-run after accessing it with model() in other reactive functions.

However, there is an odd thing...the plot is not always rendered. You sometimes have to click the button few times to get the plot. The table is working always.


library(shiny)

ui <- shinyUI(fluidPage(
   br(),
   actionButton("numb", "generate a random numbers"),
   br(),
   br(),
   verbatimTextOutput("text"),
   plotOutput("plot"),
   tableOutput("table")
))

server <- shinyServer(function(input, output) {

  model <- eventReactive(input$numb, {
    # draw a random number and print it
    random <- sample(1:100, 1)
    print(paste0("The number is: ", random))

    # generate data for a table and plot
    data <- rnorm(10, mean = 100)
    table <- matrix(data, ncol = 2)

    # create a plot 
    Plot <- plot(1:length(data), data, pch = 16, xlab ="", ylab = "")

    # return all object as a list
    list(random = random, Plot = Plot, table = table)
  })

   output$text <- renderText({
     # print the random number after accessing "model" with brackets.
     # It doesn't re-run the function.
     youget <- paste0("After using model()$random you get: ", model()$random,
                      ". Compare it with a value in the console")
     print(youget)
     youget
   })

   output$plot <- renderPlot({
     # render saved plot
     model()$Plot
   })

   output$table <- renderTable({

     model()$table
   })
})


shinyApp(ui = ui, server = server)
Michal Majka
  • 5,332
  • 2
  • 29
  • 40
  • Yes! It works perfectly! Thank you for putting together all the details! – Mark Li May 06 '16 at 17:55
  • Glad to hear :) Was the `print` function the cause of not rendering gg-plot? – Michal Majka May 06 '16 at 17:58
  • 1
    No, it was just a small typo.;P Thanks! Oh by the way, a quick question just in case you know the answer - while running the model() function, it prints a lot of log information by using "print("xxx")" in console. Is there a way I can render those "Print()" log information to the UI? – Mark Li May 06 '16 at 18:07
  • You could save log informations ("sentences") in one string separating each one with \n, return it as an element of the list and print it with `cat` (renderPrint). You could store each "sentence" in `reactiveValues` too. – Michal Majka May 06 '16 at 18:22
  • 1
    Yes, this could be a way but I want to show user the log text while the model() is running. Since it is an optimization search function, I would like user to see what's happening while the model() is running. Is it possible? – Mark Li May 06 '16 at 18:29
  • Ohh I see, you probably use `optim` :) It is a very good and interesting question! I did a small research and found [this](http://stackoverflow.com/questions/30474538/possible-to-show-console-messages-written-with-message-in-a-shiny-ui). There is a function `withCallingHandlers` which can help you with it :) – Michal Majka May 06 '16 at 18:40
  • 1
    It is not possible (or I am not aware of) any method that can output message to UI while a reactive process is running (output will appear after the reactive process ends). The root cause is that both Shiny and R are single-threaded, and it cannot do two tasks in the same time. – Xiongbing Jin May 06 '16 at 20:01
  • 1
    @UnnamedUser Thank you for sharing! This is very helpful! Why sometimes there is nothing happened after I hit "Go!" button and after a while it starts to run? – Mark Li May 06 '16 at 20:02
  • 1
    @warmoverflow Gotcha - Makes sense. Thanks for clarification!:) – Mark Li May 06 '16 at 20:03