R 在Shiny应用程序中动态更新选择列表
我有一个应用程序,允许用户指定他们将选择多少预测值,并相应地选择每个预测值。有谁能告诉我,我怎样才能为每个后续预测做出反映之前预测的选择 预测选择 例如,如果预测器1=cyl,那么预测器2的选择将是{disp,drat,hp,mpg}。然后,如果预测器2=hp,那么预测器3的选择将是{disp,drat,mpg}R 在Shiny应用程序中动态更新选择列表,r,shiny,R,Shiny,我有一个应用程序,允许用户指定他们将选择多少预测值,并相应地选择每个预测值。有谁能告诉我,我怎样才能为每个后续预测做出反映之前预测的选择 预测选择 例如,如果预测器1=cyl,那么预测器2的选择将是{disp,drat,hp,mpg}。然后,如果预测器2=hp,那么预测器3的选择将是{disp,drat,mpg} ## libraries library(tidyverse) library(shiny) ## store variable names var_names <- mtca
## libraries
library(tidyverse)
library(shiny)
## store variable names
var_names <- mtcars %>% select(1:5) %>% colnames
ui <- fluidPage(
## select number of variables to use
numericInput(inputId = "n_preds",
label = "Number Predictors",
value = 1,
min = 1,
max = length(var_names)),
## store UI object for future use
uiOutput("vars")
)
server <- function(input, output, session) {
## create objects to store individual predictors
predictors <- reactive(paste0("Predictor ", seq_len(input$n_preds)))
## generate dynamic UI
output$vars <- renderUI({
map(predictors(), ~ selectInput(inputId = .x,
label = .x,
choices = var_names,
selected = isolate(input[[.x]])) %||% "")
})
}
shinyApp(ui, server)
您可以尝试使用conditionalPanel根据input$inputID变量反应性地显示不同的UI输入。这需要在UI函数上实现
或者,switch函数听起来也可以满足您的需要。您可以尝试使用conditionalPanel根据您的input$inputID变量反应性地显示不同的UI输入。这需要在UI函数上实现
或者,switch函数听起来也可以满足您的需要。实现这一点的方法可能比循环更优雅,但您可以使用insertUI和observeEvent的组合来动态创建所需的所有不同观察者。它有助于在UI中创建一个容器,在其中插入元素,并将每个selectInput包装在一个div中,以便在预测器的数量发生变化时轻松删除它们:
# libraries
library(tidyverse)
library(shiny)
# store variable names
var_names <- mtcars %>% select(1:5) %>% colnames
ui <- fluidPage(
# select number of variables to use
numericInput(inputId = "n_preds",
label = "Number Predictors",
value = 1,
min = 1,
max = length(var_names)),
# create container for predictors to live in
div(id = "preds-container")
)
server <- function(input, output, session) {
observeEvent(input$n_preds, {
# clear out previous predictors
removeUI(selector = ".pred", multiple = TRUE)
# define function for updating select input
updatePredictor <- function(session, input, i, var_names) {
updateSelectInput(session, paste("predictor", i, sep = "_"),
# get remaining possible options from previous inputs
choices = setdiff(
var_names,
unlist(
reactiveValuesToList(input)[paste("predictor", 1:(i-1), sep = "_")]
))
)
}
# loop through each predictor
for (i in seq_len(input$n_preds)) {
# insert a new predictor
insertUI(selector = "#preds-container",
where = "beforeEnd",
ui = div(class = "pred",
selectInput(inputId = paste("predictor", i, sep = "_"),
label = paste("Predictor", i),
choices = var_names)))
# update to reflect previous choices
updatePredictor(session, input, i, var_names)
# create observers for each of the previous inputs to update the current one
for (j in seq_len(i-1)) {
observeEvent(
# observe predictor j for j = 1, 2, ..., i-1
eventExpr = input[[paste("predictor", j, sep = "_")]],
# update predictor i
handlerExpr = updatePredictor(session, input, i, var_names),
ignoreNULL = FALSE)
}
}
})
}
shinyApp(ui, server)
如果您这样做,我可能会建议您使用一个按钮来触发任何依赖于不同预测器的计算/输出,否则,由于所有对updateSelectInput的调用,您可能会得到许多无意义的重新渲染。可能有比循环更优雅的方法来实现这一点,但是您可以使用insertUI和observeEvent的组合来动态创建所需的所有不同的观察者。它有助于在UI中创建一个容器,在其中插入元素,并将每个selectInput包装在一个div中,以便在预测器的数量发生变化时轻松删除它们:
# libraries
library(tidyverse)
library(shiny)
# store variable names
var_names <- mtcars %>% select(1:5) %>% colnames
ui <- fluidPage(
# select number of variables to use
numericInput(inputId = "n_preds",
label = "Number Predictors",
value = 1,
min = 1,
max = length(var_names)),
# create container for predictors to live in
div(id = "preds-container")
)
server <- function(input, output, session) {
observeEvent(input$n_preds, {
# clear out previous predictors
removeUI(selector = ".pred", multiple = TRUE)
# define function for updating select input
updatePredictor <- function(session, input, i, var_names) {
updateSelectInput(session, paste("predictor", i, sep = "_"),
# get remaining possible options from previous inputs
choices = setdiff(
var_names,
unlist(
reactiveValuesToList(input)[paste("predictor", 1:(i-1), sep = "_")]
))
)
}
# loop through each predictor
for (i in seq_len(input$n_preds)) {
# insert a new predictor
insertUI(selector = "#preds-container",
where = "beforeEnd",
ui = div(class = "pred",
selectInput(inputId = paste("predictor", i, sep = "_"),
label = paste("Predictor", i),
choices = var_names)))
# update to reflect previous choices
updatePredictor(session, input, i, var_names)
# create observers for each of the previous inputs to update the current one
for (j in seq_len(i-1)) {
observeEvent(
# observe predictor j for j = 1, 2, ..., i-1
eventExpr = input[[paste("predictor", j, sep = "_")]],
# update predictor i
handlerExpr = updatePredictor(session, input, i, var_names),
ignoreNULL = FALSE)
}
}
})
}
shinyApp(ui, server)
如果您这样做,我可能会建议您使用一个按钮来触发任何依赖于不同预测值的计算/输出,否则您可能会由于所有对updateSelectInput的调用而得到许多无意义的重新渲染。这里有一个与@cwthom类似的答案,但我并不总是删除每个输入并再次添加它,但是动态地插入/删除新输入。这样做的优点是保留了先前为预测器选择的值。另外,我需要更少的观察员,这可能会快一点
library(tidyverse)
library(shiny)
## store variable names
var_names <- mtcars %>% select(1:5) %>% colnames
ui <- fluidPage(
## select number of variables to use
numericInput(inputId = "n_preds",
label = "Number Predictors",
value = 1,
min = 1,
max = length(var_names)),
# anchor where the inputs get added
div(id = "add_vars")
)
server <- function(input, output, session) {
current_n_preds <- 0
observeEvent(input$n_preds, {
# add inputs
if (input$n_preds > current_n_preds) {
for (i in (current_n_preds + 1):input$n_preds) {
possible_choices <- setdiff(var_names, preds_selected())
insertUI(
selector = "#add_vars",
ui = div(
id = paste0("Predictor_", i),
selectInput(inputId = paste0("Predictor_", i),
label = paste0("Predictor ", i),
choices = possible_choices,
selected = possible_choices[1])
)
)
current_n_preds <<- current_n_preds + 1
}
} else {
# remove inputs
for (i in current_n_preds:(input$n_preds + 1)) {
removeUI(
selector = paste0("#Predictor_", i)
)
current_n_preds <<- current_n_preds - 1
}
}
})
# vector of selected predictors
preds_selected <- reactive({
unlist(lapply(seq_len(input$n_preds), function(i) {
input[[paste0("Predictor_", i)]]
}))
})
# update the inputs
observeEvent(preds_selected(), {
lapply(seq_len(input$n_preds), function(i) {
updateSelectInput(session,
inputId = paste0("Predictor_", i),
choices = c(input[[paste0("Predictor_", i)]],
setdiff(var_names, preds_selected())))
})
})
}
shinyApp(ui, server)
编辑
此解决方案允许您为每个输入选择以下所有选项:
library(tidyverse)
library(shiny)
## store variable names
var_names <- mtcars %>% select(1:5) %>% colnames
ui <- fluidPage(
## select number of variables to use
numericInput(inputId = "n_preds",
label = "Number Predictors",
value = 1,
min = 1,
max = length(var_names)),
# anchor where the inputs get added
div(id = "add_vars")
)
server <- function(input, output, session) {
current_n_preds <- 0
observeEvent(input$n_preds, {
# add inputs
if (input$n_preds > current_n_preds) {
for (i in (current_n_preds + 1):input$n_preds) {
if (i == 1) {
possible_choices <- var_names
} else {
possible_choices <- setdiff(var_names, preds_selected()[1:(i - 1)])
}
insertUI(
selector = "#add_vars",
ui = div(
id = paste0("Predictor_", i),
selectInput(inputId = paste0("Predictor_", i),
label = paste0("Predictor ", i),
choices = possible_choices,
selected = possible_choices[1])
)
)
current_n_preds <<- current_n_preds + 1
}
} else {
# remove inputs
for (i in current_n_preds:(input$n_preds + 1)) {
removeUI(
selector = paste0("#Predictor_", i)
)
current_n_preds <<- current_n_preds - 1
}
}
})
# vector of selected predictors
preds_selected <- reactive({
unlist(lapply(seq_len(input$n_preds), function(i) {
input[[paste0("Predictor_", i)]]
}))
})
# update the inputs
observeEvent(preds_selected(), {
lapply(seq_len(input$n_preds), function(i) {
if (!is.null(input[[paste0("Predictor_", i)]])) {
if (i == 1) {
possible_choices <- var_names
} else {
possible_choices <- setdiff(var_names, preds_selected()[1:(i - 1)])
}
if (input[[paste0("Predictor_", i)]] %in% possible_choices) {
new_value <- input[[paste0("Predictor_", i)]]
} else {
new_value <- possible_choices[1]
}
updateSelectInput(session,
inputId = paste0("Predictor_", i),
choices = possible_choices,
selected = new_value)
}
})
})
}
shinyApp(ui, server)
这里有一个与@cwtom类似的答案,但我并不总是删除每个输入并再次添加,而是动态插入/删除新输入。这样做的优点是保留了先前为预测器选择的值。另外,我需要更少的观察员,这可能会快一点
library(tidyverse)
library(shiny)
## store variable names
var_names <- mtcars %>% select(1:5) %>% colnames
ui <- fluidPage(
## select number of variables to use
numericInput(inputId = "n_preds",
label = "Number Predictors",
value = 1,
min = 1,
max = length(var_names)),
# anchor where the inputs get added
div(id = "add_vars")
)
server <- function(input, output, session) {
current_n_preds <- 0
observeEvent(input$n_preds, {
# add inputs
if (input$n_preds > current_n_preds) {
for (i in (current_n_preds + 1):input$n_preds) {
possible_choices <- setdiff(var_names, preds_selected())
insertUI(
selector = "#add_vars",
ui = div(
id = paste0("Predictor_", i),
selectInput(inputId = paste0("Predictor_", i),
label = paste0("Predictor ", i),
choices = possible_choices,
selected = possible_choices[1])
)
)
current_n_preds <<- current_n_preds + 1
}
} else {
# remove inputs
for (i in current_n_preds:(input$n_preds + 1)) {
removeUI(
selector = paste0("#Predictor_", i)
)
current_n_preds <<- current_n_preds - 1
}
}
})
# vector of selected predictors
preds_selected <- reactive({
unlist(lapply(seq_len(input$n_preds), function(i) {
input[[paste0("Predictor_", i)]]
}))
})
# update the inputs
observeEvent(preds_selected(), {
lapply(seq_len(input$n_preds), function(i) {
updateSelectInput(session,
inputId = paste0("Predictor_", i),
choices = c(input[[paste0("Predictor_", i)]],
setdiff(var_names, preds_selected())))
})
})
}
shinyApp(ui, server)
编辑
此解决方案允许您为每个输入选择以下所有选项:
library(tidyverse)
library(shiny)
## store variable names
var_names <- mtcars %>% select(1:5) %>% colnames
ui <- fluidPage(
## select number of variables to use
numericInput(inputId = "n_preds",
label = "Number Predictors",
value = 1,
min = 1,
max = length(var_names)),
# anchor where the inputs get added
div(id = "add_vars")
)
server <- function(input, output, session) {
current_n_preds <- 0
observeEvent(input$n_preds, {
# add inputs
if (input$n_preds > current_n_preds) {
for (i in (current_n_preds + 1):input$n_preds) {
if (i == 1) {
possible_choices <- var_names
} else {
possible_choices <- setdiff(var_names, preds_selected()[1:(i - 1)])
}
insertUI(
selector = "#add_vars",
ui = div(
id = paste0("Predictor_", i),
selectInput(inputId = paste0("Predictor_", i),
label = paste0("Predictor ", i),
choices = possible_choices,
selected = possible_choices[1])
)
)
current_n_preds <<- current_n_preds + 1
}
} else {
# remove inputs
for (i in current_n_preds:(input$n_preds + 1)) {
removeUI(
selector = paste0("#Predictor_", i)
)
current_n_preds <<- current_n_preds - 1
}
}
})
# vector of selected predictors
preds_selected <- reactive({
unlist(lapply(seq_len(input$n_preds), function(i) {
input[[paste0("Predictor_", i)]]
}))
})
# update the inputs
observeEvent(preds_selected(), {
lapply(seq_len(input$n_preds), function(i) {
if (!is.null(input[[paste0("Predictor_", i)]])) {
if (i == 1) {
possible_choices <- var_names
} else {
possible_choices <- setdiff(var_names, preds_selected()[1:(i - 1)])
}
if (input[[paste0("Predictor_", i)]] %in% possible_choices) {
new_value <- input[[paste0("Predictor_", i)]]
} else {
new_value <- possible_choices[1]
}
updateSelectInput(session,
inputId = paste0("Predictor_", i),
choices = possible_choices,
selected = new_value)
}
})
})
}
shinyApp(ui, server)
我认为insertUI/removeUI是一种很好的方法,但是您首先总是删除所有现有的输入,从而丢失以前选择的值。我认为你推荐的计算按钮非常好。@starja这是真的,肯定会更有效率。我认为可能的用户行为可能是首先为n确定一个值,因此在实践中我想知道这是否太重要了?我认为insertUI/removeUI是一个好方法,但是您首先总是删除所有现有的输入,从而丢失以前选择的值。我认为你推荐的计算按钮非常好。@starja这是真的,肯定会更有效率。我认为可能的用户行为可能是首先为n确定一个值,所以在实践中我想知道这是否太重要了?我认为这有一个缺陷,当你增加输入$n_preds时,你会失去预测值中的选项-如果你将其设置为5,那么预测值1必须是mpg,预测值2必须是cyl等等。这可能是因为,根据之后运行的计算,用户需要能够设置所有5个预测值,但顺序不同?编辑-我说缺陷,太强了-我的意思是它提供了不同的用户体验,可能不是预期的体验,即使有这种不同的体验,我认为这个解决方案最好地抓住了我想要的。一个好处是,即使添加或删除预测值,也会保留以前的选择。谢谢你们的回复。非常感谢@CWTOM感谢您的投入!我已经玩了一点,我想现在找到了解决办法。DJC我的编辑做你想要的吗?是的。作品great1@starja我只是想提一下,我刚才在这里问了另一个相关的问题-您的解决方案还没有实现,但很快就会实现:我认为这有一个缺陷,当您增加输入$n_preds时,您会失去预测值中的选项-如果您将其设置为5,那么pre
指令1必须是mpg,预测器2必须是cyl,依此类推。这可能是因为,根据之后运行的计算,用户需要能够设置所有5个预测值,但顺序不同?编辑-我说缺陷,太强了-我的意思是它提供了不同的用户体验,可能不是预期的体验,即使有这种不同的体验,我认为这个解决方案最好地抓住了我想要的。一个好处是,即使添加或删除预测值,也会保留以前的选择。谢谢你们的回复。非常感谢@CWTOM感谢您的投入!我已经玩了一点,我想现在找到了解决办法。DJC我的编辑做你想要的吗?是的。作品great1@starja我只是想提一下,我刚才在这里问了另一个相关问题-您的解决方案尚未实施,但很快就会实施: