使用R / Shiny创建动态数量的输入元素 [英] Create dynamic number of input elements with 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
orsliderInput
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屋!