Shiny双SelectInput筛选的响应式箱线图直方图报错排查
错误原因
触发First argument data must be a data frame or shared data报错的核心原因是传入plotly的data参数不是合法的数据框类型,对应现有逻辑的常见问题点如下:
- 最高发诱因:调用响应式数据集
meus_dados时遗漏括号。Shiny中reactive()生成的是响应式函数对象,而非直接存储的数据框,必须通过meus_dados()的形式调用才能拿到过滤后的数据集,直接将meus_dados作为参数传给plot_ly()就会触发该类型错误。 - 筛选逻辑分支返回值类型不统一:处理"TODOS"全选选项时,部分判断分支没有返回数据框,比如误返回单列向量、NULL值,或是行筛选时未加
drop = FALSE参数,导致子集操作后数据结构被强制转成向量,不符合plotly的入参要求。 - 编码适配问题:数据集为latin1编码,读取时如果未显式指定编码,带葡萄牙语重音符号的列名会出现乱码,后续按列名筛选、取字段时会匹配失败,返回空值或非数据框类型对象。
- 字段类型问题:薪资类字段从csv读取时默认是字符型(巴西数据常用逗号作为小数分隔符),直接传入绘图函数会触发类型兼容问题,连带触发数据参数报错。
可运行修正方案
完整代码如下,核心调整点包括:读取数据时指定编码、统一列名与字段类型、规范响应式数据写法、调用响应式数据时强制带括号、初始化阶段做空值拦截。
# 加载依赖包 library(shiny) library(shinydashboard) library(plotly) library(dplyr) # 1. 数据读取:指定latin1编码,统一处理列名和字段类型 afastamentos <- read.csv("base_afastamentos.csv", encoding = "latin1", stringsAsFactors = FALSE) afastamentos <- afastamentos %>% rename( cargo = `Descrição do Cargo`, uf = UF, salario_liquido = `Valor do Rendimento Líquido`, salario_hora = `Rendimento Líquido Hora`, tipo_afastamento = descricao_2 ) %>% # 处理小数分隔符,转成数值型 mutate( salario_liquido = as.numeric(gsub(",", ".", salario_liquido)), salario_hora = as.numeric(gsub(",", ".", salario_hora)) ) # 2. UI端 ui <- dashboardPage( dashboardHeader(title = "公职人员休假分析面板"), dashboardSidebar( sidebarMenu( menuItem("Visualização", tabName = "vis", menuSubItem("Distribuição Amostral", tabName = "dist") ) ) ), dashboardBody( tabItems( tabItem(tabName = "dist", fluidRow( box( width = 4, selectizeInput( inputId = "select_UF", label = "选择州", choices = c("TODOS", unique(afastamentos$uf)), selected = "TODOS", multiple = TRUE ), selectizeInput( inputId = "descricao_2", label = "选择休假类型", choices = c("TODOS", unique(afastamentos$tipo_afastamento)), selected = "TODOS", multiple = TRUE, options = list(maxItems = 5) ) ), box( width = 8, plotlyOutput("boxplot_salario", height = "300px"), plotlyOutput("hist_salario_hora", height = "300px") ) ) ) ) ) ) # 3. Server端 server <- function(input, output) { # 响应式筛选逻辑,所有分支保证返回data.frame类型 meus_dados <- reactive({ dados <- afastamentos # 州筛选逻辑 if(!"TODOS" %in% input$select_UF){ dados <- dados %>% filter(uf %in% input$select_UF) } # 休假类型筛选逻辑 if(!"TODOS" %in% input$descricao_2){ dados <- dados %>% filter(tipo_afastamento %in% input$descricao_2) } # 移除绘图用字段的缺失值 dados <- dados %>% filter(!is.na(salario_liquido), !is.na(salario_hora)) return(as.data.frame(dados)) }) # 渲染箱线图:必须用meus_dados()带括号调用,req做空值拦截 output$boxplot_salario <- renderPlotly({ req(meus_dados()) plot_ly( data = meus_dados(), y = ~salario_liquido, type = "box", fillcolor = "#1f77b4", line = list(color = "#1f77b4"), name = "净薪资分布" ) %>% layout( yaxis = list(title = "Valor do Rendimento Líquido"), title = "净薪资分布箱线图" ) }) # 渲染直方图 output$hist_salario_hora <- renderPlotly({ req(meus_dados()) plot_ly( data = meus_dados(), x = ~salario_hora, type = "histogram", marker = list(color = "#ff7f0e"), name = "时薪分布" ) %>% layout( xaxis = list(title = "Rendimento Líquido Hora"), title = "净时薪分布直方图" ) }) } # 启动应用 shinyApp(ui, server)
关键避坑点
- 所有通过
reactive()、reactiveVal()、eventReactive()生成的响应式对象,调用时必须带括号,否则传递的是函数定义本身,不是内部存储的响应值。 - 写多分支筛选逻辑时,要保证所有分支的返回值类型统一为数据框,避免出现某分支返回空值、向量的情况。
- 读取含非ASCII字符(如带重音的葡语列名)的本地文件时,必须显式指定文件编码,否则列名乱码会导致后续所有数据操作失效。
- 涉及数值计算、绘图的字段,要提前做类型转换,处理好小数分隔符、缺失值问题,避免连带触发参数类型报错。
- 绘图、表格渲染前加
req(响应式数据对象),可以避免Shiny初始化阶段输入控件未完成渲染、数据为空时触发的类型报错。
内容的提问来源于stack exchange,提问作者Paula Macedo
相关产品推荐
相关产品推荐

