在闪亮的应用程序中更新观察者循环内的图

时间:2014-01-22 11:54:54

标签: r shiny

我已经在R中编写了一个模拟,我现在想用闪亮的可视化。我已将模拟的主要部分放入 observe 块中以便进行评估。在此评估过程中,即对于每次迭代,我想绘制当前状态。问题是如何实现这一点,因为在我的实际代码中,绘图的渲染只是在主要观察者被评估之后才执行。有没有办法,例如,暂停执行观察块并在更新绘图后恢复它?

不应该有更多功能从闪亮到解决这种情况,因为我可以想象我不是唯一一个想做这样的事情的人吗?!

如果你可以帮助我,那将是很好的:)

下面是服务器和ui的一些框架代码。

ui.R:

library(shiny)

shinyUI(pageWithSidebar(

  headerPanel("... Simulation"),

  sidebarPanel(
    sliderInput("epochs", 
                "Number of Epochs:", 
                min = 1,
                max = 100, 
                value = 10),
    verbatimTextOutput("curr.iter"),
    actionButton("actionB", "Action!")
  ),

  mainPanel(
    plotOutput("distPlot")
  )
))

server.R:

library(shiny)

sinus <- data.frame()

shinyServer(function(input, output) {

  dummy <- reactiveValues(iter=0)

  obsMain <- observe({
    for (i in 1:input$epochs) {
      cat(i, " ")
      x <- seq(1:input$epochs)
      y <- sin(x)
      sinus <<- data.frame(x, y)
      dummy$iter <- i
      #
      # At this time I want distPlot & curr.iter to be evaluated/updated!
      #
      Sys.sleep(1)

    }

  }, suspended=TRUE)


  obsAction <- observe({ if(input$actionB > 0) obsMain$resume() })  # Helps to avoid initial evaluation of obsMain...

  output$curr.iter <- renderText({ as.numeric(dummy$iter) })

  output$distPlot <- renderPlot({ if (dummy$iter > 1) plot(sinus, type="l") })

})

2 个答案:

答案 0 :(得分:9)

我一直在考虑这个问题。我认为正确的解决方案是使用invalidateLater来安排工作以小块的形式发生,但允许其他反应依赖关系中断我们长时间运行的过程来执行更新图形等操作。

我在https://gist.github.com/trestletech/8608815汇总了一个简短的例子。你可以用

运行它
runGist(8608815)

基本前提是我们正在进行一些长期运行的迭代计算,就像你在模拟中所做的一样,但我们在较小的块中进行,以允许其他反应在两者之间运行。我的代码执行起来非常简单,所以我可以在大约1秒钟内完成我的循环的100,000次迭代,这是我愿意等待我的应用程序以交互方式更新的时间。我想做500万次迭代,所以我安排了50个块。

每次运行100,000次迭代时,我都会更新一些反应值,这些值会产生一些其他更新,最终会在renderText中发送到我的用户界面(尽管renderPlot像你的工作完全一样)。如果您运行该应用程序,您将看到在下一个计划运行之前运行的每个块之间更新了这些响应项。

这种方法有一些开销,所以你的计算可能会减慢一点。但是在我的机器上,当在控制台上同时运行时,500万次迭代花了21秒,并且在这个延迟调度模型中花费了23秒。当然,你可以通过做更大的块来进一步推动这一点。

让我知道你的想法。我认为将它包起来可能是有意义的,要么将它包含在Shiny中,要么包含在扩展包中。

答案 1 :(得分:0)

由于您的代码中有Sys.sleep()调用,我假设您只想每秒运行一次迭代。如果是这种情况,您可以设置reactiveTimer来每秒评估代码。在该计时器内部,您将运行当前迭代的代码,然后提升dummy$iter变量。