Shiny中调用input$z列时get()触发==比较类型错误
问题描述
尝试在Shiny应用中通过响应式输入复现dr4pl的IC50计算方案,但遇到错误:
Error in ==: comparison (==) is possible only for atomic and list types
已创建包含示例数据的CSV文件,数据如下:
curve列
C1、C1、C1、C1、C1、C1、C1、C1、C1、C2、C2、C2、C2、C2、C2、C2、C2、C2、C3、C3、C3、C3、C3、C3、C3、C3、C3
POC列
1.07129314、0.9112628、0.97914297、0.95904437、0.8850967、0.84338263、0.75843762、0.61319681、0.52635571、0.84563087、1.24435113、1.11757648、0.82383523、0.82763447、0.72585483、0.31953609、0.15056989、0.10057988、0.57384256、0.65984339、0.81439758、0.84572057、0.62797088、0.30800934、0.08957274、0.06360764、0.04451161
dose列
0.078125、0.15625、0.3125、0.625、1.25、2.5、5、10、20、0.078125、0.15625、0.3125、0.625、1.25、2.5、5、10、20、0.078125、0.15625、0.3125、0.625、1.25、2.5、5、10、20
附上完整Shiny代码:
library(shiny) library(tidyverse) library(plotly) library(dr4pl) library(data.table) ui <- fluidPage(# Application title sidebarLayout( sidebarPanel( fileInput('file', "Choose CSV File"), uiOutput('X_dropdown'), uiOutput('Y_dropdown'), uiOutput('Z_dropdown'), uiOutput('IC50_Checkbox'), ), mainPanel(plotlyOutput("Plot"), plotlyOutput("P_IC50_table")) )) # Define server logic required to draw graph server <- function(input, output, session) { #saves uploaded file as a reactive output, file can be changed dta <- reactive({ req(input$file) read.csv(input$file$datapath, header = T) }) output$X_dropdown <- renderUI({ req(dta()) selectInput('x', "X", names(dta()), selected = "dose") }) output$Y_dropdown <- renderUI({ req(dta()) selectInput('y', "Y", names(dta()), selected = "POC") }) output$Z_dropdown <- renderUI({ req(dta()) selectInput('z', "Color", names(dta()), selected = "curve") }) output$IC50_Checkbox <- renderUI({ req(dta()) checkboxInput("ic50", "Calculate ICXX?", value = F) }) output$Plot <- renderPlotly({ req(dta()) req(input$x) if (input$ic50 == T) { #Assign functions to variables multiIC <- function(data, colDose, colResp, colID, inhib.percent, ...) { # Get curve IDs locID <- unique(data[[colID]]) # Prepare a vector to store IC50s locIC <- rep(NA, length(locID)) # Calculate IC50 separately for every curve #seq_along takes the length of the sequence for (ii in seq_along(locID)) { # Subset a single dose response locSub <- data[get(colID) == locID[[ii]], ] # Calculate IC50 locIC[[ii]] <- dr4pl::IC( dr4pl::dr4pl(dose = locSub[[colDose]], response = locSub[[colResp]], ...), inhib.percent) } return(data.frame(id = locID, x = locIC)) } #changed data dfIC50 <- multiIC(data = dta(), colDose = input$x, colResp = input$y, colID = input$z, inhib.percent = 50 ) #browser() IC50_table <- data.frame(id = locID, x = locIC) ggplotly( width = 500, height = 500, ggplot(data = dta(), aes( x = .data[[input$x]], y = .data[[input$y]], color = as.factor(.data[[input$z]]) )) + labs(color = "") + geom_vline(data = IC50_table, aes(xintercept = x)) ) output$P_IC50_table <- renderTable({ #Attempt to add in IC50 curve IC50_table }) } else{} }) } # Run the application shinyApp(ui = ui, server = server)
错误原因与修正方案
关键错误点
get()作用域问题:multiIC函数中get(colID)默认在全局环境查找,无法正确解析函数内data数据框的列名,导致比较操作失败。- 变量未定义:
IC50_table <- data.frame(id = locID, x = locIC)中的locID和locIC是multiIC内部局部变量,外部无法访问。 - 输出嵌套违规:在
renderPlotly内部定义output$P_IC50_table不符合Shiny输出逻辑,输出应独立定义。
修正后的完整代码
library(shiny) library(tidyverse) library(plotly) library(dr4pl) library(data.table) ui <- fluidPage( sidebarLayout( sidebarPanel( fileInput('file', "Choose CSV File"), uiOutput('X_dropdown'), uiOutput('Y_dropdown'), uiOutput('Z_dropdown'), uiOutput('IC50_Checkbox') ), mainPanel( plotlyOutput("Plot"), tableOutput("IC50_table") # 改用tableOutput适配表格显示 ) ) ) server <- function(input, output, session) { dta <- reactive({ req(input$file) read.csv(input$file$datapath, header = TRUE) }) output$X_dropdown <- renderUI({ req(dta()) selectInput('x', "X", names(dta()), selected = "dose") }) output$Y_dropdown <- renderUI({ req(dta()) selectInput('y', "Y", names(dta()), selected = "POC") }) output$Z_dropdown <- renderUI({ req(dta()) selectInput('z', "Color", names(dta()), selected = "curve") }) output$IC50_Checkbox <- renderUI({ req(dta()) checkboxInput("ic50", "Calculate IC50?", value = FALSE) }) # 独立封装IC50计算为反应式对象 dfIC50 <- reactive({ req(dta(), input$ic50, input$x, input$y, input$z) if (!input$ic50) return(NULL) multiIC <- function(data, colDose, colResp, colID, inhib.percent, ...) { locID <- unique(data[[colID]]) locIC <- rep(NA, length(locID)) for (ii in seq_along(locID)) { # 直接用数据框索引子集化,避免get()作用域问题 locSub <- data[data[[colID]] == locID[[ii]], ] # 拟合模型并计算IC值 dr_model <- dr4pl(dose = locSub[[colDose]], response = locSub[[colResp]], ...) locIC[[ii]] <- IC(dr_model, inhib.percent) } data.frame(id = locID, IC50 = locIC) } multiIC(data = dta(), colDose = input$x, colResp = input$y, colID = input$z, inhib.percent = 50) }) output$Plot <- renderPlotly({ req(dta(), input$x, input$y, input$z) p <- ggplot(data = dta(), aes(x = .data[[input$x]], y = .data[[input$y]], color = as.factor(.data[[input$z]]))) + geom_point() + # 补充散点图层,显示原始数据 labs(color = "") # 勾选IC50时添加垂直参考线 if (input$ic50 && !is.null(dfIC50())) { p <- p + geom_vline(data = dfIC50(), aes(xintercept = IC50, color = id), linetype = "dashed") } ggplotly(p, width = 500, height = 500) }) # 独立定义表格输出 output$IC50_table <- renderTable({ req(dfIC50()) dfIC50() }) } shinyApp(ui = ui, server = server)
核心修改说明
- 子集化方式优化:用
data[[colID]]直接访问列,替代get(colID),解决作用域导致的类型错误。 - 反应式逻辑分离:将IC50计算封装为独立反应式对象,让绘图和表格输出复用结果,避免变量作用域问题。
- 输出结构调整:将表格输出从绘图逻辑中移出,单独定义输出对象,符合Shiny的响应式规则。
- 可视化完善:补充
geom_point()显示原始数据点,让可视化更完整。
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

