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

Shiny应用点击保存动态过滤数据按钮时卡顿求助

Shiny应用点击保存按钮卡顿的问题排查与修复

问题根源分析

你的代码存在两个核心问题导致卡顿:

  • 重复计算:保存按钮的observeEvent里完全复刻了txtout渲染时的数据过滤逻辑,每次点击保存都要重新从原始数据开始过滤,浪费大量计算资源。
  • 主线程阻塞:如果数据集较大,过滤和写入CSV的操作会占用Shiny的主线程,导致UI无法响应,出现卡顿。
    另外还有一个小语法错误:row.names = True中True是Python写法,R里应该用小写的TRUE。

解决方案

1. 复用过滤后的数据

把数据过滤逻辑抽成一个独立的reactive表达式,让表格渲染和保存操作共享这个结果,避免重复计算。

2. 可选:异步处理大数据

如果你的数据集非常大,建议用future和promises包将保存操作放到后台线程执行,不阻塞UI主线程。

修改后的完整代码

library(dplyr)
library(shinyWidgets)
library(shinythemes) # 补充缺失的shinythemes包引用

fpath <- '/dbfs/May2022'

# Define UI
ui <- fluidPage(theme = shinytheme("spacelab"),
                navbarPage(
                  "Display Data",
                  tabPanel(
                    "Select File",
                    sidebarPanel(
                      selectInput('selectfile','Select File',choices = list.files(fpath, pattern = ".csv")),
                      mainPanel("Main Panel",dataTableOutput("ftxtout"),style = "font-size:50%") # mainPanel
                    ), #sidebarPanel
                  ), #tabPanel
                  tabPanel("Subset Data",
                           sidebarPanel(
                             dropdown(
                               label = "Please Select Columns to Display",
                               icon = icon("sliders"),
                               status = "primary",
                               pickerInput(
                                 inputId = "columns",
                                 choices = NULL,
                                 multiple = TRUE
                               )#pickerInput
                             ), #dropdown
                             selectInput("v_attribute1", "First Attribute to Filter Data", choices = NULL),
                             selectInput("v_attribute2", "Second Attribute to Filter Data", choices = NULL),
                             selectInput("v_filter1", "First Filter", choices = NULL),
                             selectInput("v_filter2", "Second Filter", choices = NULL),
                             textInput("save_file", "Save to file:", value=""),
                             actionButton("doSave", "Save Selected Data")

                           ), #sidebarPanel

                           mainPanel(tags$br(),tags$br(),
                                     h4("Data Selection"),
                                     dataTableOutput("txtout"),style = "font-size:70%"
                           ) # mainPanel

                  ), # Navbar 1, tabPanel
                  tabPanel("Create Label", "This panel is intentionally left blank")

                ) # navbarPage
) # fluidPage


# Define server function
server <- function(input, output, session) {
  
  output$fileselected<-renderText({
    paste0('You have selected: ', input$selectfile)
  })

  info <- eventReactive(input$selectfile, {
    fullpath <- file.path(fpath,input$selectfile)
    read.csv(fullpath, header = TRUE,  sep = ",")
  })

  observeEvent(info(), {
    df <- info()
    vars <- names(df)
    updatePickerInput(session, "columns","Select Columns", choices = vars, selected=vars[1:2])
  })
  
  observeEvent(input$columns, {
    vars <- input$columns
    updateSelectInput(session, "v_attribute1","First Attribute to Filter Data", choices = vars)
    updateSelectInput(session, "v_attribute2","Second Attribute to Filter Data", choices = vars, selected=vars[2]) 
  })

  observeEvent(input$v_attribute1, {
    choicesvar1=unique(info()[[input$v_attribute1]])
    req(choicesvar1)
    updateSelectInput(session, "v_filter1","First Filter", choices = choicesvar1)
  })

  observeEvent(input$v_attribute2, {
    choicesvar2=unique(info()[[input$v_attribute2]])
    req(choicesvar2)
    updateSelectInput(session, "v_filter2","Second Filter", choices = choicesvar2)
  })

  output$ftxtout <- renderDataTable({
    head(info())
  }, options =list(pageLength = 5))

  # 抽离过滤逻辑为reactive表达式,供渲染和保存复用
  filtered_data <- reactive({
    req(input$columns, input$v_attribute1, input$v_attribute2, input$v_filter1, input$v_filter2)
    f <- info() %>% select(all_of(input$columns)) # 用dplyr的select替代subset,更规范
    f %>% 
      filter(
        .data[[input$v_attribute1]] == input$v_filter1,
        .data[[input$v_attribute2]] == input$v_filter2
      )
  })

  output$txtout <- renderDataTable({
    head(filtered_data())
  }, options =list(pageLength = 5)                 
  ) #renderDataTable
  
  #Saving data
  observeEvent(input$doSave, {
    req(input$save_file, filtered_data())
    fullfpath <- file.path(fpath, paste0(input$save_file, ".csv")) # 简化路径拼接
    write.csv(filtered_data(), fullfpath, row.names = TRUE) # 修正True为TRUE
    showNotification("Data has been saved", duration = 3) # 优化通知显示
  })

} # server

# Create Shiny object
shinyApp(ui = ui, server = server)

额外优化说明

  • 用dplyr::select替代subset,语法更规范且避免潜在问题。
  • 使用.data[[input$v_attribute1]]的方式引用动态列名,符合dplyr的编程规范。
  • 简化文件路径拼接逻辑,避免冗余的paste0和sep参数。
  • 调整通知的duration为3秒,自动关闭更友好。

大数据场景进阶优化

如果数据集极大,上述优化后仍有卡顿,可引入异步处理:

  1. 安装并加载future和promises包:
library(future)
library(promises)
plan(multisession) # 启用多会话异步
  1. 修改保存逻辑为异步:
observeEvent(input$doSave, {
  req(input$save_file, filtered_data())
  fullfpath <- file.path(fpath, paste0(input$save_file, ".csv"))
  data_to_save <- filtered_data()
  
  # 异步执行保存操作
  future({
    write.csv(data_to_save, fullfpath, row.names = TRUE)
  }) %...>% {
    showNotification("Data has been saved", duration = 3)
  } %...!% {
    showNotification(paste("Save failed:", .error), type = "error")
  }
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 04:18:16