You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shiny应用中多分组多CSV文件合并与展示问题求助

Shiny应用:分组合并CSV并在Raw Data标签页展示的解决方案

问题描述

我开发了一个Shiny应用,在Raw Data标签页主面板展示多个CSV文件输出时遇到问题。已完成的工作流程:

  • 允许用户选择数据集分组数量
  • 允许每个分组选择多个CSV文件(选N则显示N个fileInput框)

需要解决的问题:

  • 将每个分组下选中的文件进行rbind合并
  • 在Raw Data标签页展示各分组的合并后数据集

用户输入的数据列完全一致,基础代码如下:

ui <- fluidPage(
  theme = bs_theme(version = 4, bootswatch = "pulse"),
  # Application title
  titlePanel(HTML("<center>Analysis</center>")),
  sidebarLayout(
    sidebarPanel(
      #begin tab
      conditionalPanel(condition = "input.tabselected==3", numericInput(
        "group_num", 
        "How many independent variables are you comparing", 
        2, 
        min = 1, 
        max = 20
      ),
      uiOutput("groups"),
      actionButton("save_btn", "Save Variables"),
      uiOutput("files")),
      actionButton("save_files", "Save Files"),
      
    ),
    
    
    
    mainPanel(
      tabsetPanel(type = "tabs", id = "tabselected", selected = 3, #Default tab selected is 3
                  tabPanel("Begin", textOutput("text1"), verbatimTextOutput("saved_variables"), value = 3),
                  tabPanel("Raw Data", tableOutput("raw_data")),
                  tabPanel("Stats", textOutput("stats_choice")),
                  tabPanel("Data", tableOutput("out_data")),
      )
    )
  )
)



server <- function(input, output) {
  
  # Generate dynamic text input fields based on the number of groups
  output$groups <- renderUI({
    group_num <- as.integer(input$group_num)
    lapply(
      1:group_num, 
      function(i){
        textInput(
          paste0("group", i), 
          paste0("Enter the name of group ", i)
        )
      }
    )
  })
  
  # Generate dynamic file input fields based on the number of groups
  output$files <- renderUI({
    group_num <- as.integer(input$group_num)
    lapply(
      1:group_num, 
      function(i){
        fileInput(
          paste0("file", i), 
          paste0(
            "Select the .csv file for group ", i), 
          multiple = TRUE, 
          accept= ".csv"
        )
      }
    )
  })
  
  # Save the text inputs as variables when the button is clicked
  saved_variables <- reactiveValues()
  observeEvent(input$save_btn, {
    group_num <- as.integer(input$group_num)
    saved_variables$names <- sapply(1:group_num, function(i) input[[paste0("group", i)]])
  })
  
  
  # Generate the output text
  output$text1 <- renderText({
    if (is.null(input$group_num)) {
      return()
    }
    group_num <- as.integer(input$group_num)
    variables <- saved_variables$names
    paste0(
      "You are comparing ", group_num, " independent variables. ",
      "The groups are called ", paste(variables, collapse = ", "), "."
    )
  })
  
  # Display the saved variables
  output$saved_variables <- renderPrint({
    req(saved_variables$names)
    saved_variables$names
  })
  
  
  
  output$stats_choice <- renderText({paste0("test4")})
  output$out_data <- renderText({paste0("test5")})
  
}
# Run the application 
shinyApp(ui = ui, server = server)

解决方案

核心修改点

在server函数中添加以下逻辑:

  1. 创建响应式对象存储合并后的数据
# 存储每个分组合并后的数据集
merged_data <- reactiveValues(data = list())
  1. 实现文件合并逻辑
    点击Save Files按钮时,遍历每个分组,读取并合并该分组下所有CSV文件,同时添加分组标识列:
# 处理文件合并与保存
observeEvent(input$save_files, {
  req(saved_variables$names)  # 确保已保存分组名称
  group_num <- as.integer(input$group_num)
  merged_list <- list()
  
  for(i in 1:group_num) {
    files <- input[[paste0("file", i)]]
    req(files)  # 确保当前分组已上传文件
    
    # 读取当前分组的所有CSV文件并合并
    group_dfs <- lapply(files$datapath, read.csv)
    combined_df <- do.call(rbind, group_dfs)
    
    # 添加分组名称列,方便区分数据来源
    combined_df$group <- saved_variables$names[i]
    
    merged_list[[saved_variables$names[i]]] <- combined_df
  }
  
  merged_data$data <- merged_list
})
  1. 在Raw Data标签页展示数据
    提供两种展示方式,可按需选择:

方式1:合并为单个表格展示

所有分组数据合并成一个大表,通过group列区分:

output$raw_data <- renderTable({
  req(merged_data$data)
  do.call(rbind, merged_data$data)
})

方式2:按分组单独展示

每个分组数据单独生成表格,带分组标题:
修改UI中Raw Data标签页的内容为uiOutput("raw_data"):

