Créer un nombre dynamique d'éléments d'entrée avec R / brillant

j'écris une application brillante pour visualiser les régimes d'assurance de ma compagnie. Voici ce que j'aimerais arriver:

  • j'aurai un selectInput ou sliderInput où l'utilisateur choisira le nombre de personnes sur son plan médical
  • un nombre correspondant de glisseurs double face apparaîtra (un pour chaque membre)
  • ils peuvent alors entrer leurs estimations pour les frais médicaux les meilleurs / les pires cas pour chaque membre sur son plan
  • j'ai un code qui va prendre ces estimations et créer côte à côte des tracés illustrant le coût prévisionnel sur les trois offres de plan afin qu'ils puissent décider lequel est le moins cher basé sur leurs estimations

Voici mon fichier actuel ui.R avec des entrées codées, simulant une famille de quatre:

shinyUI(pageWithSidebar(

  headerPanel("Side by side comparison"),

  sidebarPanel(

    selectInput(inputId = "class", label = "Choose plan type:",
                list("Employee only" = "emp", "Employee and spouse" = "emp_spouse",
                     "Employee and child" = "emp_child", "Employee and family" = "emp_fam")),

    sliderInput(inputId = "ind1", label = "Individual 1",
                min = 0, max = 20000, value = c(0, 2500), step = 250),

    sliderInput(inputId = "ind2", label = "Individual 2",
                min = 0, max = 20000, value = c(0, 2500), step = 250),

    sliderInput(inputId = "ind3", label = "Individual 3",
                min = 0, max = 20000, value = c(0, 2500), step = 250),

    sliderInput(inputId = "ind4", label = "Individual 4",
                min = 0, max = 20000, value = c(0, 2500), step = 250)
    ),

  mainPanel(
    tabsetPanel(  
    tabPanel("Side by Side", plotOutput(outputId = "main_plot", width = "100%")),
    tabPanel("Summary", tableOutput(outputId = "summary"))
  )
)))

