我有一个闪亮的应用程序,它调用一个脚本,迭代在每次迭代中生成一个数字。我需要显示每个绘图并尝试使用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)
编辑:
通过使用r2evans和Gregor 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
}
}
我建议用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 jcheng5(shiny
的主要作者之一)。
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)
})
})
}
}
当然,这种方法只有在图表相对完全且变化较小时才有效。否则,上面的第一个版本可能更合适。
实际上,由于懒惰的评估,在循环中使用renderXXX
,reactive
或observe
时可能会遇到几个问题。根据我的经验,最干净的解决方法是使用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
}
)
)
对于将来的某个人来说,我最终带来的解决方案是将数据结构更改为一个列表,其中存储每次迭代的结果,之后,列表中的每个元素都被绘制到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)
})
})
}
}