使用R / Shiny创builddynamic数量的input元素

我正在写一个Shiny的应用程序,在我的公司可视化保险福利计划。 这是我想要发生的事情:

  • 我将有一个selectInputsliderInput ,用户将select他们的医疗计划中的个人数量
  • 将出现匹配数量的双面滑块(每个成员一个)
  • 然后,他们可以input他们对每个成员的最佳/最坏情况医疗费用的估计
  • 我有代码,将采取这些估计和创build并行的情节说明三个计划产品的预测成本,所以他们可以决定哪一个是最便宜的根据他们的估计

这是我目前的ui.R文件,硬编码input,模拟一个四口之家:

 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")) ) ))) 

看起来是这样的(透明的末端部分是两个计划的HSA贡献的结果,我认为这是一个很好的方式来显示保费和医疗费用,同时显示公司HSA贡献的影响,因此,你只是比较单色的长度)。

光泽-示例

我已经看到这样的例子,在UIinput本身是固定的(在这种情况下,一个checkboxGroupInput存在,但其内容是根据另一个UIinput的select),但我没有看到定制数字的例子或者说,input)作为另一个UIinput内容的结果而产生的input元素。

任何build议(这是甚至可能)?


我最后的办法是创build15个input滑块,并将它们初始化为零。 我的代码将工作得很好,但我想清理界面,而不必为偶尔有一个非常大的家庭的用户创build许多滑块。


更新根据凯文Ushay的答案

我试图去server.R路线,并有这样的:

 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) }) }) }) 

紧接着,我尝试从input提取出每个人的费用:

 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)) }) 

最后,我有两个函数,根据低和高的医疗费用估计,创build对象来存储绘图数据框。 他们被称为best_caseworst_case ,都需要expenses对象的工作,所以我把它称为我的第一线,因为我从这个问题

 best_case <- reactive({ expenses <- expenses() ... )} 

我得到了一些错误,所以我使用browser()来通过expenses位,并注意到特殊的东西,如input$ind1似乎并不存在于expenses函数内。

我也玩过各种print()语句来看看发生了什么。 最引人注目的是当我print(names(input))作为函数的第一行:

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

我得到了两个结果 ,我认为这是由于expenses的定义和随后的调用。 奇怪的是…当worst_case使用完全相同的expenses <- expense()行时,我不会得到第三个。

如果我在我的expenses函数里面print(expenses) ,我也会得到重复:

 # the first mins maxs 1 NA NA 2 NA NA # the second mins maxs 1 0 500 2 0 500 

任何提示,为什么我的ind1ind2 input元素不会显示,直到expenses第二次调用,从而防止正确创builddataframe?

你可以在server.R处理UI元素的server.R ,所以你有这样的东西:

 ui.R ---- shinyUI( pageWithSideBar( ... selectInput("numIndividuals", ...) uiOutput("sliders"), ... )) 

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

当我的UI元素依赖于来自其他UI元素的值时,我发现在server.R生成它们是最简单的。

理解所有_Input函数只是生成HTML是有用的。 当你想要dynamic地生成HTML时,把它移动到server.R 。 也许值得强调的另一件事是,可以在renderUI调用中返回一个HTML元素list

您可以使用以下语法从闪存访问dynamic命名的variables:

 input[["dynamically_named_element"]] 

所以在你上面的例子中,如果你初始化你的滑块元素

 # 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") 

您可以使用以下内容将值打印到表格中

 # 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") 

看到这里工作的shiny的例子。 工作要点在这里 。

资料来源:Joe Cheng的第一个答案,大概是这个话题的一半

您可以使用do.calllapply生成侧栏,如下所示:

 # 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)