【问题标题】:R Shiny: Selective outputs assignment inside loops and IF statementsR Shiny:循环和 IF 语句内的选择性输出分配
【发布时间】:2018-09-29 07:06:31
【问题描述】:

我有一个闪亮的应用程序,它调用一个在每次迭代中迭代生成一个图形的脚本。我需要显示每个图并尝试使用 recordPlot 将每个图保存到列表中并单独调用每个元素,但应用程序稍后无法识别这些对象。然后我还尝试在 IF 语句中包含不同的输出,但我的算法只为所有输出生成最后一个图,就像 IF 语句被忽略了,我不知道如何处理它。这是我的代码的简化:

library(shiny)
ui <- fluidPage(
  # Main panel for displaying outputs ----
    mainPanel(
      actionButton("exec", "Start!!"),
      tagList(tags$h4("First iteration:")),
              plotOutput('PlotIter1'),
              tags$hr(),
              tagList(tags$h4("Second iteration:")),
              plotOutput('PlotIter2'),
              tags$hr(),
              tagList(tags$h4("Third iteration:")),
              plotOutput('PlotIter3'),
              tags$hr())
  )

server <- function(input, output) {
  ii <- 1
observeEvent(input$exec,{ 

  continue <- TRUE
  while(continue==TRUE){
    if(ii == 1){
      output$PlotIter1<-renderPlot({
        plot(rep(ii,50),main=ii)
      })
    }
    if(ii == 2){
      output$PlotIter2<-renderPlot({
        plot(rep(ii,50),main=ii)
      })
    }
    if(ii == 3){
      output$PlotIter3<-renderPlot({
        plot(rep(ii,50),main=ii)
      })
    }
    ii <- ii+1
    if(ii == 4){continue <- FALSE}
  }
  })
}

shinyApp(ui, server)

编辑:

通过使用 r2evansGregor de Cillia 提供的 local() 方法,问题得到了部分解决,但是将 server() 更改为更接近我的,(将 IF 语句替换为其他策略 FAPP 等效项)包括一些在每个绘图之间进行计算,问题仍然存在,并且最后一个数据被绘制在所有三个绘图中。

server <- function(input, output) {
  y=rnorm(10,20,2)
  for (i in 1:3) {
    local({
      thisi <- i
      plotname <- sprintf("PlotIter%d", thisi)
      output[[plotname]] <<- renderPlot({
        plot(y, main=paste0("iteration: ",thisi,", mean: ",mean(y)
                            ))
        abline(h=mean(y),col=thisi)
      })
    })
    y=y+100
  }
}

