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

R Shiny中ggplotly图表空格千位分隔符显示异常求助

解决ggplotly中hover提示同时显示格式化代码与数值的问题

我在开发R Shiny应用,用ggplotly制作交互式柱状图,想把数值的千位分隔符设为空格。现有代码运行后,hover提示框最后一行能显示正确格式化的数值,但同时也会出现格式化代码,不知道怎么解决。

原代码如下:

mrg <- list(l=10, r=10, b=10, t=100, pad=1)

output$sortie3_evo <- renderPlotly({
  g <- ggplot(filtered_data_evo(), aes(x=Année.Scolaire, y= Nombre.élèves, fill=Secteur,label = format(as.numeric(..y..), nsmall=0, big.mark=" ")))+
  stat_summary(fun = sum, geom = "bar", position = position_dodge(0.9))+
    theme(axis.text.x = element_text(angle=25, hjust=1))+
    facet_wrap(~ NiveauLV, ncol = 2) +
    xlab("")

    
g <- ggplotly(g) %>%
              layout(margin = mrg)

})

问题原因

直接在aes()的label参数中写入format(as.numeric(..y..), ...)这类表达式时,ggplot会将表达式本身和计算后的结果同时纳入hover提示内容——因为..y..是stat层的内部变量,ggplotly解析时无法自动区分表达式和最终数值。

解决方案

方案一:通过stat_summary定义自定义hover文本

在stat_summary中用text参数构建完整的hover提示内容,再指定ggplotly仅显示该文本:

mrg <- list(l=10, r=10, b=10, t=100, pad=1)

output$sortie3_evo <- renderPlotly({
  g <- ggplot(filtered_data_evo(), aes(x=Année.Scolaire, y=Nombre.élèves, fill=Secteur)) +
    stat_summary(
      fun = sum, 
      geom = "bar", 
      position = position_dodge(0.9),
      # 在stat层生成格式化后的完整hover文本
      aes(text = paste0(
        "Année Scolaire: ", Année.Scolaire, "\n",
        "Secteur: ", Secteur, "\n",
        "Nombre élèves: ", format(..y.., nsmall=0, big.mark=" ")
      ))
    ) +
    theme(axis.text.x = element_text(angle=25, hjust=1)) +
    facet_wrap(~ NiveauLV, ncol = 2) +
    xlab("")

  ggplotly(g, tooltip = "text") %>%  # 指定仅显示自定义text内容
    layout(margin = mrg)
})

方案二:提前计算汇总数据(更直观)

先通过分组汇总得到总和,再基于汇总数据绘图,避免依赖stat层的内部变量:

mrg <- list(l=10, r=10, b=10, t=100, pad=1)

output$sortie3_evo <- renderPlotly({
  # 提前计算各分组的学生总数,并格式化数值
  summary_data <- filtered_data_evo() %>%
    group_by(Année.Scolaire, Secteur, NiveauLV) %>%
    summarise(Nombre.élèves = sum(Nombre.élèves), .groups = "drop") %>%
    mutate(Nombre.élèves_formatted = format(Nombre.élèves, nsmall=0, big.mark=" "))
  
  g <- ggplot(summary_data, aes(x=Année.Scolaire, y=Nombre.élèves, fill=Secteur,
                                text = paste0(
                                  "Année Scolaire: ", Année.Scolaire, "\n",
                                  "Secteur: ", Secteur, "\n",
                                  "Nombre élèves: ", Nombre.élèves_formatted
                                ))) +
    geom_bar(stat = "identity", position = position_dodge(0.9)) +
    theme(axis.text.x = element_text(angle=25, hjust=1)) +
    facet_wrap(~ NiveauLV, ncol = 2) +
    xlab("")

  ggplotly(g, tooltip = "text") %>%
    layout(margin = mrg)
})

测试数据

structure(list(Année.Scolaire = c("2018-2019", "2018-2019", "2018-2019", 
"2018-2019", "2018-2019", "2018-2019", "2018-2019", "2018-2019", 
"2018-2019", "2018-2019"), Secteur = structure(c(2L, 2L, 2L, 
2L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("PUBLIC", "PRIVE SOUS CONTRAT"
), class = "factor"), Département = c("ARDECHE", "ARDECHE", "HAUTE SAVOIE", 
"SAVOIE", "HAUTE SAVOIE", "HAUTE SAVOIE", "ISERE", "ISERE", "ISERE", 
"SAVOIE"), Cycle = c("Cycle 2", "Cycle 3", "Cycle 2", "Cycle 3", 
"Cycle 1", "Cycle 3", "Cycle 1", "Cycle 2", "Cycle 3", "Cycle 1"
), LV = c("Allemand LV1", "Allemand LV1", "Allemand LV1", "Allemand LV1", 
"Allemand LV1", "Allemand LV1", "Allemand LV1", "Allemand LV1", 
"Allemand LV1", "Allemand LV1"), Nombre.élèves = c(6, 6, 6, 324, 
66, 3, 21, 258, 18, 204), NiveauLV = c("LV1", "LV1", "LV1", "LV1", 
"LV1", "LV1", "LV1", "LV1", "LV1", "LV1"), LV_recode = c("Allemand", 
"Allemand", "Allemand", "Allemand", "Allemand", "Allemand", "Allemand", 
"Allemand", "Allemand", "Allemand")), row.names = c(NA, 10L), class = "data.frame")

内容的提问来源于stack exchange,提问作者Chloé

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:46:00