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

如何在ggplot2中借助分组变量使用geom_rect绘制多类型区间覆盖图

我需要展示区间覆盖范围(包含重叠区域),在堆叠条形图尝试失败后我选择了geom_rect实现需求,以下代码在单类别的场景下运行效果良好。

library(tidyverse)

# create dummy data
foo <- tibble(start = c(1, 150, 140, 75, 300),
              end = c(150, 180, 170, 160, 400))

ggplot() +
  geom_rect(data = foo, aes(xmin = start, xmax = end, ymin = 0, ymax = 1), fill = "green", linetype = "blank", alpha = 0.3) +
  geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 0, ymax = 1), fill = NA, colour = "black") +
  scale_y_continuous(name = "", breaks = NULL, limits = c(0, 1)) +
  scale_x_continuous(name = "", breaks = NULL) +
  theme_minimal() +
  theme(panel.grid = element_blank())

ggplot geom_rect 1st try

当我新增更多数据后(当前示例仅新增1种类型,我的原始数据包含更多类别),只能手动为每个类别添加对应图层,即每类都要写两行geom_rect代码,我希望通过分组或其他非手动的方式实现相同效果,但尝试未成功。新增数据和手动实现的代码如下:

foo <- foo %>%
  mutate(type = "A", .before = 1)
bar <- tibble(type = "B",
              start = c(1, 30, 40, 100, 150, 200, 310),
              end = c(20, 50, 100, 120, 200, 300, 380))
foo <- bind_rows(foo, bar)

ggplot() +
  geom_rect(data = foo %>% filter(type == "A"), aes(xmin = start, xmax = end, ymin = 0, ymax = 1), fill = "green", linetype = "blank", alpha = 0.3) +
  geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 0, ymax = 1), fill = NA, colour = "black") +
  geom_rect(data = foo %>% filter(type == "B"), aes(xmin = start, xmax = end, ymin = 2, ymax = 3), fill = "green", linetype = "blank", alpha = 0.3) +
  geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 2, ymax = 3), fill = NA, colour = "black") +
  scale_y_continuous(name = "", breaks = NULL, limits = c(0, 3)) +
  scale_x_continuous(name = "", breaks = NULL) +
  geom_text(aes(x = c(0, 0), y = c(0.5, 2.5), label = c("A", "B")), size = 4, hjust = 2) +
  theme_minimal() +
  theme(panel.grid = element_blank())

ggplot geom_rect 2ndt try

目前绘制出的图形已经符合预期,但我更希望通过分组或其他非手动的方式实现该效果,请问有什么可行方案?另外是否有其他geom或方法可以绘制这类图形?


解决方案

方案1:基于geom_rect自动映射位置(无额外依赖)

核心思路是给每个类别自动分配y轴区间,不需要手动筛选数据加图层,任意新增类别都可以自动适配:

library(tidyverse)

# 给每个type生成对应的y轴位置
foo <- foo %>%
  mutate(
    # 按类别生成唯一索引,新增类别自动顺延
    type_id = as.integer(factor(type)),
    # 每个类别的y轴区间和你手动设置的规则一致,间隔为1
    ymin = 2*(type_id - 1),
    ymax = ymin + 1,
    y_mid = (ymin + ymax)/2
  )

# 提取类别位置信息,用于绘制外框和标签
type_info <- distinct(foo, type, ymin, ymax, y_mid)
# 全局最大end值,所有外框统一用这个值
global_max_end <- max(foo$end)

ggplot() +
  # 批量绘制所有区间
  geom_rect(data = foo, aes(xmin = start, xmax = end, ymin = ymin, ymax = ymax),
            fill = "green", linetype = "blank", alpha = 0.3) +
  # 批量绘制所有类别外框
  geom_rect(data = type_info, aes(xmin = 1, xmax = global_max_end, ymin = ymin, ymax = ymax),
            fill = NA, colour = "black") +
  # 批量绘制类别标签
  geom_text(data = type_info, aes(x = 0, y = y_mid, label = type),
            size = 4, hjust = 2) +
  scale_y_continuous(name = "", breaks = NULL, limits = c(-0.2, max(foo$ymax) + 0.2)) +
  scale_x_continuous(name = "", breaks = NULL) +
  theme_minimal() +
  theme(panel.grid = element_blank())

方案2:用分面实现,代码更简洁

如果不需要严格固定y轴间隔,用ggplot自带的分面功能更省事,完全不需要手动处理位置逻辑:

ggplot(foo) +
  geom_rect(aes(xmin = start, xmax = end, ymin = 0, ymax = 1),
            fill = "green", linetype = "blank", alpha = 0.3) +
  geom_rect(aes(xmin = 1, xmax = max(end), ymin = 0, ymax = 1),
            fill = NA, colour = "black") +
  # 按类别纵向分面,标签放在左侧
  facet_grid(rows = vars(type), switch = "y") +
  scale_y_continuous(name = "", breaks = NULL) +
  scale_x_continuous(name = "", breaks = NULL) +
  theme_minimal() +
  theme(
    panel.grid = element_blank(),
    strip.placement = "outside",
    strip.background = element_blank(),
    strip.text.y.left = element_text(size = 12)
  )

其他可选的geom

如果你愿意装扩展包,ggh4x的geom_ranges就是专门用于绘制区间覆盖图的geom,语法更简洁,也支持分组和分面。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 01:39:03