【问题标题】:How to prevent output from running twice when inputs are inter-dependent?当输入相互依赖时,如何防止输出运行两次?
【发布时间】:2019-09-03 11:12:51
【问题描述】:

我正在开发一个基于 R Shiny 的应用程序。 我想让我的输入与可用数据保持一致,因此我更新了 selectInput 中的选定值。 当我更改输入 1 中的选定值时,输入 2 的值会更新,然后数据会更新(仅一次)。好的 但是,如果我更改输入 2 中的选定值,则更新数据,然后更新输入 1 的值,然后再次更新数据。 查看打印两次的“check latest_value”。

最初我使用的是 renderUI 而不是 updateSelectInput,但在初始化时,数据被计算了两次。

library(shiny)
library(DT)
library(dplyr)
my_data=data.frame(CO2)
# Running a Shiny app object
app <- shinyApp(
  ui = bootstrapPage(
    selectInput('type','Choix du type',choices = unique(my_data$Type)),
    uiOutput('plant_ui'),
    DTOutput('plot')
  ),
  server = function(input, output) {

    data=reactive({
      # req(input$type)
      my_data_temp=my_data
      if(length(input$type)>0){
        my_data_temp=my_data_temp%>%filter(Type%in%input$type)
      }
      if(length(input$plant)>0){
        my_data_temp=my_data_temp%>%filter(Plant%in%input$plant)
      }

      my_data_temp
    })

    latest_plant_value=reactive({
      if(is.null(input$plant))data()$Plant[1]
      else input$plant
    })


    output$plant_ui=renderUI({
      sub_data=data()
      selectInput(inputId = 'plant',"filtre par plant",choices = unique(sub_data$Plant),
                  selected=latest_plant_value())
    })

    output$plot <- renderDT({ 
      print("check latest_value")
      datatable(data()) })
  }
)
runApp(app)

这就是为什么我决定基于 Alternate control of a sliderInput between a derived value and user selected value 使用 updateSelectInput,但是代码的顺序结构使得当我更改输入 2 值时要计算两次数据。

library(shiny)
library(DT)
library(dplyr)
my_data=data.frame(CO2)
# Running a Shiny app object
app <- shinyApp(
  ui = bootstrapPage(
    selectInput('type','Choix du type',choices = unique(my_data$Type),selected=my_data$Type[1]),
    selectInput('plant','Choix du type',choices = unique(my_data$Plant),selected=my_data$Plant[1]),
    DTOutput('plot')
  ),
  server = function(input, output,session) {

    data=reactive({
      # req(input$type)
      my_data_temp=my_data
      if(length(input$type)>0){
        my_data_temp=my_data_temp%>%filter(Type%in%input$type)
      }
      if(length(input$plant)>0){
        my_data_temp=my_data_temp%>%filter(Plant%in%input$plant)
      }

      my_data_temp
    })

    observeEvent(input$type,{
      print("update type changed")
      updateSelectInput(session, "plant",
                        selected =  unique(my_data%>%filter(Type%in%input$type)%>%.$Plant)[1])
    })
    observeEvent(input$plant,{
      print("update plant changed")
      updateSelectInput(session, "type",
                        selected =  unique(my_data%>%filter(Plant%in%input$plant)%>%.$Type)[1])
    })

   output$plot <- renderDT({ 
     print("check latest_value")

     datatable(data()) })
  }
)
runApp(app)

这样的修复在这种情况下不起作用,因为我不想达到同样的目的three interdependent selectInput in R/Shiny application

我希望每个输入的默认选择值保持一致,以便过滤器返回至少 1 个值。这是我更改的任何输入。

【问题讨论】:

  • 你试过用isolate()吗?

标签: r input shiny selectinput


【解决方案1】:

解决此问题的一种方法是创建一个reactiveVal,告诉应用程序正在进行更新操作,并要求data 等到该标志返回 False 后再运行。

我在您的第二个闪亮应用中添加了 5 行代码:

server()

# Create update in progress flag
updating_type_inprogress <- reactiveVal(FALSE)

