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

如何为ggplot2水平色条添加超界值指示三角形?求通用方案

为水平色条添加超界值指示三角形的解决方案

问题背景

此前Stack Overflow上已有为垂直色条添加超界值指示三角形的方案,但直接套用在水平色条上会失效。参考建议修改代码后已实现功能,但不清楚代码原理,同时希望了解更优雅的实现方式。

修改后的可运行代码

以下是适配水平色条的自定义色条代码,搭配示例使用:

library(ggplot2)
library(gtable)
library(grid)

my_triangle_colourbar <- function(...) {
  guide <- guide_colourbar(...)
  class(guide) <- c("my_triangle_colourbar", class(guide))
  guide
}

guide_gengrob.my_triangle_colourbar <- function(...) {
  # 先生成默认色条
  guide <- NextMethod()
  # 提取色条组件与首尾颜色
  is_bar <- grep("^bar$", guide$layout$name)
  bar <- guide$grobs[[is_bar]]
  extremes <- c(bar$raster[1], bar$raster[length(bar$raster)])
  # 获取色条尺寸,确定三角形大小
  width  <- guide$widths[guide$layout$l[is_bar]]
  height <- guide$heights[guide$layout$t[is_bar]]
  short  <- min(convertUnit(width, "cm",  valueOnly = TRUE),
                convertUnit(height, "cm", valueOnly = TRUE))
  # 为三角形预留左右空间
  guide <- gtable_add_cols(guide, unit(short, "cm"),
                           guide$layout$l[is_bar] - 1)
  guide <- gtable_add_cols(guide, unit(short, "cm"),
                           guide$layout$l[is_bar])
  
  # 绘制左右三角形(对应下限/上限超界)
  left <- polygonGrob(
    x = unit(c(0, 1, 1), "npc"),
    y = unit(c(.5, 0, 1), "npc"),
    gp = gpar(fill = extremes[1], col = NA)
  )
  right <- polygonGrob(
    x = unit(c(0, 0, 1), "npc"),
    y = unit(c(0, 1, .5), "npc"),
    gp = gpar(fill = extremes[2], col = NA)
  )
  # 将三角形添加到色条对应位置
  guide <- gtable_add_grob(
    guide, left, 
    t = guide$layout$t[is_bar],
    l = guide$layout$l[is_bar] - 1
  )
  guide <- gtable_add_grob(
    guide, right,
    t = guide$layout$t[is_bar],
    l = guide$layout$l[is_bar] + 1
  )
  
  return(guide)
}

# 示例使用
df <- data.frame(x = c(1:10), y = c(1:10), z = seq(1,20,2))

ggplot(df) + 
  geom_point(mapping = aes(x = x, y = y, color = z)) +
  scale_color_viridis_c(limits = c(1,10), oob = scales::squish,
                        guide = my_triangle_colourbar()) +
  theme(legend.position = 'bottom')

代码原理拆解

  1. 自定义引导类:my_triangle_colourbar函数基于默认guide_colourbar创建,通过添加自定义类my_triangle_colourbar,触发后续的自定义渲染方法。
  2. 生成基础色条:NextMethod()调用默认逻辑生成标准水平色条的grob(图形对象)。
  3. 提取关键信息:从色条的布局中定位色条组件,获取色条首尾的颜色(对应超界值的填充色),并通过色条的宽高计算三角形的尺寸(取宽高中较小值,保证三角形比例协调)。
  4. 预留空间:使用gtable_add_cols在色条左右两侧各添加一列,为放置三角形腾出位置。
  5. 绘制三角形:用polygonGrob基于归一化坐标(npc)绘制三角形,左侧三角形对应下限超界,右侧对应上限超界,确保三角形与色条高度对齐。
  6. 组装色条:将绘制好的三角形grob添加到预留的列中,与色条的行位置对齐,最终组合成完整的带三角形的色条。

更优雅的实现方式

方案1:使用ggh4x包(推荐)

ggh4x包提供了扩展的色条引导功能,无需手动操作底层gtable和grid元素,直接支持为色条添加超界指示符号:

