【问题标题】:Switching between reactive data sets of different formats with rhandsontable使用 rhandsontable 在不同格式的响应式数据集之间切换
【发布时间】:2018-10-03 13:12:58
【问题描述】:

在这个极好的问题中:Shiny: Switching reactive datasets with Rhandsontable and external parameters 数据帧和 rhandsontable 输出具有相同的结构。

我正在尝试解决类似的问题,但数据集的结构不同,并且数据集是在嵌套列表中构建的。考虑这个带有两个输入选择器的例子:

根据输入选择器,可以生成四种可能的表。它们是:

表 1(列表 1,“第一”):

表 2(列表 1,“第二”):

表 3(列表 2,“第三”):

表 4(列表 2,“第四”):

每个表格都通过一个 renderRHandsontable 元素出现在同一个位置。我认为我的问题在于更新 reactiveValue “值” - 你如何更新列表的元素而不是任何其他元素?这是一个显示正确的最小示例,但您不能更改任何元素(我正在尝试解决的问题)。

require(rhandsontable)
require(shiny)

# Create some fake lists
list_1 <- list()
list_2 <- list()
list_1[['first']] <- data.frame(matrix(1:4,ncol=4))
list_1[['second']] <- data.frame(matrix(1:2,ncol=2),bool=factor('a1',levels=c('a1','a2','a3')))
list_2[['third']] <- data.frame(matrix(7:9,ncol=3))
list_2[['fourth']] <- data.frame(matrix(10:11,ncol=2),bool=factor('b1',levels=c('b1','b2')))

ui <- fluidPage(sidebarLayout(sidebarPanel(
  selectInput(
    'list_selector', 'Select list:',
    choices = c('list_1', 'list_2')
  ),
  uiOutput("second_selectorUI")
),
mainPanel(rHandsontableOutput("out"))))

server <- function(input, output) {

  values = reactiveValues()
  values[["list_1"]] <- list_1
  values[["list_2"]] <- list_2

  # Feed user input back to the list
  observe({
    if (!is.null(input$out)) {
      temp <- hot_to_r(input$out)
      if (isolate(input$list_selector) %in% c('first','third')){
        values[[isolate(input$list_selector)]][[isolate(input$list_selector)]] <- temp$values #Returns to wide format
      } else {
        values[[isolate(input$list_selector)]][[isolate(input$list_selector)]] <- temp
      }
    }
  })

  # Why isn't values[["list_1"]][[input$second_list_selector]] allowed?
  list <- reactive({
    if (input$list_selector == "list_1") {
      values[["list_1"]]
    } else if (input$list_selector == "list_2"){
      values[["list_2"]]
    }
  })

  output$second_selectorUI <- renderUI({
    if (input$list_selector == 'list_1'){
      selectInput(inputId = "second_list_selector", label="Select element 1", 
                  choices = c('first', 'second'))
    } else if (input$list_selector == 'list_2'){
      selectInput(inputId = "second_list_selector", label="Select element 2", 
                  choices = c('third', 'fourth'))
    }
  })

  output$out <- renderRHandsontable({
    if (!is.null(list()) && !is.null(input$second_list_selector)){
      if (input$second_list_selector %in% c('first','third')){
        df <- list()[[input$second_list_selector]]
        df <- data.frame(values=as.numeric(df)) #Turns into long format
        rhandsontable(df, stretchH = "all", rowHeaderWidth = 300, width=600)
      } else if (input$second_list_selector %in% c('second','fourth')){
        df <- list()[[input$second_list_selector]]
        rhandsontable(df, stretchH = "all", rowHeaderWidth = 50, height = 300,width=600) %>%
          hot_col("bool", allowInvalid = FALSE)
      }
    }
  })
}

shinyApp(ui = ui, server = server)

【问题讨论】:

    标签: r shiny rhandsontable


    【解决方案1】:

    这是一种方法:

    ui <- fluidPage(
      sidebarLayout(
        sidebarPanel(
          selectInput("sel_1", label = "Select list:", 
                      choices = c("list_1", "list_2")),
          selectInput("sel_2", label = "Select element 1",
                      choices = names(list_1))
        ),
        mainPanel(
          rHandsontableOutput("hot")
        )
      )
    )
    
    server <- function(input, output, session) {
    
      list_1$first <- data.frame(values = as.numeric(list_1$first))
      list_2$third <- data.frame(values = as.numeric(list_2$third))
    
      values <- reactiveValues(list_1 = list_1, list_2 = list_2)
    
      observe({
        x <- input$sel_1
        label <- paste("Select element", substr(x, nchar(x) - 1, nchar(x)))
        updateSelectInput(session, "sel_2", label, 
                          choices = names(values[[x]]))
      })
    
      # Key part: storing back the data in `values` every time there is a change
      observe({
        if (!is.null(input$hot) && !is.null(input$hot$changes$changes)) 
          values[[isolate(input$sel_1)]][[isolate(input$sel_2)]] <- hot_to_r(input$hot)
      })
    
      output$hot <- renderRHandsontable({
          df <- values[[input$sel_1]][[input$sel_2]]
          if (is.null(df)) return(NULL)
          if (input$sel_2 %in% c("first", "third")) {
            rhandsontable(df, stretchH = "all", rowHeaderWidth = 300, width = 600)
          } else {
            rhandsontable(df, stretchH = "all", rowHeaderWidth = 50, height = 300, width = 600) %>%
              hot_col("bool", allowInvalid = FALSE)
          }
      })
    
    }
    

    但请注意这个答案,因为在我写它的时候,我仍然偶尔会看到奇怪的不可重现的错误,比如错误的表被覆盖,我发现在rhandsontable 的上下文中很难控制反应性。

    【讨论】:

    • 我也在与奇怪的、不可重现的错误作斗争——有时错误的表被保存到观察者的反应值中。我的解决方案(一个可怕的 hack,但它对我有用)是在分配之前检查 dim(temp) == dim(values[[isolate(input$sel_1)]][[isolate(input$sel_2)]])将表返回到 reactiveValue。我认为hot_to_r()和observe()的时间有冲突,但我无法证明。
    • 你称它为 input$hot$changes$changes 是因为它是一个嵌套列表吗?如果列表不超过“一层深”,它会只是 input$hot$changes 吗?这真的很有用。
    • @fifthace 我使用observe(cat(str(reactiveValuesToList(input)), "\n")) 作为通用Shiny 开发技巧,并注意到input$hot$changes$changes 仅在实际修改handsontable 时才更改,与input$hot(甚至input$hot$changes)形成对比)不断更新,不必要地触发hot_to_r。由于我在反应方面遇到问题,我想我可以使用它。 (问题似乎仍然存在:/
    猜你喜欢
    • 2015-11-24
    • 2018-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 2022-10-01
    • 2018-07-18
    • 2019-07-15
    相关资源
    最近更新 更多