Shiny中如何对reactive响应式变量执行relevel重设因子参考水平
Shiny中对响应式数据重设因子参考水平的实现方案
问题复现
测试数据构造代码:
mydata <- data.frame(letter = rep(letters[1:3], 3), num = 1:9) write.csv(mydata, "mydata.csv", row.names = FALSE)
原代码运行报错:
Warning: Error in <-: invalid (NULL) left side of assignment [No stack trace available]
报错核心原因:普通reactive()生成的响应式对象是只读的,返回的是临时计算结果,不能直接对其元素做赋值操作,原代码中data()$letter <- ...的写法违反了这个规则。
正确实现逻辑
Shiny中如果需要对导入的数据做修改,不能直接修改原始reactive的返回值,需要用可更新的响应式值容器(reactiveVal或reactiveValues)存储数据:
- 原始导入的CSV文件用只读reactive存储,作为基础数据源
- 单独声明一个可更新的响应式变量,存放经过因子转换、重设参考水平后的数据集
- 用户点击确认按钮时,基于原始数据完成所有数据处理操作,生成新的完整数据副本,整体替换响应式变量中存储的内容
- 所有输出、后续分析都从处理后的响应式变量中取数
修正后可运行代码
library(shiny) ui <- fluidPage( fileInput( "file", label = "File Input", accept = ".csv" ), selectInput( inputId = "ref", label = "Reference Outcome", choices = NULL ), actionButton( inputId = "action", label = "action"), textOutput("text") ) server <- function(input, output, session) { # 只读响应式:存储上传的原始CSV数据 raw_data <- reactive({ inFile <- req(input$file) read.csv(inFile$datapath, header = TRUE, stringsAsFactors = FALSE) }) # 可更新响应式值:存储处理后的数据集 processed_data <- reactiveVal(NULL) # 文件上传完成后,更新参考水平选择下拉框,初始化处理后数据 observeEvent(raw_data(), { first_col <- raw_data()[,1] updateSelectInput(session, "ref", choices = unique(first_col)) processed_data(raw_data()) }) # 点击按钮时执行因子转换、参考水平重设 observeEvent(input$action,{ # 取出原始数据做修改 df <- req(raw_data()) df$letter <- factor(df$letter) df$letter <- relevel(df$letter, ref = input$ref) # 将处理完成的完整数据更新到响应式值中 processed_data(df) }) # 输出当前参考水平 output$text <- renderText({ req(processed_data()) paste("Your reference level is: ", levels(processed_data()$letter)[1]) }) } shinyApp(ui, server)
关键注意点
- 不要把
render*渲染函数放在observe/observeEvent内部,渲染函数本身会自动追踪依赖的响应式变量,变量更新时会自动重渲染 - 所有对响应式数据的修改,都要遵循「拷贝-修改-整体替换」的流程,不能直接对reactive()返回的结果做局部赋值
- 涉及用户触发的修改操作,优先用
observeEvent绑定对应触发事件,比无差别的observe性能更好,也能避免不必要的意外触发
内容的提问来源于stack exchange,提问作者Reza Khayami
相关产品推荐
相关产品推荐

