如何通过R SHINY应用程序的服务器部分中的循环动态创建反应性数据集? [英] How can I create reactive datasets dynamically via a loop in the server section of an R Shiny app?
问题描述
首先,这是一个一般性问题,我希望有知识的人能够为我指明正确的方法方向,以实现我想要做的事情。因此,我没有可重复的示例可供分享,但我将提供一些示例代码,希望它们能够理解我正在尝试做的事情。
我有一个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";。
希望我已经足够清楚地传达了这个问题,我希望有人能够提供正确的方法来解决这个问题。肯定有什么事吧?
谢谢!
编辑:为清楚起见,数据集可以包含彼此完全不同的列。这就是为什么它们是独立的数据集。它们也是非常大的数据集,因此我想限制正在进行的筛选数量,以便它为该数据集筛选一次,而不是在每次调用它时筛选一个更大的数据集,这会花费更长的时间来运行。
推荐答案
这是一个基于模块的可行解决方案。
模块由filteredDataUI
和filteredDataServer
函数定义。filteredDataUI
在wellPanel
中显示两个selectInput
和一个dataTableOutput
。当向模块传递数据框时,selectInput
包含列名和选定列的值。选择一个(或多个)值时,将筛选该表以仅显示该列中包含这些值的行。
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屋!