voici à quoi il ressemble (les sections d'extrémité transparentes sont les résultat des cotisations à L'ass de deux des régimes. J'ai pensé que c'était une bonne façon de montrer à la fois les primes et les frais médicaux tout en montrant l'impact de la contribution de la société HSA. Ainsi, il suffit de comparer la longueur des couleurs solides).

shiny-example

j'ai vu des exemples comme ce où L'entrée de L'UI elle-même est fixe (dans ce cas, un checkboxGroupInput existe, mais son contenu est adapté basé sur le choix d'une autre entrée D'UI), mais je n'ai pas vu d'exemples d'adaptation du nombre (ou, disons, le type) d'éléments d'entrée générés à la suite du contenu D'une autre entrée D'UI.

des suggestions à ce sujet (est-ce même possible)?


mon dernier recours sera de créer, disons, 15 sliders d'entrée et de les initialiser à zéro. Mon code fonctionne très bien, mais j'aimerais nettoyer l'interface par pas avoir à créer que beaucoup de glisseurs juste pour l'utilisateur occasionnel qui a une très grande famille.


mise à jour basée sur la réponse de Kevin Ushay

j'ai essayé d'aller la route server.R et avoir ceci:

shinyServer(function(input, output) {

  output$sliders <- renderUI({
    members <- as.integer(input$members) # default 2
    max_pred <- as.integer(input$max_pred) # default 5000
    lapply(1:members, function(i) {
      sliderInput(inputId = paste0("ind", i), label = paste("Individual", i),
                  min = 0, max = max_pred, value = c(0, 500), step = 100)
    })

  })

})

immédiatement après, j'essaie d'extraire les valeurs de input pour les dépenses de chaque individu:

expenses <- reactive({
    members <- as.numeric(input$members)

    mins <- sapply(1:members, function(i) {
      as.numeric(input[[paste0("ind", i)]])[1]
    })

    maxs <- sapply(1:members, function(i) {
      as.numeric(input[[paste0("ind", i)]])[2]
    })

    expenses <- as.data.frame(cbind(mins, maxs))
})

Enfin, j'ai deux fonctions qui créent des objets pour stocker une base de données pour tracer basé sur les estimations faibles et élevées de frais médicaux. Ils sont appelés best_case et worst_case et tous les deux ont besoin de l'objet expenses pour travailler, donc je l'appelle comme ma première ligne comme j'ai appris de cette question

best_case <- reactive({

    expenses <- expenses()

    ...

)}

j'ai eu quelques erreurs, donc j'ai utilisé browser() pour passer à travers le expenses bit et remarqué des choses particulières comme input$ind1 ne semble pas exister à l'intérieur de la fonction expenses .

j'ai aussi joué avec divers print() pour voir ce qui se passait. Le plus frappant est quand je fais print(names(input)) comme la toute première ligne dans la fonction:

[1] "class"    "max_pred" "members" 

[1] "class"    "ind1"     "ind2"     "max_pred" "members" 

je reçois deux sorties, je crois que c'est en raison de la définition de expenses et à l'appel. Étrangement... Je n'ai pas de troisième quand worst_case utilise la même ligne expenses <- expense() .

si je fais quelque chose comme print(expenses) à l'intérieur de ma fonction expenses , je reçois aussi des doublons:

# the first
  mins maxs
1   NA   NA
2   NA   NA

# the second
  mins maxs
1    0  500
2    0  500

des conseils sur la raison pour laquelle mes éléments input pour ind1 et ind2 ne se présenteraient pas jusqu'à ce que expenses soit appelé la deuxième fois et ainsi empêcher la base de données d'être créé correctement?

54
demandé sur Community 2013-10-02 09:23:09

3 réponses

vous pouvez gérer la génération de L'élément UI dans server.R , donc vous avez quelque chose comme:

ui.R
----

shinyUI( pageWithSideBar(
    ...
    selectInput("numIndividuals", ...)
    uiOutput("sliders"),
    ...
))

et

server.R
--------

shinyServer( function(input, output, session) {

  output$sliders <- renderUI({
    numIndividuals <- as.integer(input$numIndividuals)
    lapply(1:numIndividuals, function(i) {
      sliderInput(...)
    })
  })


})

quand j'ai des éléments D'UI qui dépendent des valeurs d'autres éléments D'UI, je trouve plus facile de les générer dans server.R .

il est utile de comprendre que toutes les _Input fonctions juste générer HTML . Quand vous voulez générer ce HTML dynamique il est logique de le déplacer à server.R . Et peut-être que l'autre chose qui mérite d'être soulignée est qu'il est normal de retourner un list D'éléments HTML dans un appel renderUI .

42
répondu Kevin Ushey 2013-10-02 06:48:30

vous pouvez accéder aux variables dynamiquement nommées de shiny en utilisant cette syntaxe:

input[["dynamically_named_element"]]

donc dans votre exemple ci-dessus, si vous initialisez vos éléments de slider comme

# server.R

output$sliders <- renderUI({
  members <- as.integer(input$members) # default 2
  max_pred <- as.integer(input$max_pred) # default 5000
  lapply(1:members, function(i) {
    sliderInput(inputId = paste0("ind", i), label = paste("Individual", i),
                min = 0, max = max_pred, value = c(0, 500), step = 100)
  })
})

# ui.R

selectInput("num", "select number of inputs", choices = seq(1,10,1))
uiOutput("input_ui")

vous pouvez imprimer les valeurs à une table en utilisant le suivant

# server.R

output$table <- renderTable({
    num <- as.integer(input$num)

    data.frame(lapply(1:num, function(i) {
      input[[paste0("ind", i)]]
    }))
  })

# ui.R

tableOutput("table")

voir ici pour un exemple de travail brillant. Travail essentiel ici .

Source: Joe Première réponse de Cheng, environ à mi-chemin en bas ce fil

15
répondu christopherlovell 2018-08-30 15:43:37

vous pouvez générer la barre latérale avec do.call et lapply , quelque chose comme:

# create the first input, which isn't dynamic
sel.input = selectInput(inputId = "class", label = "Choose plan type:",
            list("Employee only" = "emp", "Employee and spouse" = "emp_spouse",
                 "Employee and child" = "emp_child", "Employee and family" = "emp_fam"))

num.individuals = 5  # determine the number of individuals here

# concatenate the select input and the other inputs
inputs = c(list(sel.input), lapply(1:num.individuals, function(i) {
    sliderInput(inputId = paste0("ind", i), label = paste("Individual", i), min = 0, max = 20000, value = c(0, 2500), step = 250)
}))

sidebar.panel = do.call(sidebarPanel, inputs)
5
répondu David Robinson 2013-10-02 05:35:49