4
votes

Comment formater l'entrée de Shiny updated numericInput sans changer la valeur réelle?

Dans l'exemple suivant, le numberInput "Number C" est calculé et mis à jour comme "Number A" divisé "Number B". Lorsque le résultat est une décimale infinie, cela conduit à un nombre avec beaucoup de chiffres au "Nombre C". Je voudrais arrondir la décimale infinie au deuxième ou au troisième des chiffres, ce qui rend l'apparence plus facile à lire pour "Number C". Dans le même temps, cependant, je ne veux pas appliquer la fonction round à num_C car dans le monde réel, je veux appliquer le "Nombre C" pour d'autres calculs et Je veux utiliser le numéro tel quel. En d'autres termes, je veux trouver un moyen de formater l'apparence du nombre mais pas la valeur réelle, comme formater une cellule dans une feuille de calcul Excel pour n'afficher que des chiffres limités mais ne pas modifier la valeur réelle. Est-ce possible de le faire dans Shiny?

library(shiny)

# Define UI
ui <- fluidPage(
  numericInput(inputId = "A", "Number A", value = 2),
  numericInput(inputId = "B", "Number B", value = 3),
  numericInput(inputId = "C", "Number C [A/B]", value = 1)
)

# Server logic
server <- function(input, output, session){

  observe({
    req(input$A, input$B)
    num_C <- input$A/input$B
    updateNumericInput(
      inputId = "C",
      session = session,
      value = num_C
    )
  })
}

# Complete app with UI and server components
shinyApp(ui, server)


4 commentaires

Je suggérerais de déposer un problème GH au repo brillant de Rstudio pour demander un argument de format pour numericInput () et updateNumericInput () .


@Phil Merci pour votre commentaire. C'est quelque chose que nous pouvons faire, mais j'aimerais attendre un moment pour m'assurer de ne manquer aucune solution.


@Phil: avec ma compréhension (limitée) du input type = „number“ sous-jacent, je ne pense pas que ce soit vraiment possible car l'entrée $ C correspond à la valeur et c'est tout ce que ce widget a vraiment, c'est-à-dire qu'il n'y a pas de propriété format et si l'on utilisait par exemple motif pour restreindre à deux décimales, alors la valeur et input $ C seront affectés de la même manière et vous n'avez plus la valeur de précision complète. Je pense que vous devez le stocker séparément, voir par exemple la réponse de Maurits.


@RolandASc c'est pourquoi je pense que cela devrait être une demande de fonctionnalité à l'équipe Shiny de RStudio


3 Réponses :


4
votes

Vous pouvez utiliser une expression réactive pour num_C

library(shiny)

# Define UI
ui <- fluidPage(
  numericInput(inputId = "A", "Number A", value = 2),
  numericInput(inputId = "B", "Number B", value = 3),
  numericInput(inputId = "C", "Number C [A/B]", value = 1),
  textOutput("value"),
  textOutput("rounded_value")
)

# Server logic
server <- function(input, output, session){

  num_C <- reactiveValues(
      value = NULL,
      rounded_value = NULL
  )

  observeEvent(input$A | input$B, {
      num_C$value <- input$A / input$B
      num_C$rounded_value <- round(num_C$value, 1)
  })

  observeEvent(input$C, {
      num_C$value <- input$C
      num_C$rounded_value <- input$C
  })

  output$value <- renderText(
      sprintf("Number C = %f", num_C$value)
  )
  output$rounded_value <- renderText(
      sprintf("Number C (rounded) = %f", num_C$rounded_value)
  )

}

# Complete app with UI and server components
shinyApp(ui, server)

num_C () renverra alors le valeur "non arrondie", alors que nous utilisons le format de valeur arrondie (num_C (), digits = 2) dans updateNumericInput .


Une mise à jour partielle

Pour ce que ça vaut, voici une mise à jour incomplète

library(shiny)

# Define UI
ui <- fluidPage(
  numericInput(inputId = "A", "Number A", value = 2),
  numericInput(inputId = "B", "Number B", value = 3),
  numericInput(inputId = "C", "Number C [A/B]", value = 1)
)

# Server logic
server <- function(input, output, session){

  num_C <- reactive({
      req(input$A, input$B)
      input$A / input$B
  })

  observe(
      updateNumericInput(
          inputId = "C",
          session = session,
          value = format(num_C(), digits = 2))
      )

}

# Complete app with UI and server components
shinyApp(ui, server)

