如何在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())

当我新增更多数据后(当前示例仅新增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())

目前绘制出的图形已经符合预期,但我更希望通过分组或其他非手动的方式实现该效果,请问有什么可行方案?另外是否有其他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
相关产品推荐
相关产品推荐

