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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 01:42:27