【问题讨论】:

    标签: r if-statement while-loop shiny output


    【解决方案1】:

    我建议使用while(或类似的)循环执行此操作会丢失一些反应潜力。事实上,您似乎正试图在 Shiny 的依赖/反应层中强制 order 进行绘图。

    我认为应该有三个独立的块,在 R/shiny 允许的情况下同时迭代:

    library(shiny)
    ui <- fluidPage(
      # Main panel for displaying outputs ----
      mainPanel(
        actionButton("exec", "Start!!"),
        tagList(tags$h4("First iteration:")),
        plotOutput('PlotIter1'),
        tags$hr(),
        tagList(tags$h4("Second iteration:")),
        plotOutput('PlotIter2'),
        tags$hr(),
        tagList(tags$h4("Third iteration:")),
        plotOutput('PlotIter3'),
        tags$hr()
      )
    )
    
    
    server <- function(input, output) {
      output$PlotIter1 <- renderPlot({
        plot(rep(1,50),main=1)
      })
      output$PlotIter2 <- renderPlot({
        plot(rep(2,50),main=2)
      })
      output$PlotIter3 <- renderPlot({
        plot(rep(3,50),main=3)
      })
    }
    
    shinyApp(ui, server)
    

    不过,我会进一步推论,你真的对这个 plot 的 1-3 不感兴趣;也许您想以编程方式进行操作? (我不得不查一下,因为几年前我问了一个非常相似的问题,并收到了good workaround from jcheng5shiny 的主要作者之一)。

    server <- function(input, output) {
      for (i in 1:3) {
        local({
          thisi <- i
          plotname <- sprintf("PlotIter%d", thisi)
          output[[plotname]] <<- renderPlot({
            plot(rep(thisi, 50), main=thisi)
          })
        })
      }
    }
    

    当然,这种方法只有在图相对相同且变化很小的情况下才有效。否则,上面的第一个版本可能更合适。

    【讨论】:

    • 感谢您的时间和精力,但是,也许我的 MWE 问题过于简单化了,所以第一种方法不适用于我的特殊情况。第二个解决了我的问题。非常感谢。
    • 从您的问题和您接受的答案看来,您似乎只希望情节只完成一次,从不更新。对吗?
    • 是的,这是正确的,我希望每次迭代都生成一个图,并以某种方式将该图存储在相应的 renderPlot 中以供以后显示。在任何情况下,尽管local({ }) 解决方案在我的 MWE 上运行良好,但我的程序仍然向我显示最后生成的绘图 n 次 inshead 对应于相应迭代运行的绘图。
    • "for later show" 对我来说没有意义。该代码立即生成三个图,立即显示这些图,并且永远不会再次生成它们。这个“后来”的概念不符合我对它的看法。我了解您的实际用例可能与此示例不同,因此我“了解”了更多内容,但我并不完全遵循。 (另外,我的答案对我有用;如果您说另一个答案不适用于您的实际代码,那么也许您不应该接受它,而是编辑您的 MWE/问题并重试?)
    • 'for later show'部分可以忽略,不好意思。我已经编辑了我的 MWE 和问题。
    【解决方案2】:

    实际上,在循环内部使用 renderXXXreactiveobserve 时,由于延迟评估,您可能会遇到几个问题。根据我的经验,最干净的解决方法是使用 lapply 并像这样循环 shiny modules

    ## context server.R
    lapply(1:n, function(i) { callModule(myModule, id = NS("myModule", i), moduleParam = i) })
    
    ## context: ui.R
    lapply(1:n, function(i) { myModuleUI(id = NS("myModule, i), param = i)
    

    但是,对于您的情况,更快的解决方法是按照第一个答案 here 中的建议使用 local。请注意,ii &lt;- ii 部分是它工作所必需的,因为它“本地化”了变量 ii

    library(shiny)
    ui <- fluidPage(
      # Main panel for displaying outputs ----
      mainPanel(
        actionButton("exec", "Start!!"),
        tagList(tags$h4("First iteration:")),
        plotOutput('PlotIter1'),
        tags$hr(),
        tagList(tags$h4("Second iteration:")),
        plotOutput('PlotIter2'),
        tags$hr(),
        tagList(tags$h4("Third iteration:")),
        plotOutput('PlotIter3'),
        tags$hr())
    )
    
    server <- function(input, output) {
      ii <- 1
      observeEvent(input$exec,{ 
    
        continue <- TRUE
    
        while(continue==TRUE){
          local({
            ii <- ii
    
            if(ii == 1){
              output$PlotIter1<-renderPlot({
                plot(rep(ii,50),main=ii)
              })
            }
            if(ii == 2){
              output$PlotIter2<-renderPlot({
                plot(rep(ii,50),main=ii)
              })
            }
            if(ii == 3){
              output$PlotIter3<-renderPlot({
                plot(rep(ii,50),main=ii)
              })
            }
          })
    
          ii <- ii+1
          if(ii == 4){continue <- FALSE}
        }
      })
    }
    
    shinyApp(ui, server)
    

    这是模块化方法的演示

    myModule <- function(input, output, session, moduleParam) {
      output$PlotIter <- renderPlot({
        plot(rep(moduleParam, 50), main = moduleParam)
      })
    }
    
    myModuleUI <- function(id, moduleParam) {
      ns <- NS(id)
      tagList(
        tags$h4(paste0("iteration ", moduleParam, ":")),
        plotOutput(ns('PlotIter')),
        tags$hr()
      )
    }
    
    shinyApp(
      fluidPage(
        actionButton("exec", "Start!!"),
        lapply(1:4, function(i) {myModuleUI(NS("myModule", i), i)})
      ),
      function(input, output, session) {
        observeEvent(
          input$exec,
          lapply(1:4, function(i) {callModule(myModule, NS("myModule", i), i)})
        )
      }
    )
    

    旁注:如果您想从同一个脚本中捕获多个绘图,您可以使用evaluate::evaluate 来实现

    library(evaluate)
    
    plotList <- list()
    i <- 0
    
    evaluate(
      function() {
        source("path/to/script.R")
      },
      output_handler = output_handler(
        graphics = function(plot) {
          i <- i + 1
          plotList[[i]] <- plot
        }
      )
    )
    

    【讨论】:

    • 什么时候在observeEvent 中嵌套renderPlot 是个好主意?第一个对于返回值很重要,第二个对于它的副作用很重要,为什么要结合这两个概念?
    • 我完全同意在observeEvent 中盲目使用renderXXXcallModuleobserve 是一个坏主意,而且我个人不会在我的任何项目中这样做。也就是说,我从未真正见过这种嵌套导致运行时超慢的情况,所以我没有在回答中提到它。
    • 脚本中的每次迭代都需要一点时间。用户必须上传一个文件,为计算设置一些特定的配置,然后按开始以运行脚本,然后我希望显示图表。我真的不知道是否有必要将几乎所有 server.R 部分放在observeEvent 中。你能告诉我为什么这是一个坏主意吗?还有其他方法可以实现吗?
    • 如果您每次上传只触发一次,则无需担心此 IMO。如果你每秒调用几次renderPlot,你可能会遇到麻烦(例如,因为它依赖于一个非常“忙”的反应)。潜在问题:您可能会得到更差的性能和过多的内存使用,因为某些 JavaScript 绑定必须一遍又一遍地绑定和解除绑定。我可以添加一些关于如何解决这个问题的评论,而无需在几个小时内动态地写信给output
    • 这是一个公平的问题,CristhianParedes,除了个人经验,我不知道我有什么可靠的答案。我在shiny.rstudio.com 上的TutorialGalleryArticles 中找不到使用嵌套块的示例。我参加了 RStudioConf 2018 上为期 2 天的 Intermediate Shiny 研讨会,但从未使用过一次(shiny 创作者)。 ...
    【解决方案3】:

    对于未来的某个人,我最终提出的解决方案是将数据结构更改为一个列表,其中存储每次迭代的结果,然后将列表中的每个元素绘制到相应的渲染图在for 循环内。当然,如果没有 r2evans 和 Gregor de Cecilia 指出的非常重要的事情,这是不可能的。因此,这种方法提供了以下server.R 函数:

    server <- function(input, output){
      y <- list()
      #First data set
      y[[1]] <- rnorm(10,20,2)
      #Simple example of iteration results storage in the list simulating an iteration like procces
        for(i in 2:3){
        y[[i]]=y[[i-1]]+100
      }
    
      #Plots of every result
      for(j in 1:3){
      local({
        thisi <- j
        plotname <- sprintf("PlotIter%d", thisi)
        output[[plotname]] <<- renderPlot({
          plot(y[[thisi]], main=paste0("Iteration: ",thisi,", mean: ",round(mean(y[[thisi]]),2)
          ))
          abline(h=mean(y[[thisi]]),col=thisi)
        })
      })
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-12
      • 1970-01-01
      • 1970-01-01
      • 2016-03-05
      • 1970-01-01
      • 1970-01-01
      • 2022-06-21
      • 1970-01-01
      相关资源
      最近更新 更多