【问题标题】:Challenge with dynamically generated, interactive R Shiny plots (mostly functioning code)挑战动态生成的交互式 R Shiny 图(主要是功能代码)
【发布时间】:2019-06-19 09:04:22
【问题描述】:

以下是最低代码。它有效,但有一个奇怪的问题。以下是有效的:

  1. 用户可以选择多个绘图(默认为 3)。
  2. 用户可以单击绘图并显示该值(部分有效)。

重现“部分作品”的步骤:

  1. 在启动时,单击情节 #3,没问题。
  2. 点击情节 #2,没有任何反应。
  3. 将地块数量从 3 减少到 2,然后再减少到 3。
  4. 点击情节 #2,现在可以使用了。
  5. 点击情节 #1,没有任何反应。
  6. 将地块数量从 3 减少到 1,然后再减少到 3。
  7. 点击情节 #1,现在可以使用了。

如果您重新加载应用程序并从上面的第 6 步开始,所有绘图都可以按预期进行交互。

rm(list=ls())
library(shiny)

#
# Dynamic number of plots: https://stackoverflow.com/questions/26931173/shiny-r-renderplots-on-the-fly
# That can invalidate each other: https://stackoverflow.com/questions/33382525/how-to-invalidate-reactive-observer-using-code
#

ui <- (fluidPage(sidebarLayout(
         sidebarPanel(
            numericInput("np", "Plots:", min=0, max=10, value=3, step=1)
         )
         ,mainPanel(
            fluidRow(uiOutput("plots"))
         )
)))

server <- function(input, output, session) {
   val <- reactiveValues()
   dum <- reactiveValues(v=0)
   obs <- list()

    ### This is the function to break the whole data into different blocks for each page
    plotInput <- reactive({
      print("Reactive")
      np <- input$np
      for(i in 1:np) {
         cx <- paste0("clk_p",i); dx <- paste0("dbl_p",i); px <- paste0("p",i)
         obs[[cx]] <- observeEvent(input[[cx]], {
            req(input[[cx]]); val[[px]] <- input[[cx]]$x; dum$v <- dum$v+1; print(paste("Dum",dum$v))
         })
         obs[[dx]] <- observeEvent(input[[dx]], {
            req(input[[dx]]); val[[px]] <- NULL
         })
      }

      return (list(np=np))
    })

    ##### Create divs######
    output$plots <- renderUI({
      print("Tag plots")
      pls <- list()
      for(i in 1:plotInput()$np) {
         pls[[i]] <- column(4,
                           plotOutput(paste0("p",i), height=200, width=200
                                     ,click=paste0("clk_p",i)
                                     ,dblclick=paste0("dbl_p",i))
                         )
      }
      tagList(pls)
    })

    observe({
      print("Observe")
      lapply(1:plotInput()$np, function(i){
        output[[paste("p", i, sep="") ]] <- renderPlot({
          print(paste("Plot",dum$v))
          x <- val[[paste0("p",i)]]
          x <- ifelse(is.null(x),"NA",round(x,2))
          par(mar=c(2,2,2,2))
          plot(x=runif(20), y=runif(20), main=i, xlim=c(0,1), ylim=c(0,1), pch=21, bg="gray", cex=1.5)
          if(is.numeric(x)) abline(v=x, col="blue")
          rm(x)
        })
      })
    })
}

shinyApp(ui, server)

【问题讨论】:

    标签: r shiny shiny-reactivity


    【解决方案1】:

    这是你正在尝试做的工作版本:

    library(shiny)
    
    ui <- fluidPage(
      sidebarPanel(
        numericInput("num", "Plots:", 3)
      ),
      mainPanel(
        uiOutput("plots")
      )
    )
    
    server <- function(input, output, session) {
      obs <- list()
      val <- reactiveValues()
    
      observe({
        lapply(seq(input$num), function(i){
          output[[paste0("plot", i) ]] <- renderPlot({
            click_id <- paste0("clk_p",i);
            plot(x = runif(20), y = runif(20), main=i)
            if (!is.null(val[[click_id]])) {
              abline(v = val[[click_id]], col = "blue")
            }
          })
        })
      })
      observe({
        lapply(seq(input$num), function(i){
          id <- paste0("clk_p",i);
          if (!is.null(obs[[id]])) {
            obs[[id]]$destroy()
          }
          val[[id]] <- NULL
          obs[[id]] <<- observeEvent(input[[id]], {
            cat('clicked ', id, ' ', input[[id]]$x, '\n')
            val[[id]] <- input[[id]]$x
          }, ignoreInit = TRUE)
        })
      })
    
      output$plots <- renderUI({
        lapply(seq(input$num), function(i) {
          id <- paste0("plot", i)
          plotOutput(id, height=200, width=200, click=paste0("clk_p",i))
        })
      })
    }
    
    shinyApp(ui,server)
    

    给以后看到这种情况的人的一些主要指示:

    • 原始代码的主要问题是所有观察者都只注册最后一个 ID。这是一个高级概念,与 R 中环境的工作方式有关,因为它们是在 for 循环中创建的。解决此问题的方法是使用 lapply() 而不是 for 循环来创建观察者
    • 另一个问题是 obs 正在覆盖列表中的观察者,但之前的观察者仍然存在并且仍然可以触发,所以我向 destroy() 现有的观察者添加了逻辑。
    • shiny 中最重要的规则之一是不要将副作用放在反应式中(plotInput 有副作用),因此我以一种避免这种情况的方式重写了代码

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 2019-10-08
      • 2018-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多