【问题标题】:R Shiny: Adding to plot via a loopR Shiny:通过循环添加到绘图
【发布时间】:2017-04-12 13:22:21
【问题描述】:

我正在尝试使用 Shiny 创建一个显示采样动画的应用程序。类似于here 所示的示例。

这里有一些最小的代码,只显示了我遇到问题的部分。这不是我使用的数据,而是一个可重现的示例数据集。

library(shiny)
library(ggplot2)

data <- data.frame(ID=1:60, 
                   x=sort(runif(n = 60)), 
                   y=sort(runif(n = 60)+rnorm(60)))

ui <- fluidPage(
    sidebarPanel(
        sliderInput("n",
                    "Number of samples:",
                    min = 10,
                    max = 100,
                    value = 20),

        sliderInput("surveys",
                    "Number of surveys:",
                    min = 10,
                    max = 100,
                    value = 20),

        checkboxInput("replacement", 
                      "Sample with replacement?"),

        actionButton("button", "Go!")
    ),
    # Show the plot
    mainPanel(
        plotOutput("plot1")
    )
)

server <- function(input, output) {
    output$plot1 <- renderPlot({
        plot1 <- ggplot(data, aes(x=x, y=y)) + geom_point(colour="red") + theme_bw()
        plot1 <- plot1 + geom_vline(xintercept = mean(data$x), size=1.1, colour="red")
        plot1

        for(i in 1:20){
            data$sampled <- "red"
            sample.rows <- sample(data$ID, 20, replace = F)
            data$sampled[sample.rows] <- "green"

            plot1 <- plot1 + geom_point(x=data$x, y=data$y, colour=data$sampled, size=2)

            sample.mean.x <- mean(data$x[sample.rows])

            plot1 <- plot1 + geom_vline(xintercept = sample.mean.x, colour="green")

            print(plot1)
            Sys.sleep(1.5)
        }
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

renderPlot({ ... }) 中的部分在粘贴到控制台时完全符合我的要求,但是如何在 Shiny 中实现这一点?理想情况下,我还希望情节首先出现,然后在单击 actionButton 时开始动画(绿色条)。

谢谢!

【问题讨论】:

标签: r shiny


【解决方案1】:

您可以使用reactiveTimer 来执行此操作。我已经修改了代码的服务器部分。在下面的代码中,我将计时器设置为两秒,以便绘图每两秒更新一次。

  server <- function(input, output) {

    autoInvalidate <- reactiveTimer(2000)
    plot1 <- NULL

    output$plot1 <- renderPlot({
      plot1 <<- ggplot(data, aes(x=x, y=y)) + geom_point(colour="red") + theme_bw()
      plot1 <<- plot1 + geom_vline(xintercept = mean(data$x), size=1.1, colour="red")
      plot1
    })

    observeEvent(input$button,{

      output$plot1 <- renderPlot({
        autoInvalidate()
        data$sampled <- "red"
        sample.rows <- sample(data$ID, 20, replace = F)
        data$sampled[sample.rows] <- "green"

        plot1 <<- plot1 + geom_point(x=data$x, y=data$y, colour=data$sampled, size=2)

        sample.mean.x <- mean(data$x[sample.rows])

        plot1 <<- plot1 + geom_vline(xintercept = sample.mean.x, colour="green")

        plot1

      })
    })
  }

[编辑]

由于您希望循环仅运行 20 次,因此我在 this 链接中的答案的帮助下修改了代码,以便反应计时器仅运行到计数为 20。这是您的代码需要从链接中添加:

  invalidateLaterNew <- function (millis, session = getDefaultReactiveDomain(), update = TRUE) 
  {
    if(update){
      ctx <- shiny:::.getReactiveEnvironment()$currentContext()
      shiny:::timerCallbacks$schedule(millis, function() {
        if (!is.null(session) && session$isClosed()) {
          return(invisible())
        }
        ctx$invalidate()
      })
      invisible()
    }
  }

  unlockBinding("invalidateLater", as.environment("package:shiny"))
  assign("invalidateLater", invalidateLaterNew, "package:shiny")

这是它的服务器代码:

server <- function(input, output, session) {

count = 0
plot1 <- NULL


  output$plot1 <- renderPlot({
    plot1 <<- ggplot(data, aes(x=x, y=y)) + geom_point(colour="red") + theme_bw()
    plot1 <<- plot1 + geom_vline(xintercept = mean(data$x), size=1.1, colour="red")
    plot1
  })

observeEvent(input$button,{
 count <<- 0
  output$plot1 <- renderPlot({

    count <<- count+1
    invalidateLater(1500, session,  count < 20)
    data$sampled <- "red"
    sample.rows <- sample(data$ID, 20, replace = F)
    data$sampled[sample.rows] <- "green"

    plot1 <<- plot1 + geom_point(x=data$x, y=data$y, colour=data$sampled, size=2)

    sample.mean.x <- mean(data$x[sample.rows])

    plot1 <<- plot1 + geom_vline(xintercept = sample.mean.x, colour="green")

    plot1

  })
})


 }

【讨论】:

  • 太棒了!太感谢了!只是一个后续:我在哪里放置第二个代码块(即编辑后的第一个块)?
  • 没关系,搞定了。我如何让情节首先显示?在第一个版本(没有 20 个限制)中,它首先显示情节,但直到 Go!按钮被按下。第二个版本不会先显示情节......
  • 也解决了这个问题;) 必须将 observeEvent(input$button,{ 移到第二个 renderPlot({ 调用之前。
  • 你是对的,但是你需要把`count actionButton的observeEvent里面,这样每次点击按钮时计数器都会重置。
  • 如果将`renderPlot` 移到input$buttonobserveEvent 之外,您可能会遇到一个问题,即清除绘图以渲染新绘图。因此,对于这些情况,您可能希望将 renderPlot` 留在 observeEvent 中。
猜你喜欢
  • 2020-01-13
  • 2016-08-25
  • 1970-01-01
  • 1970-01-01
  • 2013-05-25
  • 1970-01-01
  • 2018-11-28
  • 2021-07-01
  • 2023-03-09
相关资源
最近更新 更多