rshinynumeric-input

How to create mutually dependent numeric Input in Shiny?


Please find the working code below. Basically, there are 3 numericInputs which allows the user to change anyone at a time and the other 2 should adapt themselves resulting in 1

A + B + C = 1 However, since they are interlinked, they seems to be unstable. How can we make it stable and allow the user to change any 1 variable: A and other 2 change themselves summing to 1.

Based on the answer here, created a working code as shown below:

Connect mutually dependent shiny input values

Dependent inputs in Shiny application with R

library(shiny)
ui <- shinyUI(fluidPage(
  titlePanel("Mutually Dependent Input Values"),
  sidebarLayout(
    sidebarPanel(
      numericInput("A", "A",.333),
      numericInput("B", "B",.333),
      numericInput("C", "C",.333)
    ),
    mainPanel(
      verbatimTextOutput("result")
    )
  )
)) 
server <- shinyServer(function(input, output,session) {

  observeEvent(input$A,{
    newB <- 1 - input$A - input$C 
    updateNumericInput(session, "B", value = newB) 
    newC <- 1 - input$A - input$B 
    updateNumericInput(session, "C", value = newC) 
  })
  observeEvent(input$B,{
    newC <- 1 - input$B - input$A 
    updateNumericInput(session, "C", value = newC) 
    newA <- 1 - input$B - input$C 
    updateNumericInput(session, "A", value = newA) 
  })
  observeEvent(input$C,{
    newA <- 1 - input$C - input$B 
    updateNumericInput(session, "A", value = newA) 
    newB <- 1 - input$C - input$C 
    updateNumericInput(session, "B", value = newB) 
  })


})
shinyApp(ui,server)

Basically, user will be free to update any of the numericInput: A, B or C. Other 2 numericInput should adapt itself to sum up to 1. Currently, since they are interlinked, they seems to be unstable (please find the above code to regenerate the error)

####### Different approach with own solution

ui <- fluidPage(
  column(6, 
         tags$h2("Set parameters"),
         numericInput("valueA", "Value1", value = .333, min = 0, max = 1, step = .1),
         numericInput("valueB", "Value2", value = .333, min = 0, max = 1, step = .1),
         numericInput("valueC", "Value3", value = .333, min = 0, max = 1, step = .1)
  ),
  column(6,
         uiOutput("ui")
  )
)

server <- function(input, output, session) {
  output$ui <- renderUI( {
    tagList(
      tags$h2("Display in %"),
      numericInput("obs1", "Label1", value = 100 * (input$valueA / (input$valueA + input$valueB + input$valueC))),
      numericInput("obs2", "Label2", value = 100 * (input$valueB / (input$valueA + input$valueB + input$valueC))),
      numericInput("obs2", "Label2", value = 100 * (input$valueC / (input$valueA + input$valueB + input$valueC)))
    )
  })
}

shinyApp(ui, server)   


Solution

  • You need to do two main things:

    That being said, note that the order of changing the input makes a difference in this approach, this is most noticeable at the start when you give the input their first set of values.

    There might be a better approach to solve this, but this is what I can think of, so here it goes.

    The UI:

    library(shiny)
    ui <- shinyUI(fluidPage(
      titlePanel("Mutually Dependent Input Values"),
      sidebarLayout(
        sidebarPanel(
          numericInput("A", "A", NA),
          numericInput("B", "B", NA),
          numericInput("C", "C", NA)
        ),
        mainPanel(
          verbatimTextOutput("result")
        )
      )
    )) 
    

    The Server:

    server <- shinyServer(function(input, output, session) {
      sum <- reactive({
        validate(
          need(is.numeric(input$A), 'A is not a number, only numbers are allowed'),
          need(is.numeric(input$B), 'B is not a number, only numbers are allowed'),
          need(is.numeric(input$C), 'C is not a number, only numbers are allowed')
        )
        input$A + input$B + input$C})
    
      observeEvent(input$A,{
          newB <- 1 - input$A - input$C
          updateNumericInput(session, "B", value = newB)
      })
    
      observeEvent(input$B,{
          newC <- 1 - input$B - input$A
          updateNumericInput(session, "C", value = newC)
      })
    
      observeEvent(input$C,{
          newA <- 1 - input$C - input$B
          updateNumericInput(session, "A", value = newA)
      })
    
      output$result <- renderPrint({
        print(sprintf("A=%.3f      B=%.3f      C=%.3f  ---->   A + B + C = %.0f", 
                      input$A, input$B, input$C, sum()))
      })
    })
    

    Run:

    shinyApp(ui,server)