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

在R中使用Highcharts实现分组堆叠百分比柱状图及同比变化展示

在R中使用Highcharts创建分组堆叠柱状图的解决方案

问题背景

需要生成满足以下要求的柱状图:

  • X轴为Zone,每个Zone对应TTM2和TTM1两个并列柱状图组
  • 每个柱状图组内按Revenue Type堆叠
  • 堆叠段显示该类型的占比,但柱高保留实际数值总和
  • 展示同一Zone下TTM1与TTM2的百分比变化

样本数据表

ZoneRevenue TypeTTM2TTM1
AmericasSaaS9011001
AmericasSubscriptions9021002
AmericasPerpetual Maintenance9031003
AmericasPerpetual9041004
APACSaaS9051005
APACSubscriptions9061006
APACPerpetual Maintenance9071007
APACPerpetual9081008
EMEASaaS9091009
EMEASubscriptions9101010
EMEAPerpetual Maintenance9111011
EMEAPerpetual9121012

当前代码问题

现有代码将TTM1和TTM2的柱子错误堆叠在一起,且无法实现"堆叠显示占比、柱高保留实际总和"的效果。


解决方案

1. 数据预处理

先计算每个分组的总和、占比,以及TTM间的百分比变化:

library(highcharter)
library(data.table)

# 原始数据
dt <- data.table::data.table(
  Zone = c("Americas", "Americas", "Americas", "Americas", "APAC", "APAC", "APAC", "APAC", "EMEA", "EMEA", "EMEA", "EMEA"),
  Revenue_Type = c("SaaS", "Subscriptions", "Perpetual Maintenance", "Perpetual", "SaaS", "Subscriptions", "Perpetual Maintenance", "Perpetual", "SaaS", "Subscriptions", "Perpetual Maintenance", "Perpetual"),
  TTM2 = c(901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912),
  TTM1 = c(1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012)
)

# 转长格式
dt_melt <- melt(dt, id.vars = c("Zone", "Revenue_Type"), 
                measure.vars = c("TTM1", "TTM2"), 
                variable.name = "TTM", value.name = "value")

# 计算每个TTM+Zone的总和与占比
dt_melt[, total := sum(value), by = .(Zone, TTM)]
dt_melt[, percentage := round(value / total * 100, 1)]

# 计算每个Zone的TTM1与TTM2百分比变化
change_data <- dt_melt[, .(total = sum(value)), by = .(Zone, TTM)] %>%
  dcast(Zone ~ TTM, value.var = "total") %>%
  .[, change := round((TTM2 - TTM1)/TTM1 * 100, 1)]

2. 构建目标图表

通过指定堆叠组实现分组并列,自定义标签和tooltip显示占比:

# 获取所有收入类型
rev_types <- unique(dt_melt$Revenue_Type)

# 初始化图表
hc <- highchart() %>%
  hc_xAxis(categories = unique(dt_melt$Zone), title = list(text = "Zone")) %>%
  hc_yAxis(title = list(text = "Total Revenue")) %>%
  hc_plotOptions(
    column = list(
      grouping = TRUE,  # 同一Zone下TTM1/TTM2并列
      stacking = "normal",  # 同一TTM内按收入类型堆叠
      dataLabels = list(
        enabled = TRUE,
        formatter = JS("function() { return this.point.percentage + '%'; }")
      )
    )
  ) %>%
  hc_tooltip(
    formatter = JS("function() {
      return '<b>' + this.x + ' - ' + this.series.name + '</b><br/>' +
             this.point.TTM + ': $' + this.point.value + '<br/>' +
             '占比: ' + this.point.percentage + '%';
    }")
  ) %>%
  hc_legend(title = list(text = "Revenue Type"))

# 为每个收入类型添加TTM1、TTM2两个系列
for (rt in rev_types) {
  # TTM1系列
  hc <- hc %>%
    hc_add_series(
      data = dt_melt[Revenue_Type == rt & TTM == "TTM1"],
      type = "column",
      name = rt,
      hcaes(x = Zone, y = value, percentage = percentage, TTM = TTM),
      stack = "TTM1"  # 指定堆叠组为TTM1
    )
  # TTM2系列
  hc <- hc %>%
    hc_add_series(
      data = dt_melt[Revenue_Type == rt & TTM == "TTM2"],
      type = "column",
      name = rt,
      hcaes(x = Zone, y = value, percentage = percentage, TTM = TTM),
      stack = "TTM2"  # 指定堆叠组为TTM2
    )
}

# 添加百分比变化标注(显示在柱子上方)
hc <- hc %>%
  hc_add_series(
    data = change_data,
    type = "scatter",
    name = "同比变化",
    hcaes(x = Zone, y = pmax(TTM1, TTM2) * 1.1),  # 定位到较高柱子上方10%位置
    marker = list(enabled = FALSE),
    dataLabels = list(
      enabled = TRUE,
      formatter = JS("function() {
        return this.point.change >= 0 ? '+' + this.point.change + '%' : this.point.change + '%';
      }"),
      style = list(fontWeight = "bold")
    ),
    tooltip = list(formatter = JS("function() {
      return '<b>' + this.x + '</b><br/>TTM2 vs TTM1: ' + this.point.change + '%';
    }"))
  )

# 显示图表
hc

关键实现细节

  • 分组堆叠分离:通过stack参数将TTM1和TTM2分为独立堆叠组,实现同一Zone下两组柱子并列、组内堆叠
  • 占比显示:利用自定义JS函数在数据标签中展示预计算的占比,柱高由实际数值决定
  • 百分比变化:用散点系列将变化值标注在柱子上方,避免干扰堆叠结构

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 03:37:36