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

在R的ggiraph中如何为图例与图表设置相同data_id实现联动高亮?

问题:实现堆叠条形图与图例的悬停联动

我需要实现鼠标悬停在图表分段上时,对应图例标签和填充色同步高亮;悬停图例时,对应图表分段也高亮。按要求图例和图表需设置相同的data_id,但操作中遇到问题:

向scale_fill_manual_interactive()添加data_id=groupID时,触发错误:

Error in scale_interactive(scale_fill_manual, ...) :
object 'groupID' not found

改用data_id = function(breaks) { as.character(breaks) }能运行,但无法实现图表与图例的联动,找不到原因。

以下是我的代码:

library(ggplot2)
library(ggiraph)
library(ggrepel)
library(scales)

Area <- c("location1", "location2", "location3", "location4")
very_good <-  c(14, 7, 17, 16)
good <-  c(33, 31, 35, 31)
quite_bad <-  c(33, 36, 30, 1)
very_bad <-  c(17, 2, 14, 10)

#Custom labels for the legend
Labels <- c("Very good", "Good", "Quite bad", "Very bad, wont return")

df1 <- data.frame(
  Area, 
  very_good, 
  good, 
  quite_bad,
  very_bad
)

df1_subset <- df1 %>%
  mutate_at(vars(2:5), funs(./100)) %>% 
  pivot_longer(
    cols = c(2:5),
    names_to = "Question", values_to = "Result"
  )

df1_subset <- transform(
  df1_subset,groupID=as.numeric(forcats::fct_inorder(Question))
)

set.seed(1)

stacked_chart <- ggplot(
  data = df1_subset,
  aes(
    x = Result,
    y = Area,
    group = Question,
    fill = Question,
    data_id = groupID
  )
) +
  geom_col_interactive(
    position = position_fill(reverse = TRUE)
  ) +
  geom_text_repel_interactive(
    aes(
      color = ifelse(Result > 0.06,  "#FFFFFF", "transparent"),
      label = percent(Result)
    ),
    fontface = "bold",
    position = position_fill(
      reverse = TRUE
    ),
    box.padding = 0.05,
    segment.color = "transparent",
    size = 5,
    direction = "x",
    hjust = 1.5
  ) +
  scale_y_discrete(
    limits = rev(Area)
  ) +
  scale_x_continuous(
    labels = scales::percent,
    expand = c(0, 0),
    limits = c(0, 1)
  ) +
  scale_color_identity() +
  scale_fill_manual_interactive(
data_id = lapply(Labels, function(breaks) {
  as.character(breaks)
}),
labels = function(breaks) {
  lapply(Labels, function(breaks) {
    label_interactive(
      breaks,
      data_id = as.character(breaks)
    )
  })
},
    values = c(
      "#000000",
      "#333333",
      "#666666",
      "#999999"
    )
  ) +
  theme_minimal() +
  theme(
    legend.position = "top",
    legend.justification = "left",
    legend.title = element_blank()
  )

stacked_chart_ggiraph <- girafe(
  ggobj = stacked_chart, width_svg = 9, height_svg = 6,
  options = list(
    opts_sizing(rescale = TRUE),
    opts_toolbar(saveaspng = FALSE),
    opts_hover_inv(css = girafe_css(
      css = "opacity:0.3;"
    )),
    opts_hover(css = girafe_css(
      css = "cursor:pointer;fill:red;",
      text = "cursor:pointer;fill:#222222;"
    )),
    opts_hover_key(css = girafe_css(
      css = "cursor:pointer;fill:red;"
    ))
  )
)

stacked_chart_ggiraph

解决方案

问题核心是scale层无法直接访问数据框中的groupID列,scale的参数是基于图例的breaks(即Question的原始取值)进行映射的,而非直接引用数据列。要实现联动,必须让图表元素和图例元素的data_id完全匹配。

修改步骤:

  1. 图表的data_id改用Question(与图例breaks的取值一致),替代原来的groupID
  2. 在scale_fill_manual_interactive()中,data_id直接设置为function(breaks) breaks,让每个图例项的data_id等于Question的原始值
  3. 调整标签部分的data_id,确保与图表的data_id保持一致

修改后的完整代码

library(ggplot2)
library(ggiraph)
library(ggrepel)
library(scales)
library(dplyr)
library(tidyr)

Area <- c("location1", "location2", "location3", "location4")
very_good <-  c(14, 7, 17, 16)
good <-  c(33, 31, 35, 31)
quite_bad <-  c(33, 36, 30, 1)
very_bad <-  c(17, 2, 14, 10)

# 自定义图例标签
Labels <- c("Very good", "Good", "Quite bad", "Very bad, wont return")

df1 <- data.frame(
  Area, 
  very_good, 
  good, 
  quite_bad,
  very_bad
)

df1_subset <- df1 %>%
  mutate(across(2:5, ~./100)) %>% 
  pivot_longer(
    cols = 2:5,
    names_to = "Question", values_to = "Result"
  )

set.seed(1)

stacked_chart <- ggplot(
  data = df1_subset,
  aes(
    x = Result,
    y = Area,
    group = Question,
    fill = Question,
    data_id = Question  # 改用Question作为data_id,与图例breaks匹配
  )
) +
  geom_col_interactive(
    position = position_fill(reverse = TRUE)
  ) +
  geom_text_repel_interactive(
    aes(
      color = ifelse(Result > 0.06,  "#FFFFFF", "transparent"),
      label = percent(Result),
      data_id = Question  # 文本层也设置相同的data_id
    ),
    fontface = "bold",
    position = position_fill(reverse = TRUE),
    box.padding = 0.05,
    segment.color = "transparent",
    size = 5,
    direction = "x",
    hjust = 1.5
  ) +
  scale_y_discrete(limits = rev(Area)) +
  scale_x_continuous(
    labels = scales::percent,
    expand = c(0, 0),
    limits = c(0, 1)
  ) +
  scale_color_identity() +
  scale_fill_manual_interactive(
    data_id = function(breaks) breaks,  # 图例项的data_id与Question取值一致
    labels = function(breaks) {
      # 给每个标签设置对应Question的data_id
      Map(function(label, q) {
        label_interactive(label, data_id = q)
      }, Labels, breaks)
    },
    values = c("#000000", "#333333", "#666666", "#999999")
  ) +
  theme_minimal() +
  theme(
    legend.position = "top",
    legend.justification = "left",
    legend.title = element_blank()
  )

stacked_chart_ggiraph <- girafe(
  ggobj = stacked_chart, width_svg = 9, height_svg = 6,
  options = list(
    opts_sizing(rescale = TRUE),
    opts_toolbar(saveaspng = FALSE),
    opts_hover_inv(css = girafe_css(css = "opacity:0.3;")),
    opts_hover(css = girafe_css(
      css = "cursor:pointer;fill:red;",
      text = "cursor:pointer;fill:#222222;"
    )),
    opts_hover_key(css = girafe_css(css = "cursor:pointer;fill:red;"))
  )
)

stacked_chart_ggiraph

说明:

  • 图表元素(条形、文本)的data_id统一使用Question,确保与图例项的data_id一一对应
  • scale_fill_manual_interactive()中通过function(breaks) breaks让图例项的data_id等于Question的原始值
  • 标签部分用Map将自定义标签与对应的Question绑定,保证标签的data_id也匹配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 06:30:48