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

use*_*352 9 r shiny

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

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

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

下面是服务器和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")
  )
))
Run Code Online (Sandbox Code Playgroud)

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") })

})
Run Code Online (Sandbox Code Playgroud)

Jef*_*len 10

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

我在https://gist.github.com/trestletech/8608815上放了一个简单的例子.你可以运行它

runGist(8608815)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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


Jef*_*len 0

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