如何实现ggplot2 geom_col中各柱形内部显示独立轮廓色?
问题
使用ggplot2的geom_col绘图时,想让每个矩形的填充色由一个变量控制,轮廓色由另一个变量控制,但默认的轮廓线是包围整个矩形的,垂直相邻的柱形会互相覆盖轮廓,只能看到顶部柱形的轮廓。如何让每个柱形的轮廓色都在自身区域内显示?
示例代码(问题复现)
df.toy <- data.frame(x = rep(1:2, each = 3), y = c(1, 2, 1, 1, 3, 1), color = c("a", "b", "b", "b", "c", "a"), fill = c("d", "e", "e", "e", "e", "e")) ggplot() + geom_col(data = df.toy, aes(x = x, y = y, color = color, fill = fill), linewidth = 2) + scale_fill_manual(values = c("yellow", "orange")) + scale_color_manual(values = c("blue", "red", "green"))
当前问题:垂直堆叠的柱形中,下方柱形的顶部轮廓被上方柱形覆盖,无法同时显示两个相邻柱形的轮廓色。
解决方案
核心思路是避免绘制柱形的完整外围轮廓,改为手动绘制每个柱形的局部边框(左、右、上三条边),这样相邻柱形的边框不会重叠。具体步骤如下:
- 先计算每个堆叠柱形的起始y坐标(堆叠基线)
- 绘制无轮廓的填充柱形
- 用
geom_segment为每个柱形绘制左、右、上边框
完整实现代码
library(ggplot2) library(dplyr) # 计算堆叠柱形的起始和结束y坐标 df.toy <- df.toy %>% group_by(x) %>% arrange(desc(y)) %>% # 按高度降序排列,保证堆叠顺序正确 mutate(y_start = cumsum(y) - y, # 柱形底部y值 y_end = cumsum(y)) %>% # 柱形顶部y值 ungroup() ggplot() + # 第一步:绘制填充柱形(不带轮廓) geom_col(data = df.toy, aes(x = x, y = y, fill = fill)) + # 第二步:绘制左侧边框 geom_segment(data = df.toy, aes(x = x - 0.4, xend = x - 0.4, y = y_start, yend = y_end, color = color), linewidth = 2) + # 绘制右侧边框 geom_segment(data = df.toy, aes(x = x + 0.4, xend = x + 0.4, y = y_start, yend = y_end, color = color), linewidth = 2) + # 绘制顶部边框 geom_segment(data = df.toy, aes(x = x - 0.4, xend = x + 0.4, y = y_end, yend = y_end, color = color), linewidth = 2) + # 设置颜色映射 scale_fill_manual(values = c("yellow", "orange")) + scale_color_manual(values = c("blue", "red", "green")) + theme_minimal()
效果说明
通过这种方式,每个柱形的左、右、上边框独立存在,相邻柱形之间不会出现轮廓重叠,所有柱形的轮廓色都能在自身区域内清晰显示。
如果需要更灵活的矩形绘制,也可以使用ggforce包的geom_rect,但上述方案仅依赖基础的dplyr和ggplot2,无需额外安装扩展包。
内容的提问来源于stack exchange,提问作者InColorado
相关产品推荐
相关产品推荐

