在闪亮的renderPlot中奇怪的实际参数分配

时间:2016-09-13 22:04:03

标签: shiny output rendering

当使用for循环渲染一些图时,我得到的结果我并不理解。所有的情节都等于(预期的)最后一个。我希望在下面的例子中更清楚地说明这一点。注意,plot1显示了' 6' (最后一个),而不是' 5' (如预期)。 任何人都可以解释这个和/或给出一个避免这种行为的解决方案 闪亮的ui:

shinyUI (fluidPage(
fluidRow (
    column (6, plotOutput ('plot1', height = "180px")),
    column (6, plotOutput ('plot2', height = "180px"))
),
fluidRow (
    column (6, plotOutput ('plot3', height = "180px")),
    column (6, plotOutput ('plot4', height = "180px"))
)
))

闪亮的服务器:

shinyServer (function (input, output, session) {
require (ggplot2)
plotId      <- c('plot1', 'plot2', 'plot3', 'plot4')
pw          <- initPlotWindows ()   
output[['plot1']] <- renderPlot ({pw[[1]]})
output[['plot2']] <- renderPlot ({pw[[2]]})
output[['plot3']] <- renderPlot ({pw[[3]]}) 
output[['plot4']] <- renderPlot ({pw[[4]]})
id  <- 'plot1'
i   <- 5
output[[id]] <- renderPlot ({pw[[i]]})
id  <- 'plot3'
i   <- 6
# output[[id]] <- renderPlot ({pw[[i]]})
})  

initPlotWindows <- function () {
    pw          <- vector (mode = 'list', length = 6)                           # plot window data
    for (i in 1:6)  pw[[i]] <- p_empty (plotN = i)
    pw
}
p_empty <- function (plotN) {
    p <- ggplot()
    p <- p + annotate("text", label = plotN, x = .5, y = .5, size = 20, colour = "grey")
    p <- p + theme(axis.ticks.x = element_blank(), axis.text.x = element_blank(), axis.title.x=element_blank())
    p <- p + theme(axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.title.y=element_blank())
    p
}

产生的结果。 Plot1包含6而不是5! enter image description here

3 个答案:

答案 0 :(得分:2)

发生这种情况的原因是因为闪亮不会逐行执行服务器端代码。 Shiny在开始时执行一次所有非反应性代码。但是renderPlot()和其他反应代码只会在必要时运行。 (当他们被召唤时)

所以发生的事情是,它分配5,然后6分配给i,之后只渲染了图。

output[[id]] <- renderPlot ({
    print("section D")
    pw[[i]]})

您可以使用print()功能在R控制台中轻松查看:

shinyServer (function (input, output, session) {
  print("section A")
  require (ggplot2)
  plotId      <- c('plot1', 'plot2', 'plot3', 'plot4')
  pw          <- initPlotWindows ()

  output[['plot1']] <- renderPlot ({
    print("section B1")
    pw[[1]]})
  output[['plot2']] <- renderPlot ({
    print("section B2")
    pw[[2]]})
  output[['plot3']] <- renderPlot ({
    print("section B3")
    pw[[3]]}) 
  output[['plot4']] <- renderPlot ({
    print("section B4")
    pw[[4]]})
  print("section C")
  id  <- 'plot1'
  i   <- 5
  output[[id]] <- renderPlot ({
    print("section D")
    pw[[i]]})
  print("section E")
  id  <- 'plot3'
  i   <- 6
  # output[[id]] <- renderPlot ({pw[[i]]})
})  

控制台输出:

[1] "section A"
[1] "section C"
[1] "section E"
[1] "section B1"
[1] "section B2"
[1] "section B3"
[1] "section B4"
[1] "section D"

编辑:

至于output[[id]]id会在开始时进行评估,因此output[[id]]变为output[[plot1]]。但pw[[i]]仅在闪亮要求渲染图时进行评估,即在将值设置为5然后再设置为6之后。

如果您希望plot1显示5,那么您可以在id之外指定renderPlot(),并在i内指定renderPlot()

id  <- 'plot1'
  output[[id]] <- renderPlot ({
    i <- 5
    pw[[i]]})

答案 1 :(得分:0)

谢谢GyD,
根据你的回答,我得出结论,正常的for循环是不可能的。运行变量&#39; i&#39;将在外部定义,而不是在renderPlot()内定义。 所以我似乎必须至少使用eval(parse())来实现renderPlot()。 像for循环一样:

for ( i in 1:4) {
        pltId <- paste ('plot', i, sep='')
        output[[pltId]] <- renderPlot ({pw[i]})
    }
然后

将成为(使用eval(parse())进行整个分配):

for ( i in 1:4) {
        eval (parse (text = paste ("output[['plot", i, "']] <- renderPlot ({pw[", i, "]})", sep='')))
    }

答案 2 :(得分:0)

您的答案应该有效,但您可以使用循环生成UI元素和服务器端。 (不知道你是否感兴趣)

我不是R中for循环的忠实粉丝,所以我使用的是lapply

library(shiny)
library(ggplot2)

# I want to plot these elements

vec <- c(5, 2, 3, 4)

# Defining some functions

initPlotWindows <- function () {
  pw          <- vector (mode = 'list', length = 6)                           # plot window data
  for (i in vec)  pw[[i]] <- p_empty (plotN = i)
  pw
}
p_empty <- function (plotN) {
  p <- ggplot()
  p <- p + annotate("text", label = plotN, x = .5, y = .5, size = 20, colour = "grey")
  p <- p + theme(axis.ticks.x = element_blank(), axis.text.x = element_blank(), axis.title.x=element_blank())
  p <- p + theme(axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.title.y=element_blank())
  p
}

# Creating pw list
pw <- initPlotWindows()

ui <- fluidPage(
  # Creating UI from loop
  lapply(vec, function(i) {
    column(6, plotOutput(paste0('plot', i), height = "180px"))
  })
)

server <- function(input, output, session){
  # Generating UI output from loop

  lapply(vec, function(i) {
    output[[paste0('plot', i)]] <- renderPlot({
      pw[[i]]
    })
  })
}

shinyApp(ui, server)