在 R Shiny 中,如何正确 link 矩阵值?

In R Shiny, how to correctly link matrix values?

在下面的 MWE 代码中,用户可以选择通过一系列逐步矩阵输入(在本例中为 3)来优化模型 assumptions/inputs。首次调用应用程序时默认显示的第一个矩阵是 X 周期内的固定利率 Y。通过单击“显示”调用的第二个矩阵允许用户在 X 周期内改变 Y(构建“曲线”)。第三个矩阵,通过单击“添加场景”操作按钮调用,允许用户添加曲线场景(请注意,为简化 MWE,此第三个矩阵当前不水平扩展)。

矩阵在一个方向上链接:第一个矩阵中的值输入第二个矩阵,第二个矩阵输入第三个矩阵,最后一个矩阵在所有计算中优先。 (请注意,由于我尚未下载补丁的 shinyMatrix 包中的一个小错误,可扩展矩阵的任何输入可能必须从右到左)。

MWE中矩阵1的id是flatInput,矩阵2是curveBaseRate,矩阵3是curveBaseRateAdd.

我遇到的问题是,如果第二个矩阵发生变化(添加了任何行),它不会正确反映在第三个矩阵中(如底部的第三张图片所示)。如果第二个矩阵未更改而仅显示,则第二个矩阵正确地馈入第三个矩阵(如底部的第二个图像所示)。这怎么能解决?我相信我在模态对话框中呈现的矩阵 3 中对矩阵 2 row/column 的引用有问题(行 input$curveBaseRate[,1]input$curveBaseRate[,2],但我不知道我在做什么错了。

MWE 代码:

library(shiny);library(shinyMatrix);library(shinyjs)

f <- function(action,i){as.character(checkboxInput(paste0(action,i),label=NULL))}
actions       <- c("show", "reset")
tbl           <- t(outer(actions, c(1,2), FUN = Vectorize(f)))
colnames(tbl) <- c("Show", "Reset")
rownames(tbl) <- c("Base", "Spread")

xDflt <- .05 # << default value for x column of input matrix
yDflt <- .01  # << default value for y column of input matrix

flatRate <- function(inputId,x,y){
  matrixInput(inputId, 
              value = matrix(c(x,y), 1, 1, dimnames = list(c("Base rate"),NULL)),
              rows =  list(extend = FALSE, names = TRUE),
              cols =  list(extend = FALSE, names = FALSE, editableNames = FALSE),
              class = "numeric")
} 

curveRate <- function(inputId,w,x,y){
  matrixInput(inputId,
              label = w,
              value = matrix(c(x,y),1,2,dimnames=list(NULL,c("Period (X)","Curved (Y)"))),
              rows =  list(extend = TRUE,  names = FALSE),
              cols =  list(extend = FALSE, names = TRUE, editableNames = FALSE),
              class = "numeric")
}  

ui <- 
  fluidPage(
    tags$head( 
      tags$style(HTML(
        "td .checkbox {margin-top: 0; margin-bottom: 0;}
       td .form-group {margin-bottom: 0;}"
      ))
    ),
    titlePanel("Model"),
    sidebarLayout(
      sidebarPanel(uiOutput("Panel")),
      mainPanel(tabsetPanel(tabPanel("Rates", value=2),id = "tabselected"))
    ) 
  ) 

server <- function(input,output,session)({
  output$Panel <- renderUI({
      conditionalPanel(
          condition = "input.tabselected == 2",
          useShinyjs(),
          helpText("Modeled periods (X):"),
          sliderInput('periods','',min=2,max=120,value=60),
          helpText("Initial rates (Y):"),
          flatRate("flatInput",xDflt,yDflt), 
          helpText("Generate curves (Y|X):"),
          tableOutput("checkboxes"),
          hidden(uiOutput("curveBaseRate")),
          actionButton("addScenarios","Add scenarios")
      )
  }) 
  
  ### Begin checkbox matrix ###
  output[["checkboxes"]] <- 
    renderTable({tbl}, 
                rownames = TRUE, align = "c",
                sanitize.text.function = function(x) x
    )
  
  observeEvent(input[["show1"]], {
    if(input[["show1"]]){
      shinyjs::show("curveBaseRate")
    } else {
      shinyjs::hide("curveBaseRate")
    }
  })
  ### End checkbox matrix ###
  
  output$curveBaseRate <- renderUI({
    req(input$periods,input$flatInput)
    input[["reset1"]]
    curveRate("curveBaseRate",
              "Base rates curve (Y|X):",
              input$periods,
              input$flatInput[1,1])
  })
  
  outputOptions(output, "curveBaseRate", suspendWhenHidden = FALSE)
  
  observeEvent(input$addScenarios, {
    showModal(
      modalDialog(
        curveRate("curveBaseRateAdd",
                  "Base rates curve (Y|X):",
                  input$curveBaseRate[,1],
                  input$curveBaseRate[,2]),
        footer =  modalButton("Close")
      ))
  }) 
  
}) 

shinyApp(ui, server)

解释性图片:

  1. 第一张图片显示第一个用户输入矩阵。
  2. 第二张图片显示了正确呈现的第二个用户输入矩阵(点击“显示”后) 在模态对话框中输入第三个用户输入矩阵,没有用户更改第二个输入矩阵。
  3. 第三张图片显示在用户对第二个输入矩阵进行更改后,模态对话框中第三个输入矩阵的渲染不正确。

在上面的代码中,您只传递了第二个矩阵的前两列:

          input$curveBaseRate[,1],
          input$curveBaseRate[,2])

