如何通过R SHINY应用程序的服务器部分中的循环动态创建反应性数据集? [英] How can I create reactive datasets dynamically via a loop in the server section of an R Shiny app?

查看:13
本文介绍了如何通过R SHINY应用程序的服务器部分中的循环动态创建反应性数据集?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,这是一个一般性问题,我希望有知识的人能够为我指明正确的方法方向,以实现我想要做的事情。因此,我没有可重复的示例可供分享,但我将提供一些示例代码,希望它们能够理解我正在尝试做的事情。

我有一个R闪亮的仪表板应用程序。此应用程序使用在全局环境中(即不在服务器内)生成的多个不同的数据集。在服务器内,需要根据用户输入将过滤应用于每个数据集。因此,这些数据集是反应性的。

我目前对每个数据集都有单独的代码块,如下面的示例代码所示。这很管用。但是,我希望对此进行动态编程,以便可以将相同的代码应用于数据集列表,而不必分别为每个数据集复制和编辑相同的代码块。

下面是当前代码块的示例。它们调用处理数据集的过滤函数。例如,代码的后面部分可以调用";filtered_data_apple()";,并且它可以按预期工作。

 filtered_data_apple <- reactive({
    data <- filter_data('apple',as.data.table(df_apple))
    data
  })
  filtered_data_banana <- reactive({
    data <- filter_data('banana',as.data.table(df_banana))
    data
  })
  filtered_data_cherry <- reactive({
    data <- filter_data('cherry',as.data.table(df_cherry))
    data
  })

我想要的是能够提供(在本例中是水果的)列表,并使服务器能够遍历它们并将相同的代码块应用到所有水果,并且使代码的其他部分能够调用它们生成的数据集,而不会出现任何错误。

以下代码不起作用,但希望演示我正在尝试做的事情:

for (fruit in c('apple','banana','cherry')){
   filtered_data_name <- paste('filtered_data_',fruit,sep="")
   df_name <- paste('df_',fruit,sep="")
   assign(filtered_data_name,
            reactive({
              data <- filter_data(fruit,as.data.table(get(df_name)))
              data
            })
   )
}
我相信上面的方法之所以失败,是因为代码在SHILY服务器中求值的时候。我认为在每次迭代中,&Quot;Fruits&Quot;的值最终都是相同的(列表中的最后一个值,&chry";)。因此,它将适用于&cherry";数据集,但不适用于其他数据集。我也曾尝试将循环中的代码包含在本地语句中,但这不起作用,因为生成的数据集仍然包含在本地环境中,不能从该环境外部调用。我还尝试过使用Repeat循环,但它也失败了,原因与for循环相同。在所有迭代中,水果的值将是&chry";。

希望我已经足够清楚地传达了这个问题,我希望有人能够提供正确的方法来解决这个问题。肯定有什么事吧?

谢谢!

编辑:为清楚起见,数据集可以包含彼此完全不同的列。这就是为什么它们是独立的数据集。它们也是非常大的数据集,因此我想限制正在进行的筛选数量,以便它为该数据集筛选一次,而不是在每次调用它时筛选一个更大的数据集,这会花费更长的时间来运行。

推荐答案

这是一个基于模块的可行解决方案。

模块由filteredDataUIfilteredDataServer函数定义。filteredDataUIwellPanel中显示两个selectInput和一个dataTableOutput。当向模块传递数据框时,selectInput包含列名和选定列的值。选择一个(或多个)值时,将筛选该表以仅显示该列中包含这些值的行。

模块的使用允许对不同的数据帧重用相同的代码,并从主程序流中删除过滤逻辑。模块服务器和模块UI函数的id参数允许模块的单独实例处理不同的数据框。

可以通过以下方式显示任意数量的数据框:只需为每个数据框定义模块的单独实例。

library(shiny)
library(tidyverse)

# Module UI
filteredDataUI <- function(id, label) {
  ns <- NS(id)
  wellPanel(
    label,
    selectInput(ns("fieldName"), "Select a column", choices=c()),
    selectInput(ns("fieldValues"), "Select values", choices=c(), multiple=TRUE),
    dataTableOutput(ns("table"))
  )
}

# Module server
filteredDataServer <- function(id, data) {
  moduleServer(
    id,
    function(input, output, session) {
      # Populate column names
      observe({
        updateSelectInput(session, "fieldName", choices=names(data))
      })
      
      # Update field values on change of field name
      observeEvent(input$fieldName, {
        req(input$fieldName)
        
        valueList <- data %>% select(one_of(input$fieldName)) %>% distinct() %>% arrange() %>% pull()
        updateSelectInput(session, "fieldValues", choices=valueList, selected=NULL)
      })
      
      # Filter the input data
      filteredData <- reactive({
        if (is.null(input$fieldValues)) {
          data
        } else {
          idx <- which(names(data) == input$fieldName)
          valueList <- input$fieldValues
          data %>% filter(data[[idx]] %in% valueList)
        }
      })
      
      # Render the filtered table
      output$table <- renderDataTable({ filteredData() }, options=list("pageLength"=5))
    
      # Return the filtered data to the app.  Note that the reactive is returned,
      # not its value
      return(filteredData)
    }
  )
}

ui <- fluidPage(
  wellPanel(
    fluidRow(
      column(width=6, textOutput("data1Text")),
      column(width=6, textOutput("data2Text"))
    )
  ),
  filteredDataUI("data1", "The mtcars data frame"),
  filteredDataUI("data2", "The diamonds data frame"),
)

server <- function(input, output) {
  # Define the modules
  fd1 <- filteredDataServer("data1", mtcars)
  fd2 <- filteredDataServer("data2", diamonds)
  
  # React to changes in module return values
  output$data1Text <- renderText({
    paste0("mtcars contains ", fd1() %>% nrow(), " rows after filtering.")
  })
  output$data2Text <- renderText({
    paste0("diamonds contains ", fd2() %>% nrow(), " rows after filtering.")
  })
}

shinyApp(ui = ui, server = server)

已编辑07May21以包括注释并演示在应用程序的主服务器函数中使用模块返回值。

这篇关于如何通过R SHINY应用程序的服务器部分中的循环动态创建反应性数据集?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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