使用R / Shiny创建动态数量的输入元素 [英] Create dynamic number of input elements with R/Shiny

查看:141
本文介绍了使用R / Shiny创建动态数量的输入元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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




  • 我会有一个 selectInput sliderInput 用户将选择其医疗计划中的个人数量

  • 将显示匹配数量的双面滑块(

  • 然后,他们可以输入他们对他们计划中每个成员的最佳/最差情况医疗费用的估计。

  • 我有代码将采取这些估计,并创建并列计划,说明三个计划产品的预测成本,以便他们可以根据他们的估计来决定哪一个是最便宜的。



这是我当前的 ui.R 文件与硬编码输入,模拟一个四口之家:

  ShinyUI(pageWithSidebar(

headerPanel(并排比较),

sidebarPanel(

selectInput(inputId =class,label =选择计划类型:,
list(Employee只有=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(并排,plotOutput(outputId =main_plot,width =100%)),
tabPanel(Summary,tableOutput (outputId =summary))

)))

正是它看起来像(透明端部是由两个计划HSA贡献的结果。我认为这是一个很好的方法来显示保费和医疗费用,同时显示公司HSA贡献的影响。因此,您只需比较固体颜色的长度。)





我看过例子像这样,UI输入本身是固定的(在这种情况下,一个 checkboxGroupInput 存在,但其内容是根据另一个UI输入),但是我没有看到由于另一个UI输入内容而导致的输入元素的数量(或者说,类型)的剪裁



对此有任何建议(甚至可能)?






我最后的手段是创建15个输入滑块,并将其初始化为零。我的代码可以正常工作,但是我想通过不需要为具有非常大的家族的偶尔用户创建许多滑块来清理界面。



< hr>

基于Kevin Ushay的回答更新



我试图去 server.R 路由,并具有以下功能:

  shinyServer(function(input,output){ 

输出$滑块< - renderUI({
成员< - as.integer(输入$ 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)
})

})

})

之后,我尝试从输入每个人的费用:

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

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

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

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

最后,我有两个功能创建对象根据低和高的医疗费用估算来存储用于绘图的数据框架。它们被称为 best_case worst_case ,并且都需要费用反对工作,所以我把它称为我的第一行,从我从这个问题中得到的

  best_case<  - 被动({

费用< - 费用()

...

)}

我有一些错误,所以我使用 browser()来逐步执行费用位,并注意到特殊的东西,如code $ input $ ind1 似乎不存在于费用函数中。



还在各种各样的 print()语句中播放,看看发生了什么。最引人注目的是当我做 print(names(input))作为函数的第一行:

  [1]classmax_predmembers

[1]classind1ind2max_predmembers

我得到两个输出,我相信是由于费用并随后调用它。奇怪的是,当 worst_case 使用完全相同的费用< - 费用()



如果我在我的费用之内,像 print(费用) / code>函数,我也得到重复的:

 #第一个
mins maxs
1 NA NA
2 NA NA

#第二个
最小
1 0 500
2 0 500

有关为什么我的输入元素为 ind1 ind2 不会显示直到费用被第二次调用,从而防止数据框架被正确创建?

解决方案

您可以处理 server.R中的UI元素的生成/ code>,所以你有这样的东西:

  ui.R 
----

ShinyUI(pageWithSideBar(
...
selectInput(numIndividuals,...)
uiOutput(sl iders),
...
))

  server.R 
--------

ShinyServer(function(input,输出,会话){

输出$滑块< - renderUI({
numIndividuals< - as.integer(input $ numIndividuals)
lapply(1:numIndividuals,function i){
sliderInput(...)
})
})


})

当我的UI元素依赖于其他UI元素的值时,我发现在 server.R



了解所有的 _Input 函数只是生成HTML 的。当您想要生成HTML 动态时,将其移动到 server.R 是有意义的。也许值得强调的另一件事是,在 renderUI 调用中返回一个HTML元素的列表


I'm writing a Shiny app for visualizing insurance benefit plans at my company. Here is what I'd like to happen:

  • I'll have a selectInput or sliderInput where the user will choose the number of individuals on their medical plan
  • A matching number of double sided sliders will appear (one for each member)
  • They can then input their estimates for best/worst case medical expenses for each member on their plan
  • I have code that will take those estimates and create side by side plots illustrating the forecast cost on the three plan offerings so they can decide which one is least expensive based on their estimates

Here's my current ui.R file with hard coded inputs, simulating a family of four:

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

Here's what it looks like (the transparent end sections are the result of HSA contributions from two of the plans. I thought it was a nice way to show both the premiums and medical expenses while showing the impact of the company HSA contribution. Thus, you'd just compare the length of the solid colors).

I've seen examples like this where the UI input itself is fixed (in this case, one checkboxGroupInput exists, but its contents are tailored based on the choice from another UI input), but I've not seen examples of tailoring the number (or, say, type) of input elements spawned as the result of another UI input's contents.

Any suggestions on this (is it even possible)?


My last resort will be to create, say, 15 input sliders and initialize them to zero. My code will work just fine, but I'd like to clean up the interface by not having to create that many sliders just for the occasional user who has a very large family.


Update based on Kevin Ushay's answer

I tried to go the server.R route and have this:

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

  })

})

Immediately afterwards, I try and extract the values out of input for each individual's expenses:

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

Lastly, I have two functions that create objects to store a data frame for plotting based on the low and high medical expense estimates. They're called best_case and worst_case and both need the expenses object to work, so I call it as my first line as I learned from this question

best_case <- reactive({

    expenses <- expenses()

    ...

)}

I got some errors, so I used browser() to step through the expenses bit and noticed peculiar things like input$ind1 not seeming to exist from within the expenses function.

I also played around with various print() statements in it to see what was happening. The most striking is when I do print(names(input)) as the very first line in the function:

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

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

I get two outputs, which I believe is due to the defining of expenses and subsequent calling of it. Strangely... I don't get a third when worst_case uses the exact same expenses <- expense() line.

If I do something like print(expenses) inside of my expenses function, I also get duplicates:

# the first
  mins maxs
1   NA   NA
2   NA   NA

# the second
  mins maxs
1    0  500
2    0  500

Any tips on why my input elements for ind1 and ind2 wouldn't show up until expenses is called the second time and thus prevent the data frame from being created correctly?

解决方案

You could handle generation of the UI element in server.R, so you have something like:

ui.R
----

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

and

server.R
--------

shinyServer( function(input, output, session) {

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


})

When I have UI elements that depend on values from other UI elements, I find it easiest to generate them in server.R.

It's useful to understand that all of the _Input functions just generate HTML. When you want to generate that HTML dynamically it makes sense to move it to server.R. And perhaps the other thing worth emphasizing is that it's okay to return a list of HTML 'elements' in a renderUI call.

这篇关于使用R / Shiny创建动态数量的输入元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