【问题标题】:Subsetting a reactive object in Shiny在 Shiny 中设置反应对象
【发布时间】:2018-02-07 12:52:09
【问题描述】:

我正在学习 Shiny 并开发一个简单的应用程序。程序的启动将允许用户导入 CSV 文件,然后在需要时应用过滤器变量。在这个阶段,他们只能使用因子作为过滤变量。我在迭代的基础上应用过滤器。因此,可以根据一个因子级别应用过滤器,然后应用另一个因子级别,依此类推,直到完成。

我能找到的能够对响应式数据框进行子集化的最佳应用是将数据框作为响应式值应用。这似乎可行,但我遇到了一些我不知道如何解决的问题。

1) 鉴于过滤是一个迭代过程,我想跟踪并打印出过滤过程中应用的每个变量和级别。我能想到的最好方法是创建一个全局变量 (

2) 当我尝试在闪亮代码末尾保存反应数据帧时,我收到以下错误“警告:$ 中的错误:$ 运算符对原子向量无效”。我尝试了几件事,但不太明白这里发生了什么,因为对象“file$dfSource”不像普通的反应数据框 dfSource()?

下面闪亮的应用程序使用虹膜数据,因此更易于使用/测试。我不知道将数据框应用于反应值是否是编程的最佳方法,或者是否有更简单的方法来完成所有这些 - 只是想在这里学习最佳方法。

library(shiny)
allfilters <- c()

ui <- (fluidPage(

# Application title
titlePanel("Filter Data"),

# Input Forms
sidebarLayout(
sidebarPanel(
    h3("Data"),
        checkboxInput("selectFilter", label = "Apply Filter Variable", value = FALSE),
        uiOutput("selectFilterVar"),
    uiOutput("selectFilterGroup"),
    helpText("Apply filter to data"),
    uiOutput("selectFilterButton"),
    helpText("Reset data to total"),
    uiOutput("selectResetButton"),   
    h3("Download Data"),    
        helpText("Download Data"),
        downloadButton("downloadData", "Download File")
    ),

# Output Forms
mainPanel(
  tabsetPanel(
     tabPanel("Summary",
     h2("Output Summary"),
     textOutput("ncases"),
     textOutput("selectedfilters")))
  )
  )
))

server <- (function(input, output, session) {

data <- iris
file <- reactiveValues(dfSource = data)

## Select Filter Variable
output$selectFilterVar <- renderUI({
       req(file$dfSource)
       if (input$selectFilter){
       selectInput("filterVar", "Select Filter Variable", multiple = FALSE, choices = sort(names(file$dfSource[, sapply(file$dfSource, is.factor), drop = FALSE])))
}         
})         

# Select Filter Group(s)
output$selectFilterGroup <- renderUI({
        req(file$dfSource)
        req(input$filterVar)
        if (input$selectFilter){
        selectInput("filterGroup", "Select Filter Group", multiple = TRUE, choices = sort(unique(file$dfSource[,input$filterVar])))
}
})

# Apply Filter Button
output$selectFilterButton <- renderUI({
         req(file$dfSource)
         if (input$selectFilter) {
          actionButton("filterButton", "Apply Filter")
}
})

# Apply filter group to data
    observeEvent(input$filterButton, {
    temp <- file$dfSource[(file$dfSource[,input$filterVar] %in% c(input$filterGroup)),]
    file$dfSource <- temp
})

# Reset Total Sample Button
output$selectResetButton <- renderUI({
         req(file$dfSource)
         if (input$selectFilter) {
          actionButton("resetButton", "Reset Total")
}
})

# Reset data to total sample
    observeEvent(input$resetButton, {
    file$dfSource <- data
    updateCheckboxInput(session, "selectFilter", value = FALSE)
    allfilters <- NULL
})

## Summary number of cases
output$ncases <- renderText({
  req(file$dfSource)
  mainTitle <- paste("Number of cases =" , nrow(file$dfSource))
  return(mainTitle)  
})

## Capture selected filter variables in global object
testfilter <- eventReactive(input$filterButton, {
     appliedfilter <- paste0(input$filterVar, "(", input$filterGroup,")")
      if (is.null(allfilters)) {
        allfilters <<- paste("Selected Filters:", appliedfilter)
        } else {
        allfilters <<- paste(allfilters, "&", appliedfilter)
   }
   return(allfilters)
   })

# Print out filter variables in global object
output$selectedfilters <- renderText({
    filteroutput <- testfilter()
    print(filteroutput)
    return(filteroutput)
})

## Save out case data file
output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
    write.csv(file$dfSource, file)
    }
  )

})

shinyApp(ui, server)

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    1) 将其存储为全局变量可能不是一个好主意(shiny 中的作用域已经足够复杂了!)。你已经有一个reactiveValues 对象,为什么不使用它呢?

    然而,仅此是不够的;问题似乎是eventReactive - 我不太清楚为什么。

    这行得通:

    # this replaces the testfilter eventReactive
    observeEvent(input$filterButton, {
      appliedfilter <- paste0(input$filterVar, "(", input$filterGroup,")")
      if (is.null(file$allfilters)) {
        file$allfilters <- paste("Selected Filters:", appliedfilter)
      } else {
        file$allfilters <- paste(file$allfilters, "&", appliedfilter)
      }
     })
    
    # Print out filter variables in global object
    output$selectedfilters <- renderText({
      filteroutput <- file$allfilters
      print(filteroutput)
      return(filteroutput)
    })
    

    2) 错误出现在您传递给downloadHandler 的内容函数中。该参数称为file,它会影响file reactiveValues。这有效:

      ## Save out case data file
      output$downloadData <- downloadHandler(
        filename = function() {
          paste("data-", Sys.Date(), ".csv", sep="")
        },
        content = function(filetarget) {
          write.csv(file$dfSource, filetarget)
        }
      )
    

    PS 广告 1:存储过滤器可能会更好,而不是存储过滤后的数据框和列出过滤器的字符串。如果您的用户改变主意,他们必须从头开始,但是如果您存储过滤器,您可以拥有一个允许删除/编辑单个过滤器的表或类似的表。您可以只存储一个二元素向量列表,然后遍历该列表以过滤数据。

    【讨论】:

    • 感谢您的回复-您在带有文件的 downloaderHandler 上是对的,我没有收到该引用。关于您对范围界定的评论,我同意复杂性。但是,据我了解,创建全局变量可以保存选择历史,我相信您的版本只保存最后一个选择。如果我无法使用全局变量呈现给定当前应用程序的所有选项的历史记录,我喜欢您存储所有过滤器并一次应用它们的想法,而不是分别迭代每个过滤器并尝试存储文本和过滤后的数据。
    • 不,您可以在reactiveValue 中保存您想要的任何内容,包括列表。如果我没记错的话,在闪亮中使用&lt;&lt;- 的唯一原因是您想让所有用户都可以使用某些东西,以便一个用户的过滤器历史记录会覆盖其他用户的过滤器历史记录。在这种情况下,我们编辑并存储包含历史记录的字符串,它会一直存在,直到您覆盖它;此外,您已经使用相同的方式存储和更新过滤后的数据,并且它在那里工作。我已经用你的最小示例对其进行了测试,试试吧。
    猜你喜欢
    • 2013-06-21
    • 2013-07-15
    • 2020-05-19
    • 2015-04-02
    • 2017-12-30
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    相关资源
    最近更新 更多