tabPanel("Raw Data", uiOutput("raw_data")),

然后在server中添加:

output$raw_data <- renderUI({
  req(merged_data$data)
  lapply(names(merged_data$data), function(group_name) {
    tagList(
      h3(paste("分组:", group_name)),
      tableOutput(paste0("table_", group_name))
    )
  })
})

# 动态生成每个分组的表格输出
observe({
  req(merged_data$data)
  lapply(names(merged_data$data), function(group_name) {
    output[[paste0("table_", group_name)]] <- renderTable({
      merged_data$data[[group_name]]
    })
  })
})

修改后的完整代码

ui <- fluidPage(
  theme = bs_theme(version = 4, bootswatch = "pulse"),
  titlePanel(HTML("<center>Analysis</center>")),
  sidebarLayout(
    sidebarPanel(
      conditionalPanel(condition = "input.tabselected==3", 
                       numericInput("group_num", 
                                    "How many independent variables are you comparing", 
                                    2, min = 1, max = 20),
                       uiOutput("groups"),
                       actionButton("save_btn", "Save Variables"),
                       uiOutput("files")),
      actionButton("save_files", "Save Files"),
      
    ),
    mainPanel(
      tabsetPanel(type = "tabs", id = "tabselected", selected = 3,
                  tabPanel("Begin", textOutput("text1"), verbatimTextOutput("saved_variables"), value = 3),
                  # 若使用方式2,将tableOutput改为uiOutput
                  tabPanel("Raw Data", tableOutput("raw_data")),
                  tabPanel("Stats", textOutput("stats_choice")),
                  tabPanel("Data", tableOutput("out_data")),
      )
    )
  )
)

server <- function(input, output) {
  
  # 生成动态分组名称输入框
  output$groups <- renderUI({
    group_num <- as.integer(input$group_num)
    lapply(1:group_num, function(i){
      textInput(paste0("group", i), paste0("Enter the name of group ", i))
    })
  })
  
  # 生成动态文件上传框
  output$files <- renderUI({
    group_num <- as.integer(input$group_num)
    lapply(1:group_num, function(i){
      fileInput(paste0("file", i), 
                paste0("Select the .csv file for group ", i), 
                multiple = TRUE, accept= ".csv")
    })
  })
  
  # 保存分组名称
  saved_variables <- reactiveValues()
  observeEvent(input$save_btn, {
    group_num <- as.integer(input$group_num)
    saved_variables$names <- sapply(1:group_num, function(i) input[[paste0("group", i)]])
  })
  
  # 生成提示文本
  output$text1 <- renderText({
    if (is.null(input$group_num)) return()
    group_num <- as.integer(input$group_num)
    variables <- saved_variables$names
    if(is.null(variables)) return("Please save group names first.")
    paste0("You are comparing ", group_num, " independent variables. ",
           "The groups are called ", paste(variables, collapse = ", "), ".")
  })
  
  # 展示已保存的分组名称
  output$saved_variables <- renderPrint({
    req(saved_variables$names)
    saved_variables$names
  })
  
  # ----------------------- 新增核心逻辑 -----------------------
  # 存储合并后的分组数据
  merged_data <- reactiveValues(data = list())
  
  # 合并每个分组的CSV文件
  observeEvent(input$save_files, {
    req(saved_variables$names)
    group_num <- as.integer(input$group_num)
    merged_list <- list()
    
    for(i in 1:group_num) {
      files <- input[[paste0("file", i)]]
      req(files)
      
      group_dfs <- lapply(files$datapath, read.csv)
      combined_df <- do.call(rbind, group_dfs)
      combined_df$group <- saved_variables$names[i]
      
      merged_list[[saved_variables$names[i]]] <- combined_df
    }
    
    merged_data$data <- merged_list
  })
  
  # 方式1:合并为单表展示Raw Data
  output$raw_data <- renderTable({
    req(merged_data$data)
    do.call(rbind, merged_data$data)
  })
  
  # 若使用方式2,注释方式1的代码,取消下面的注释:
  # output$raw_data <- renderUI({
  #   req(merged_data$data)
  #   lapply(names(merged_data$data), function(group_name) {
  #     tagList(
  #       h3(paste("Group:", group_name)),
  #       tableOutput(paste0("table_", group_name))
  #     )
  #   })
  # })
  # 
  # observe({
  #   req(merged_data$data)
  #   lapply(names(merged_data$data), function(group_name) {
  #     output[[paste0("table_", group_name)]] <- renderTable({
  #       merged_data$data[[group_name]]
  #     })
  #   })
  # })
  # ----------------------- 新增核心逻辑结束 -----------------------
  
  output$stats_choice <- renderText({paste0("test4")})
  output$out_data <- renderText({paste0("test5")})
  
}

shinyApp(ui = ui, server = server)

内容的提问来源于stack exchange,提问作者MM1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 03:01:59