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
ousliderInput
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).
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?
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
.
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
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)