R Shiny:循环和IF语句中的选择性输出赋值

问题描述 投票:0回答:3

我有一个闪亮的应用程序,它调用一个脚本,迭代在每次迭代中生成一个数字。我需要显示每个绘图并尝试使用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()方法,问题得到了部分解决,但是将服务器()更改为更接近我的服务器(更换其他策略FAPP等效的IF语句),包括每个情节之间的一些计算,问题持久化,最后的数据绘制在所有三个图中。

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
3个回答
1
投票

我建议用while(或类似的)循环来做它会缺少一些反应潜力。实际上,看起来你正试图在闪亮的依赖/反应层内强制绘制顺序。

我认为应该有三个独立的块,与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)
      })
    })
  }
}

当然,这种方法只有在图表相对完全且变化较小时才有效。否则,上面的第一个版本可能更合适。


1
投票

实际上,由于懒惰的评估,在循环中使用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)

然而,对于你的情况,更快的解决方法是使用local,如第一个答案here中所建议的那样。请注意,ii <- 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
    }
  )
)

0
投票

对于将来的某个人来说,我最终带来的解决方案是将数据结构更改为一个列表,其中存储每次迭代的结果,之后,列表中的每个元素都被绘制到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)
    })
  })
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.