library(ggplot2)
library(ggh4x)

df <- data.frame(x = c(1:10), y = c(1:10), z = seq(1,20,2))

ggplot(df) + 
  geom_point(mapping = aes(x = x, y = y, color = z)) +
  scale_color_viridis_c(limits = c(1,10), oob = scales::squish,
                        guide = guide_colourbar_extend(
                          extend = c("left", "right"), # 左右都添加扩展符号
                          shape = c(24, 25), # 左侧下三角、右侧上三角
                          fill = scales::viridis_pal()(2) # 自动取色条首尾颜色
                        )) +
  theme(legend.position = 'bottom')

该方案优势:代码简洁、易维护,可灵活调整符号的形状、颜色、大小,同时支持水平/垂直色条。

方案2:优化自定义代码(通用水平/垂直色条)

修改原代码,自动识别色条方向(水平/垂直),实现通用的超界三角形添加:

library(ggplot2)
library(gtable)
library(grid)

my_triangle_colourbar <- function(...) {
  guide <- guide_colourbar(...)
  class(guide) <- c("my_triangle_colourbar", class(guide))
  guide
}

guide_gengrob.my_triangle_colourbar <- function(...) {
  guide <- NextMethod()
  # 定位色条组件
  is_bar <- grep("^bar$", guide$layout$name)
  bar <- guide$grobs[[is_bar]]
  extremes <- c(bar$raster[1], bar$raster[length(bar$raster)])
  
  # 判断色条方向
  width <- convertUnit(guide$widths[guide$layout$l[is_bar]], "cm", valueOnly = TRUE)
  height <- convertUnit(guide$heights[guide$layout$t[is_bar]], "cm", valueOnly = TRUE)
  is_horizontal <- width > height
  tri_size <- min(width, height)
  
  # 按方向处理空间与三角形
  if (is_horizontal) {
    # 水平色条:左右加列
    guide <- gtable_add_cols(guide, unit(tri_size, "cm"), guide$layout$l[is_bar] - 1)
    guide <- gtable_add_cols(guide, unit(tri_size, "cm"), guide$layout$l[is_bar])
    # 绘制三角形
    left_tri <- polygonGrob(
      x = unit(c(0, 1, 1), "npc"), y = unit(c(.5, 0, 1), "npc"),
      gp = gpar(fill = extremes[1], col = NA)
    )
    right_tri <- polygonGrob(
      x = unit(c(0, 0, 1), "npc"), y = unit(c(0, 1, .5), "npc"),
      gp = gpar(fill = extremes[2], col = NA)
    )
    # 添加到色条
    guide <- gtable_add_grob(guide, left_tri, t = guide$layout$t[is_bar], l = guide$layout$l[is_bar]-1)
    guide <- gtable_add_grob(guide, right_tri, t = guide$layout$t[is_bar], l = guide$layout$l[is_bar]+1)
  } else {
    # 垂直色条:上下加行
    guide <- gtable_add_rows(guide, unit(tri_size, "cm"), guide$layout$t[is_bar]-1)
    guide <- gtable_add_rows(guide, unit(tri_size, "cm"), guide$layout$t[is_bar])
    # 绘制三角形
    bottom_tri <- polygonGrob(
      x = unit(c(.5, 0, 1), "npc"), y = unit(c(0, 1, 1), "npc"),
      gp = gpar(fill = extremes[1], col = NA)
    )
    top_tri <- polygonGrob(
      x = unit(c(0, 1, .5), "npc"), y = unit(c(0, 0, 1), "npc"),
      gp = gpar(fill = extremes[2], col = NA)
    )
    # 添加到色条
    guide <- gtable_add_grob(guide, bottom_tri, t = guide$layout$t[is_bar]-1, l = guide$layout$l[is_bar])
    guide <- gtable_add_grob(guide, top_tri, t = guide$layout$t[is_bar]+1, l = guide$layout$l[is_bar])
  }
  return(guide)
}

该方案优势:一套代码同时支持水平和垂直色条,适配性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:40:14