【问题标题】:How to automatically delete a matrix column in R if there otherwise would be subscript out of bounds error?如果存在下标越界错误,如何自动删除 R 中的矩阵列?
【发布时间】:2021-12-04 16:00:08
【问题描述】:

下图显示了运行以下 MWE 代码时发生的情况以及我要解决的问题:

  1. 第一张图片显示除了默认的“场景 1”之外,用户还输入了两个额外的插值场景。请注意,当用户准备删除场景 2 时,光标如何停留在场景 3 下方。
  2. 第二张图片显示了用户通过单击场景 2 列标题中的 [x] 从第一张图像中删除场景 2 后导致的错误,而光标仍位于场景 3 下方。(请注意,此如果将光标放在场景 2 下,则不会发生错误 - 但我正在尝试考虑真实世界的用户输入)。
  3. 第三张图片显示了用户通过单击第二张图片中无关的空白场景 3 中的 [x] 删除符号来纠正错误的结果。

在这样的扩展/收缩矩阵中很自然会出现下标越界错误。

我的问题是:如果出现下标越界错误,如何自动删除最后一列?

MWE 代码:

library(shiny)
library(shinyMatrix)
library(dplyr)
library(ggplot2)

interpol <- function(a, b) { # a = periods, b = matrix inputs
  c <- rep(NA, a)
  c[1] <- b[1]
  c[a] <- b[2]
  c <- approx(seq_along(c)[!is.na(c)], c[!is.na(c)], seq_along(c))$y # << interpolates
  return(c)
}

ui <- fluidPage(
  sliderInput('periods','Periods to interpolate:',min=2,max=10,value=10),
  matrixInput(
    "myMatrixInput",
    label = "Values to interpolate paired under each scenario heading:",
    value =  matrix(c(1, 5), 1, 2, dimnames = list(NULL, c("Scenario 1", "NULL"))),
    cols = list(extend = TRUE,  delta = 2, names = TRUE,  delete = TRUE,  multiheader = TRUE),
    rows = list(extend = FALSE, delta = 1, names = FALSE, delete = FALSE),
    class = "numeric"),
  plotOutput("plot")
)

server <- function(input, output, session) {
  
  sanitizedMat <- reactiveVal()
  
  observeEvent(input$myMatrixInput, {
      tmpMatrix <- input$myMatrixInput
      colnames(tmpMatrix) <- paste("Scenario", trunc(1:ncol(input$myMatrixInput)/2+1))
      updateMatrixInput(session, inputId = "myMatrixInput", value = tmpMatrix)
      sanitizedMat(na.omit(input$myMatrixInput))
  })
  
  plotData <- reactive({
    lapply(seq_len(ncol(sanitizedMat())/2),
           function(i){
             tibble(
               Scenario = colnames(sanitizedMat())[i*2-1],
               X = seq_len(input$periods),
               Y = interpol(input$periods, sanitizedMat()[1,(i*2-1):(i*2)])
             )
           }) %>% bind_rows()
  })
  
  output$plot <- renderPlot({
    plotData() %>% ggplot() + geom_line(aes(
      x = X,
      y = Y,
      colour = as.factor(Scenario)
    ))
  })

}

shinyApp(ui, server)

图片:

【问题讨论】:

    标签: r matrix shiny shiny-reactivity


    【解决方案1】:

    我做了很多更改,所以我只强调最重要的:

    • 关键的变化可能是这样的:我重新插入了代码以从observeEvent(input$myMatrixInput 的矩阵中删除空单元格。该代码最初由 PO 提供,但在寻找解决方案时被删除。我在子设置语句tmpMatrix &lt;- tmpMatrix[, !empty_columns, drop=FALSE] 中添加了drop=FALSE 以使其工作,因为we need to preserve the matrix type。如果您不这样做,代码将会中断。
    • sanitizedMat 已删除。一切都可以用矩阵输入来完成,它没有带来任何价值。相反,它让它变得更复杂、更难理解……而且复杂也总是意味着更容易出错。
    • updateMatrixInput 周围添加了一个isolate 以避免依赖关系。
    • plotData 反应式中添加了tryCatch,因为matrixInput 临时发送了无法解释的值。现在,当matrixInput 无效并且renderPlot 由于req(plotData()) 而不会运行时,响应式返回NULL
    library(shiny)
    library(shinyMatrix)
    library(dplyr)
    library(ggplot2)
    
    interpol <- function(a, b) { # a = periods, b = matrix inputs
      c <- rep(NA, a)
      c[1] <- b[1]
      c[a] <- b[2]
      c <- approx(seq_along(c)[!is.na(c)], c[!is.na(c)], seq_along(c))$y # << interpolates
      return(c)
    }
    
    ui <- fluidPage(
      sliderInput('periods', 'Periods to interpolate:', min=2, max=10, value=10),
      matrixInput(
        "myMatrixInput",
        label = "Values to interpolate paired under each scenario heading:",
        value =  matrix(c(1, 5), 1, 2, dimnames = list(NULL, rep("Scenario 1", 2))),
        cols = list(extend = TRUE,  delta = 2, names = TRUE,  delete = TRUE, multiheader = TRUE),
        rows = list(extend = FALSE, delta = 1, names = FALSE, delete = FALSE),
        class = "numeric"),
      plotOutput("plot")
    )
    
    server <- function(input, output, session) {
      
      observeEvent(input$myMatrixInput, {
          tmpMatrix <- input$myMatrixInput
          
          # Remove any empty matrix columns
          empty_columns <- sapply(tmpMatrix, function(x) all(is.na(x) | x == ""))
          tmpMatrix <- tmpMatrix[, !empty_columns, drop=FALSE]
    
          # Assign column header names
          colnames(tmpMatrix) <- paste("Scenario", rep(1:ncol(tmpMatrix), each = 2, length.out = ncol(tmpMatrix)))
          
            isolate( # isolate update to prevent infinite loop
              updateMatrixInput(session, inputId = "myMatrixInput", value = tmpMatrix)
            )
      })
      
      plotData <- reactive({
        tryCatch(
          lapply(seq_len(ncol(input$myMatrixInput)/2),
                 function(i){
                   tibble(
                     Scenario = colnames(input$myMatrixInput)[i*2-1],
                     X = seq_len(input$periods),
                     Y = interpol(input$periods, input$myMatrixInput[1,(i*2-1):(i*2)])
                   )
                 }) %>% bind_rows(),
          error = function(e) NULL
        )
      })
      
      output$plot <- renderPlot({
        req(plotData())
        
        plotData() %>% ggplot() + geom_line(aes(
          x = X,
          y = Y,
          colour = as.factor(Scenario)
        ))
      })
    
    }
    
    shinyApp(ui, server)
    

    【讨论】:

    • Jan,这运行得很漂亮。我不能打破它。我在这里有很多新东西要学,我会花一些时间研究它(像“TryCatch”这样的东西)。当矩阵垂直扩展(而不是当前水平)时,自定义函数 sanitizedMat() 非常有用;它修补了一个shinyMatrix 错误,您必须在左列之前输入右列。但是当这段代码从垂直矩阵切换到水平矩阵时,就不再需要 sanitizedMat 了。我会把它放在我的后兜里,因为我的下一步是让它水平和垂直扩展。谢谢!!
    猜你喜欢
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 2015-04-03
    • 2015-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多