L'idée est d'utiliser reactiveValues ​​ pour suivre la précision totale et la valeur arrondie du nombre C. Cela fonctionne aussi loin que

  1. changer les nombres A, B à numericInput calculera (et affichera) correctement la précision complète et les nombres arrondis pour C dans les textOutput s.
  2. changer le nombre C via numericInput affichera également correctement le nombre de précision complète (qui est égal à l'arrondi) dans les textOutput s.

Cependant , je n'ai pas réussi à utiliser updateNumericInput pour mettre à jour la valeur de C avec le nombre arrondi lorsque les nombres A et B ont été modifiés.

À suivre ...


9 commentaires

Merci pour votre réponse. J'ai voté pour votre message. Cependant, maintenant input $ C serait une valeur arrondie, ce que je veux dans l'apparence, mais pas pour le calcul ultérieur. Par exemple, plus tard, je ferai input $ C * 100000 . Je veux calculer cette valeur en utilisant 0.666666667 , mais en affichant l'apparence comme 0.67 .


@www L'idée était d'utiliser num_C () au lieu de input $ C . Le premier aura la valeur de précision totale. Cela ne fonctionnerait-il pas?


C'est une bonne idée. Mais dans ce cas, je veux donner aux utilisateurs la flexibilité que lorsqu'ils changent la valeur d'entrée en "Number C", par exemple, 0.25 , le calcul suivant utilisera input $ C < / code> ( 0.25 ), pas num_c () ( 0.67 ).


@www Hmm, donc juste pour clarifier: vous aimeriez changer l'entrée de "C" soit (1) indirectement en changeant "A" et / ou "B", auquel cas "C = A / B" devrait être arrondi, ou (2) directement en changeant "C" lui-même, auquel cas il ne devrait pas y avoir d'arrondi. Est-ce exact? Je pense que ce cas est plus délicat à réaliser. Je vais devoir y réfléchir.


Disons que C = A / B ou le nombre directement mis dans C est un nombre décimal, tel que 0.6666667 . Je veux l'arrondir à 0,67 dans son apparence, mais dans les coulisses, il est toujours 0.6666667 . Ce que je demande n'est peut-être pas possible, mais dans une feuille de calcul Excel, il est courant de formater le numéro de cellule avec arrondi, mais pas de modifier les valeurs de cellule réelles. Je voudrais avoir le même comportement que mon numericInput C .


@www Ok, je pense que je comprends. Mais la difficulté supplémentaire ici est que vous voulez avoir deux options pour changer "C": Soit directement ou indirectement en changeant "A" et / ou "B". Avoir un nombre arrondi uniquement pour l'affichage est possible pour l'un ou l'autre scénario séparé, mais je ne sais pas (encore) comment les lier.


Merci. Je sais que ce que je demande n'est peut-être pas possible. Mais comme c'est courant dans Excel, je veux poster ma question ici pour voir si quelqu'un a une idée. C'est OK si la réponse n'est pas possible.


@www Je suis entièrement d'accord avec vous, cela semble être une fonctionnalité / une demande sensée. Je n'ai pas abandonné là-dessus ;-) Je reviendrai là-dessus demain matin (il se fait tard ici en Australie).


Merci beaucoup pour votre aide. Je continuerai également à examiner cela.



2
votes

Je viens de changer un peu le code de Maurits en ajoutant un flag et un observeEvent pour la value qui contient la valeur réelle. Désormais, "C" affichera toujours la valeur arrondie, mais value stockera la valeur réelle.

J'espère que cela sera utile ...

library(shiny)

# Define UI
ui <- fluidPage(
  numericInput(inputId = "A", "Number A", value = 2),
  numericInput(inputId = "B", "Number B", value = 2),
  numericInput(inputId = "C", "Number C [A/B]", value = 1),
  textOutput("value"),
  textOutput("rounded_value")
)

# Server logic
server <- function(input, output, session){

  value <- reactiveVal(1)
  rounded_value <- reactiveVal()
  flag <- reactiveVal(T)

  observeEvent(input$A | input$B, {
    value(input$A / input$B)
    rounded_value(round(value(), 2))
  })


  observeEvent(input$C, {
    if(flag()){
      value(input$C)
      rounded_value(round(input$C, 2))
    }else{
      flag(T)
    }
  })

  observeEvent(value(),{
    flag(F)
    updateNumericInput(session, "C", value = rounded_value())
  })



  output$value <- renderText(
    sprintf("Number C = %f", value())
  )
  output$rounded_value <- renderText(
    sprintf("Number C (rounded) = %f", rounded_value())
  )

}

# Complete app with UI and server components
shinyApp(ui, server)


1 commentaires

Merci pour votre réponse. Je vous ai d'abord donné un vote favorable. Je suis occupé aujourd'hui, mais je passerai en revue votre réponse et vous ferai savoir si cela fonctionne lorsque j'aurai le temps.



3
votes

La chose la plus simple à faire serait de compter la longueur de input $ C et d'appliquer un chiffre d'arrondi - tout en enregistrant la vraie valeur en tant que variable factice:

library(shiny)
#this counts the decimal places of a value
decimalplaces <- function(x) {
  if ((x %% 1) != 0) {
    nchar(strsplit(sub('0+$', '', as.character(x)), ".", fixed=TRUE)[[1]][[2]])
  } else {
    return(0)
  }
}

# Define UI
ui <- fluidPage(
  numericInput(inputId = "A", "Number A", value = 2),
  numericInput(inputId = "B", "Number B", value = 3),
  numericInput(inputId = "C", "Number C [A/B]", value = 1)
)

# Server logic
server <- function(input, output, session){
  num_C <- reactiveValues(
    value = NULL
  )

  observeEvent(input$A|input$B,{

    num_C$value <- input$A/input$B
    updateNumericInput(
      inputId = "C",
      session = session,
      value = round(num_C$value,2)
    )
  })

  observeEvent(input$C, {
    #identify if num_C is greater than 2 dc
  if(decimalplaces(input$C)>2){
    num_C$value <- input$C
  }
    updateNumericInput(
      inputId = "C",
      session = session,
      value = round(num_C$value,2)
    )

  })
  #check that everything is working correctly. 
  observeEvent( input$A|input$B|input$C,{
    print(paste0(num_C$value," ", input$C))
  })
}

# Complete app with UI and server components
shinyApp(ui, server)

merci à @Maurits Evers pour la majorité du code.


1 commentaires

Merci pour votre réponse. Je l'ai voté et je vérifierai plus tard.