【发布时间】:2016-10-31 04:46:06
【问题描述】:
目标
我正在开发一个 Shiny 应用程序,该应用程序允许用户上传自己的数据并通过提供下图描述的数据过滤小部件来关注整个数据或子集 select输入“Variable 1”会显示用户上传数据的所有列名,selectize输入“Value”会显示对应的所有唯一值在“变量 1”中选择的列。理想情况下,用户将能够通过某种触发器添加尽可能多的此类行(“Variable X”+“Value”),一种可能性是单击“添加更多”操作按钮。
一个可能的解决方案
在网上查了一下,发现下面贴了Nick Carchedi给出的一个很有希望的解决方案
ui.R
library(shiny)
shinyUI(pageWithSidebar(
# Application title
headerPanel("Dynamically append arbitrary number of inputs"),
# Sidebar with a slider input for number of bins
sidebarPanel(
uiOutput("allInputs"),
actionButton("appendInput", "Append Input")
),
# Show a plot of the generated distribution
mainPanel(
p("The crux of the problem is to dynamically add an arbitrary number of inputs
without resetting the values of existing inputs each time a new input is added.
For example, add a new input, set the new input's value to Option 2, then add
another input. Note that the value of the first input resets to Option 1."),
p("I suppose one hack would be to store the values of all existing inputs prior
to adding a new input. Then,", code("updateSelectInput()"), "could be used to
return inputs to their previously set values, but I'm wondering if there is a
more efficient method of doing this.")
)
))
server.R
library(shiny)
shinyServer(function(input, output) {
# Initialize list of inputs
inputTagList <- tagList()
output$allInputs <- renderUI({
# Get value of button, which represents number of times pressed
# (i.e. number of inputs added)
i <- input$appendInput
# Return if button not pressed yet
if(is.null(i) || i < 1) return()
# Define unique input id and label
newInputId <- paste0("input", i)
newInputLabel <- paste("Input", i)
# Define new input
newInput <- selectInput(newInputId, newInputLabel,
c("Option 1", "Option 2", "Option 3"))
# Append new input to list of existing inputs
inputTagList <<- tagAppendChild(inputTagList, newInput)
# Return updated list of inputs
inputTagList
})
})
缺点
正如Nick Carchedi 本人所指出的,每次添加新的输入小部件时,所有现有的输入小部件都会被重置。
Shiny 中数据子集/过滤的有前途的解决方案
正如warmoverflow 所建议的,DT 包中的datatable 函数提供了一种在 Shiny 中过滤数据的好方法。请参阅下面启用数据过滤的最小示例。
library(shiny)
shinyApp(
ui = fluidPage(DT::dataTableOutput('tbl')),
server = function(input, output) {
output$tbl = DT::renderDataTable(
iris, filter = 'top', options = list(autoWidth = TRUE)
)
}
)
如果您打算在 Shiny 应用中使用它,有一些重要方面值得注意。
- 过滤箱型
- 对于数字/日期/时间列:范围滑块用于过滤范围内的行
- 对于因子列:选择输入用于显示所有可能的类别
- 对于字符列:使用普通搜索框
- 如何获取过滤后的数据
- 假设表输出id为
tableId,使用input$tableId_rows_all作为所有页面的行索引(在表被搜索字符串过滤后)。 请注意,input$tableId_rows_all返回所有页面上 DT (>= 0.1.26) 的行索引。如果你通过常规install.packages('DT')使用DT版本,则只返回当前页面的索引 - 要安装DT (>= 0.1.26),请参考其GitHub page
- 假设表输出id为
- 列宽
- 如果数据有很多列,列宽和过滤框宽度会变窄,导致很难看到文本作为报告here
还有待解决
尽管存在一些已知问题,DT 包中的datatable 仍是 Shiny 中数据子集化的有前途的解决方案。然而,问题本身,即如何在 Shiny 中动态附加任意数量的输入小部件,既有趣又具有挑战性。在人们找到解决问题的好方法之前,我将保留这个问题:)
谢谢!
【问题讨论】:
-
如果您预先创建了多个
uiOutput,您可以将这些uiOutput分别填充到您的输入小部件中,那么以前的小部件将不会被重置。限制是您将限制用户可以添加的最大输入。但由于您的输入取决于列数,我认为这不是问题。 -
不过,如果您使用具有过滤器和其他表格功能的现有解决方案,例如
DTrstudio.github.io/DT,则要容易得多。 -
@warmoverflow
DT看起来很棒。谢谢(你的)信息!是否可以返回过滤列的子集? -
是的,您可以使用
input$tbl_rows_all来引用过滤结果(注意tbl是您的表名,根据需要更改)。有关示例,请参见 stackoverflow.com/questions/30042456/…。