如何在Shiny中针对多变量特定分类值实现分面展示?
问题
开发用于元分析的Shiny大数据可视化应用,需实现:
- 允许用户选择变量的特定分类值进行分面展示(而非全部值),适配后续分类值增加的场景(例如PLATFORM变量有Duobody、Hexabody、Bispecific三个取值,支持选一个或多个展示)
- 该功能需覆盖多个变量(如PLATFORM、MUTATION)
已在UI中为PLATFORM和MUTATION设置conditionalPanel,选择对应变量作为行分面时弹出选择框,但server端facet_grid/facet_wrap仍显示该变量的所有分类值,无法实现筛选效果。
解决方案
核心思路是先基于用户选择过滤数据,再用过滤后的数据绘图分面,具体步骤:
1. 修复UI中的错误
- 修正conditionalPanel的拼写错误:
conditoin改为condition - 在
row选择框的选项中添加"Mutation"(原代码中row选项无此值,导致对应的conditionalPanel无法触发)
2. 构建反应式过滤数据
在server中创建反应式数据集,根据用户选择的行分面变量(input$row)和对应的分类选择(如input$platform、input$mutation)过滤原始数据df。
3. 使用过滤后的数据绘图
用反应式数据替代原始数据绘图,分面时用更规范的语法构建公式,确保只展示用户选定的分类值。
修改后的完整代码
UI代码
ui <- fluidPage( tabsetPanel(tabPanel("Tab 1", titlePanel("Shiny App: Concentration vs Time Graphs"), sidebarLayout( mainPanel("Concentration vs Time graphs", plotOutput(outputId = "plot")), sidebarPanel(helpText("This app is developed to visualize pharmacokinetic data of different antibodies..."), selectInput( inputId = "study", label = "Include study:", choices = c("GLP Toxicity" = "ME1044-011", "Dose Range Finding", "Single Dose", "Repeat Dose"), selected = "ME1044-011", multiple = T ), selectInput( inputId = "x", label = "X-axis:", choices = c("Time" = "TIME", "TLD"), selected = "Time" ), selectInput( inputId = 'column', label = "Columns for:", choices = c("Dose mg/kg" = "DOSEMGKG", "Species" = "SPECIES", "Antibody" = "ABXID", "Subspecies" = "SUBSPECIES", "Age" = "AGE", "Animal ID" = "ANIMALID"), selected = "DOSEMGKG" ), selectInput( inputId = 'row', label = "Rows for:", choices = c("Dose mg/kg" = "DOSEMGKG", "Species" = "SPECIES", "Antibody" = "ABXID", "Subspecies" = "SUBSPECIES", "Age" = "AGE", "Animal ID" = "ANIMALID", "Platform" = "PLATFORM", "Mutation" = "MUTATION"), selected = "ABXID" ), conditionalPanel( condition = "input.row == 'PLATFORM'", selectInput( inputId = 'platform', label = "Choose platform(s)", choices = c("Hexabody", "Duobody", "Bispecific"), multiple = T, selected = c("Hexabody") # 默认选一个值,避免空选择报错 ) ), conditionalPanel( condition = "input.row == 'MUTATION'", selectInput( inputId = 'mutation', label = "Choose mutation(s):", choices = c('M1', "M2", "M3"), multiple = T, selected = c("M1") # 默认选一个值,避免空选择报错 ) ), selectInput( inputId = "group", label = "Group by:", choices = c("Dose mg/kg" = "DOSEMGKG", "Species" = "SPECIES", "Antibody" = "ABXID", "Subspecies" = "SUBSPECIES", "Age" = "AGE", "Animal ID" = "ANIMALID"), selected = "ANIMALID" ), sliderInput( inputId = 'trange', label = "Time range:", min = 0, max = 1704, value = c(0, 1704 ) ) )) )), tabsetPanel(tabPanel("Tab 2", titlePanel("Tab 2"), sidebarLayout( mainPanel("Plot #2", plotOutput(outputId = "plot2")), sidebarPanel(helpText("Whatever text..."), selectInput( inputId = 't', label = "Example", choices = c("#1", "#2", "#3"), selected = "#1" ) ) ))) )
Server代码
server <- function(input, output, session){ # 构建反应式过滤数据 filtered_df <- reactive({ temp_df <- df # 根据行分面变量过滤分类值 if(input$row == "PLATFORM"){ temp_df <- temp_df[temp_df$PLATFORM %in% input$platform, ] } else if(input$row == "MUTATION"){ temp_df <- temp_df[temp_df$MUTATION %in% input$mutation, ] } # 时间范围过滤 temp_df <- temp_df[temp_df[[input$x]] >= input$trange[1] & temp_df[[input$x]] <= input$trange[2], ] # 研究筛选(原UI有该选项但未使用) if(!is.null(input$study)){ temp_df <- temp_df[temp_df$STUDY %in% input$study, ] } temp_df }) output$plot <- renderPlot({ ggplot(data = filtered_df(), aes_string(x = input$x, y = "DV", col = input$group)) + xlab("Time") + ylab("Concentration (ug/mL)") + geom_point() + facet_grid(reformulate(input$column, input$row)) + # 用reformulate替代get(),语法更规范 scale_x_continuous(limits = input$trange) + scale_color_viridis(discrete = T) + theme_bw() }) } shinyApp(ui = ui, server = server)
关键说明
- 反应式数据
filtered_df()会实时根据用户选择过滤原始数据,确保绘图只包含选定的分类值 - 使用
reformulate()构建分面公式,比get()更符合ggplot语法规范,避免潜在问题 - 给分类选择框设置默认值,防止用户未选择时出现空数据报错
- 补上了原UI中
study选项的过滤逻辑(之前代码未使用该输入)
内容的提问来源于stack exchange,提问作者AsGi
相关产品推荐
相关产品推荐