老实说,我会去掉所有未至少调用两次的自定义函数(否则没有附加值 - 只会造成混淆)。

请检查以下内容:


library(shiny);library(shinyMatrix);library(shinyjs)

f <- function(action,i){as.character(checkboxInput(paste0(action,i),label=NULL))}
actions       <- c("show", "reset")
tbl           <- t(outer(actions, c(1,2), FUN = Vectorize(f)))
colnames(tbl) <- c("Show", "Reset")
rownames(tbl) <- c("Base", "Spread")

xDflt <- .05 # << default value for x column of input matrix
yDflt <- .01  # << default value for y column of input matrix

flatRate <- function(inputId,x,y){
  matrixInput(inputId, 
              value = matrix(c(x,y), 1, 1, dimnames = list(c("Base rate"),NULL)),
              rows =  list(extend = FALSE, names = TRUE),
              cols =  list(extend = FALSE, names = FALSE, editableNames = FALSE),
              class = "numeric")
} 

curveRate <- function(inputId,w,x,y){
  matrixInput(inputId,
              label = w,
              value = matrix(c(x,y),1,2,dimnames=list(NULL,c("Period (X)","Curved (Y)"))),
              rows =  list(extend = TRUE,  names = FALSE),
              cols =  list(extend = FALSE, names = TRUE, editableNames = FALSE),
              class = "numeric")
}  

ui <- 
  fluidPage(
    tags$head( 
      tags$style(HTML(
        "td .checkbox {margin-top: 0; margin-bottom: 0;}
       td .form-group {margin-bottom: 0;}"
      ))
    ),
    titlePanel("Model"),
    sidebarLayout(
      sidebarPanel(uiOutput("Panel")),
      mainPanel(tabsetPanel(tabPanel("Rates", value=2),id = "tabselected"))
    ) 
  ) 

server <- function(input,output,session)({
  output$Panel <- renderUI({
    conditionalPanel(
      condition = "input.tabselected == 2",
      useShinyjs(),
      helpText("Modeled periods (X):"),
      sliderInput('periods','',min=2,max=120,value=60),
      helpText("Initial rates (Y):"),
      flatRate("flatInput",xDflt,yDflt), 
      helpText("Generate curves (Y|X):"),
      tableOutput("checkboxes"),
      hidden(uiOutput("curveBaseRate")),
      actionButton("addScenarios","Add scenarios")
    )
  }) 
  
  ### Begin checkbox matrix ###
  output[["checkboxes"]] <- 
    renderTable({tbl}, 
                rownames = TRUE, align = "c",
                sanitize.text.function = function(x) x
    )
  
  observeEvent(input[["show1"]], {
    if(input[["show1"]]){
      shinyjs::show("curveBaseRate")
    } else {
      shinyjs::hide("curveBaseRate")
    }
  })
  ### End checkbox matrix ###
  
  output$curveBaseRate <- renderUI({
    req(input$periods,input$flatInput)
    input[["reset1"]]
    curveRate("curveBaseRate",
              "Base rates curve (Y|X):",
              input$periods,
              input$flatInput[1,1])
  })
  
  outputOptions(output, "curveBaseRate", suspendWhenHidden = FALSE)
  
  observeEvent(input$addScenarios, {
    showModal(
      modalDialog(
        matrixInput(inputId = "curveBaseRateAdd",
                    label = "Base rates curve (Y|X):",
                    value = input$curveBaseRate,
                    rows =  list(extend = TRUE,  names = FALSE),
                    cols =  list(extend = FALSE, names = TRUE, editableNames = FALSE),
                    class = "numeric"),
        footer =  modalButton("Close")
      ))
  }) 
  
}) 

