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

上传CSV文件到Shiny应用时如何实现更完整的数据校验?

最下方已贴出完整的最终可运行代码,对应M. Jagan提供的解决方案,实现了带健壮数据校验的用户数据上传、下载全流程,可看到try()函数如何「测试」上传操作,避免应用不必要的崩溃。

运行下方最小可复现示例(MWE)代码时,用户可上传、下载输入数据,下载保存的输入后续可通过上传功能读取。目前需要优化上传校验逻辑:实际场景中用户很容易选错文件,相比当前应用直接崩溃的表现,更希望抛出警告提示。

所有下载的CSV文件为表头为X、Y的2列矩阵,当前校验规则仅校验文件格式为CSV、包含X/Y列,正常CSV可正常上传下载,但格式错误的CSV上传时会导致应用崩溃。

具体问题

  • 如何指定仅从CSV文件的对应列读取「X」和「Y」表头?当前代码会全局检索X/Y表头,已尝试read.csv(...colClasses=c(NA, NA)))、read.csv(...)[ , 1:2]两种写法均未生效。
  • 更通用的需求:是否可在上传会触发错误或崩溃时主动中断上传?类似Excel中的if(iserror(...))逻辑。
  • (可选需求,若复杂可忽略)是否可将上传警告放入modalDialog?若无法解决可在后续单独提问。

MWE代码

library(dplyr)
library(shiny)
library(shinyMatrix)

interpol <- function(a, b) { # a = periods, b = matrix inputs
  c <- rep(NA, a)
  c[1] <- b[1]
  c[a] <- b[2]
  c <- approx(seq_along(c)[!is.na(c)], c[!is.na(c)], seq_along(c))$y 
  return(c)
}

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      fileInput("file1", "Optionally choose input file (csv)", accept = ".csv"),
      sliderInput('periods', 'Periods to interpolate over:', min=1, max=10, value=10),
      matrixInput("matrix1", 
                  value = matrix(c(1,5), 
                          ncol = 2, 
                          dimnames = list("Interpolate",c("X","Y"))
                  ),
                  cols =  list(names = TRUE),
                  class = "numeric"
      ),
      downloadButton("download")
    ),
    mainPanel(
      tableOutput("contents"),
      plotOutput("plot")
    )
  )
)

server <- function(input, output, session) {
  
  input_file <- reactive({
    file <- input$file1
    ext <- tools::file_ext(file$datapath)
    req(file)
    
    if(is.null(file))
      return(NULL)
    
    file_contents <- read.csv(file$datapath,header=TRUE,colClasses=c(NA, NA))
    required_columns <- c('X','Y')
    column_names <- colnames(file_contents)
    
    shiny::validate(
      need(ext == "csv", "Incorrect file type"),
      need(required_columns %in% column_names, "Incorrect file type")
    )
    
    file_contents
    
  })
  
  output$contents <- renderTable({
    input_file()
  })
  
  data <- function(){
    tibble(
      X = seq_len(input$periods),
      Y = interpol(input$periods,matrix(c(input$matrix1[1,1],input$matrix1[1,2])))
    )
  }  
  
  output$plot<-renderPlot({plot(data(),type="l",xlab="Periods (X)", ylab="Interpolated Y values")})
  
  observeEvent(input$file1,{
    updateMatrixInput(session, 
                      inputId = "matrix1", 
                      value = matrix(as.matrix(input_file()),
                                     ncol=2,
                                     dimnames = list("Interpolate",c("X","Y"))
                              )
    )
                      
  })
  
  output$download <- downloadHandler(
    filename = function() {
      paste("Inputs","csv",sep=".")
    },
    content = function(file) {
      write.csv(input$matrix1, file,row.names=FALSE)
    }
  )
}

shinyApp(ui, server)

最终解决代码

library(dplyr)
library(shiny)
library(shinyFeedback)
library(shinyMatrix)

nms <- c("X", "Y") # < Matrix variable names (headers)

interpol <- function(a, b) { # < a = periods, b = matrix inputs
  c <- rep(NA, a)
  c[1] <- b[1]
  c[a] <- b[2]
  c <- approx(seq_along(c)[!is.na(c)], c[!is.na(c)], seq_along(c))$y 
  return(c)
}

ui <- fluidPage(
  useShinyFeedback(),
  sidebarLayout(
    sidebarPanel(
      fileInput("file", "Optionally choose input file (csv)", accept = ".csv"),
      sliderInput('periods', 'Periods to interpolate over:', min=1, max=10, value=10),
      matrixInput("matrix1", 
                  "Values to interpolate:",
                  value = matrix(c(1,5),ncol = 2,dimnames = list(NULL,nms)),
                  cols =  list(names = TRUE),
                  rows = list(names = FALSE),
                  class = "numeric"
      ),
      downloadButton("download")
    ),
    mainPanel(
      tableOutput("contents"),
      plotOutput("plot"),
      verbatimTextOutput("verb")
    )
  )
)

server <- function(input, output, session) {
  uploadData <- reactive({
    req(input$file)
    validate(need(identical(tools::file_ext(input$file$datapath),"csv"),"Invalid extension"))
    try(read.csv(input$file$datapath, header = TRUE))
  })
  
  observeEvent(uploadData(), {
    if(is.data.frame(uploadData()) && 
       all(nms %in% names(uploadData())) && 
       all(vapply(uploadData()[nms],is.numeric,NA))){
          updateMatrixInput(session,"matrix1",as.matrix(uploadData()[nms]))
          hideFeedback("file")
        } 
    else {
      showFeedbackWarning("file", "Invalid upload.")
    }
  })

  data <- function(){
    tibble(
      X = seq_len(input$periods),
      Y = interpol(input$periods,matrix(c(input$matrix1[1,1],input$matrix1[1,2])))
    )
  }  
  
  output$plot<-renderPlot({plot(data(),type="l",xlab="Periods (X)", ylab="Interpolated Y values")})
  output$verb <- renderPrint(class(uploadData()))  
  
  output$download <- downloadHandler(
    filename = function() {
      paste("Inputs","csv",sep=".")
    },
    content = function(file) {
      write.csv(input$matrix1, file,row.names=FALSE)
    }
  )
}

shinyApp(ui, server)

内容的提问来源于stack exchange,提问作者Curious Jorge - user9788072

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 20:45:05