observeEvent(input$type ...

# When type is changed, set flag to TRUE
updating_type_inprogress(TRUE)

observeEvent(input$plant ...

# Once this function has run, the updating operation is done
updating_type_inprogress(FALSE)

data()

# Stops updating data() if the in-progress flag is TRUE
req(!updating_type_inprogress())

renderDT()

# Stops updating renderDT() if the in-progress flag is TRUE
#  this is probably optional unless there's resource-intensive code
#    that doesn't depend on changes in data()
req(!updating_type_inprogress())

这是整个代码:

library(shiny)
library(DT)
library(dplyr)
my_data=data.frame(CO2)
# Running a Shiny app object
app <- shinyApp(
    ui = bootstrapPage(
        selectInput('type','Choix du type',choices = unique(my_data$Type),selected=my_data$Type[1]),
        selectInput('plant','Choix du type',choices = unique(my_data$Plant),selected=my_data$Plant[1]),
        DTOutput('plot')
    ),
    server = function(input, output,session) {

        data=reactive({
            req(!updating_type_inprogress())
            print(input$type)
            print(input$plant)
            my_data_temp=my_data
            if(length(input$type)>0){
                my_data_temp=my_data_temp%>%filter(Type%in%input$type)
            }
            if(length(input$plant)>0){
                my_data_temp=my_data_temp%>%filter(Plant%in%input$plant)
            }

            my_data_temp
        })

        observeEvent(input$type,{
            updating_type_inprogress(TRUE)
            updateSelectInput(session, "plant",
                              selected =  unique(my_data%>%filter(Type%in%input$type)%>%.$Plant)[1])
        })
        observeEvent(input$plant,{
            updating_type_inprogress(FALSE)
            updateSelectInput(session, "type",
                              selected =  unique(my_data%>%filter(Plant%in%input$plant)%>%.$Type)[1])
        })

        updating_type_inprogress <- reactiveVal(FALSE)

        output$plot <- renderDT({ 
            req(!updating_type_inprogress())
            print("check latest_value")
            datatable(data()) })
    }
)
runApp(app)

如您所见,当您更改 input$type 时,data()renderDT() 函数仅使用正确更新的值运行一次:

[1] "check latest_value"
[1] "Quebec"
[1] "Qn1"
[1] "check latest_value"
[1] "Mississippi"
[1] "Mn1"
[1] "check latest_value"
[1] "Quebec"
[1] "Qn1"

【讨论】:

  • 不错的更新标志!但是,没有解决我的回答中解决的问题 1。
【解决方案2】:

有趣的问题,不容易解决!有趣的是,你所要求的并不是你所需要的。观察:

  1. 如果用户选择 Qn2 而 Input1 是“Mississippi”,您首先在 Quebec 上设置 Input1,然后在 上设置 硬设置 Input2 >Qn1,改变用户的选择。这很糟糕。
  2. 一旦两个输入中的任何一个发生更改,数据表就会始终更新,因此需要对表进行多次重新计算。

因此解决方案是双重的:

  1. 不要覆盖用户的选择,例如Qc2Qc1。我为此使用了 if 条件。
  2. 安装 watchguard 以仅更新 内容实际更改时的数据表。我使用 reactiveVal() 执行此操作,仅当两个输入的选择有效时(即结果集大于 0)才更新。

查看下面的结果。观察控制台输出以观察决策。

library(shiny)
library(DT)
library(dplyr)
my_data=data.frame(CO2)

shinyApp(
  ui = bootstrapPage(
    selectInput('type','Choix du type',choices = unique(my_data$Type),selected=my_data$Type[1]),
    selectInput('plant','Choix du plant',choices = unique(my_data$Plant),selected=my_data$Plant[1]),
    DTOutput('plot')
  ),
  server = function(input, output,session) {

    latest_data <- reactiveVal(my_data)
    observe({
      result <- my_data %>% filter(Type %in% input$type, Plant %in% input$plant)

      if(nrow(result) > 0){
        latest_data(result)
      }else{
        cat(format(Sys.time(), "%H:%M:%S"), "Didn't update the dataframe because the choice was not valid.\n")
      }
    })

    observeEvent(input$type,{
      if(! input$plant %in% my_data$Plant[my_data$Type == input$type]){
        old <- input$plant
        new <- my_data %>% filter(Type %in% input$type) %>% slice(1) %>% pull(Plant) %>% as.character()
        updateSelectInput(session, "plant", selected = new)
        cat(format(Sys.time(), "%H:%M:%S"), "Updated input$plant from", old, "to", new, "so that it represents a valid choice for", input$type, "\n")
      }else{
        cat(format(Sys.time(), "%H:%M:%S"), "Didn't update input$plant", input$plant, "because it is a valid choice for", input$type, "already\n")
      }
    })
    observeEvent(input$plant,{
        updateSelectInput(session, "type",
                          selected = my_data %>% filter(Plant %in% input$plant) %>% slice(1) %>% pull(Type))
    })

    output$plot <- renderDT({ 
      cat(format(Sys.time(), "%H:%M:%S"), "updating datatable to only include", isolate(input$plant), "and", isolate(input$type), "\n\n")
      latest_data()
      datatable(latest_data())
    })
  }
)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-23
    • 2020-03-12
    • 2021-01-21
    • 2023-01-07
    • 2017-08-06
    • 2019-11-29
    相关资源
    最近更新 更多