shinyApp(ui, server)

使用 do.call 的示例 - 请参阅 flatInputArgs

library(shiny);library(shinyMatrix);library(shinyjs)

f <- function(action,i){as.character(checkboxInput(paste0(action,i),label=NULL))}
actions       <- c("show", "reset")
tbl           <- t(outer(actions, c(1,2), FUN = Vectorize(f)))
colnames(tbl) <- c("Show", "Reset")
rownames(tbl) <- c("Base", "Spread")

xDflt <- .05 # << default value for x column of input matrix
yDflt <- .01  # << default value for y column of input matrix

flatInputArgs <- list(inputId = "flatInput", 
            value = matrix(c(xDflt,yDflt), 1, 1, dimnames = list(c("Base rate test"),NULL)),
            rows =  list(extend = FALSE, names = TRUE),
            cols =  list(extend = FALSE, names = FALSE, editableNames = FALSE),
            class = "numeric")

curveRate <- function(inputId,w,x,y){
  matrixInput(inputId,
              label = w,
              value = matrix(c(x,y),1,2,dimnames=list(NULL,c("Period (X)","Curved (Y)"))),
              rows =  list(extend = TRUE,  names = FALSE),
              cols =  list(extend = FALSE, names = TRUE, editableNames = FALSE),
              class = "numeric")
}  

ui <- 
  fluidPage(
    tags$head( 
      tags$style(HTML(
        "td .checkbox {margin-top: 0; margin-bottom: 0;}
       td .form-group {margin-bottom: 0;}"
      ))
    ),
    titlePanel("Model"),
    sidebarLayout(
      sidebarPanel(uiOutput("Panel")),
      mainPanel(tabsetPanel(tabPanel("Rates", value=2),id = "tabselected"))
    ) 
  ) 

server <- function(input,output,session)({
  output$Panel <- renderUI({
    conditionalPanel(
      condition = "input.tabselected == 2",
      useShinyjs(),
      helpText("Modeled periods (X):"),
      sliderInput('periods','',min=2,max=120,value=60),
      helpText("Initial rates (Y):"),
      do.call(matrixInput, flatInputArgs),
      helpText("Generate curves (Y|X):"),
      tableOutput("checkboxes"),
      hidden(uiOutput("curveBaseRate")),
      actionButton("addScenarios","Add scenarios")
    )
  }) 
  
  ### Begin checkbox matrix ###
  output[["checkboxes"]] <- 
    renderTable({tbl}, 
                rownames = TRUE, align = "c",
                sanitize.text.function = function(x) x
    )
  
  observeEvent(input[["show1"]], {
    if(input[["show1"]]){
      shinyjs::show("curveBaseRate")
    } else {
      shinyjs::hide("curveBaseRate")
    }
  })
  ### End checkbox matrix ###
  
  output$curveBaseRate <- renderUI({
    req(input$periods,input$flatInput)
    input[["reset1"]]
    curveRate("curveBaseRate",
              "Base rates curve (Y|X):",
              input$periods,
              input$flatInput[1,1])
  })
  
  outputOptions(output, "curveBaseRate", suspendWhenHidden = FALSE)
  
  observeEvent(input$addScenarios, {
    showModal(
      modalDialog(
        matrixInput(inputId = "curveBaseRateAdd",
                    label = "Base rates curve (Y|X):",
                    value = input$curveBaseRate,
                    rows =  list(extend = TRUE,  names = FALSE),
                    cols =  list(extend = FALSE, names = TRUE, editableNames = FALSE),
                    class = "numeric"),
        footer =  modalButton("Close")
      ))
  }) 
  
}) 

shinyApp(ui, server)