求助:如何在ggplot2中实现自定义半填充圆形形状?
问题:在ggplot2中实现半填充圆形数据标记
数据集包含shape列,取值为0、1、2、3,对应四种数据标记需求:
- 0:仅显示轮廓的空心圆
- 1:左半部分填充的圆形
- 2:右半部分填充的圆形
- 3:完全填充的圆形
R内置形状代码无半填充圆形选项,尝试用grid包自定义形状函数后,在ggplot2中调用时出现错误:自定义形状无法显示,且对应数据行被移除,需寻求有效实现方法。
用户尝试的自定义形状代码
library(grid) # Define custom point shapes half_filled_left <- function(size) { polygonGrob(x = c(-0.5, 0.5, 0.5, -0.5), y = c(-0.5, -0.5, 0.5, 0.5), gp = gpar(col = "black", fill = "yellow")) arc <- seq(0, pi, length.out = 100) linesGrob(x = cos(arc) * 0.5, y = sin(arc) * 0.5, gp = gpar(col = "black", lwd = size / 5)) } half_filled_right <- function(size) { polygonGrob(x = c(-0.5, 0.5, 0.5, -0.5), y = c(-0.5, -0.5, 0.5, 0.5), gp = gpar(col = "black", fill = "red")) arc <- seq(pi, 2*pi, length.out = 100) linesGrob(x = cos(arc) * 0.5, y = sin(arc) * 0.5, gp = gpar(col = "black", lwd = size / 5)) }
用户尝试的ggplot2代码
newdata_small %>% ggplot(aes(x=as.numeric(F6_GR), y=emmean, shape=shape)) + #shape = condition geom_errorbar(aes(ymin=emmean-SE, ymax=emmean+SE), width=.2, position = pd) + geom_line(position = pd)+ geom_point(position = pd, size=5) + theme_classic() + ggtitle("Small Populations \n")+ scale_shape_manual(name= "Heat Waves", labels=c("No heat waves", "Initial heat wave only", "Final heat wave only ", "Initial & final heat wave"), values = c("0" = 1, # Circle outline only "1" = half_filled_left(5), # Half filled circle left side "2" = half_filled_right(5), # Half filled circle right side "3" = 16)) # Default filled circle
有效解决方案
方法一:使用ggpmisc包的geom_custom()自定义形状
scale_shape_manual仅支持整数/字符类型的内置形状代码,无法直接传入grob对象。通过ggpmisc::geom_custom()可以渲染自定义grob图形,步骤如下:
- 加载依赖包
library(ggplot2) library(grid) library(ggpmisc) library(dplyr)
- 重写自定义形状函数,返回组合后的grobTree对象
# 左半填充圆形 half_filled_left <- function(x, y, size, colour = "black", fill = "yellow") { # 填充左半区域的矩形 poly_grob <- polygonGrob( x = x + c(-0.5, 0, 0, -0.5) * size / 5, y = y + c(-0.5, -0.5, 0.5, 0.5) * size / 5, gp = gpar(col = colour, fill = fill, lwd = size / 5) ) # 圆形上半轮廓弧 arc_grob <- linesGrob( x = x + cos(seq(0, pi, length.out = 100)) * size / 10, y = y + sin(seq(0, pi, length.out = 100)) * size / 10, gp = gpar(col = colour, lwd = size / 5) ) # 组合所有图形元素 grobTree(poly_grob, arc_grob) } # 右半填充圆形 half_filled_right <- function(x, y, size, colour = "black", fill = "red") { poly_grob <- polygonGrob( x = x + c(0, 0.5, 0.5, 0) * size / 5, y = y + c(-0.5, -0.5, 0.5, 0.5) * size / 5, gp = gpar(col = colour, fill = fill, lwd = size / 5) ) arc_grob <- linesGrob( x = x + cos(seq(pi, 2*pi, length.out = 100)) * size / 10, y = y + sin(seq(pi, 2*pi, length.out = 100)) * size / 10, gp = gpar(col = colour, lwd = size / 5) ) grobTree(poly_grob, arc_grob) }
- 分图层添加不同形状的点并配置图例
# 基础图层 p <- newdata_small %>% ggplot(aes(x = as.numeric(F6_GR), y = emmean)) + geom_errorbar(aes(ymin = emmean - SE, ymax = emmean + SE), width = .2, position = pd) + geom_line(position = pd) + theme_classic() + ggtitle("Small Populations \n") # 添加各类型点 p <- p + # 空心圆(shape=0) geom_point(data = filter(newdata_small, shape == 0), position = pd, size = 5, shape = 1, color = "black") + # 左半填充圆(shape=1) geom_custom(data = filter(newdata_small, shape == 1), aes(x = as.numeric(F6_GR), y = emmean), grob_fun = half_filled_left, size = 5, position = pd) + # 右半填充圆(shape=2) geom_custom(data = filter(newdata_small, shape == 2), aes(x = as.numeric(F6_GR), y = emmean), grob_fun = half_filled_right, size = 5, position = pd) + # 完全填充圆(shape=3) geom_point(data = filter(newdata_small, shape == 3), position = pd, size = 5, shape = 16, color = "black") # 手动配置图例 p <- p + scale_color_manual( name = "Heat Waves", labels = c("No heat waves", "Initial heat wave only", "Final heat wave only ", "Initial & final heat wave"), values = rep("black", 4) ) + guides(color = guide_legend(override.aes = list( shape = c(1, NA, NA, 16), fill = c(NA, "yellow", "red", NA), grob = list(NULL, half_filled_left(0,0,5), half_filled_right(0,0,5), NULL) ))) print(p)
方法二:使用ggsymbol包快速实现(更简便)
ggsymbol包内置了半填充圆形等特殊形状,无需自定义grob:
- 加载依赖包
library(ggplot2) library(ggsymbol) library(dplyr)
- 直接调用geom_symbol()绘图
newdata_small %>% ggplot(aes(x = as.numeric(F6_GR), y = emmean, symbol = factor(shape))) + geom_errorbar(aes(ymin = emmean - SE, ymax = emmean + SE), width = .2, position = pd) + geom_line(position = pd) + geom_symbol(position = pd, size = 5, aes(fill = factor(shape)), color = "black") + theme_classic() + ggtitle("Small Populations \n") + scale_symbol_manual( name = "Heat Waves", labels = c("No heat waves", "Initial heat wave only", "Final heat wave only ", "Initial & final heat wave"), values = c("circle-open", "circle-half-left", "circle-half-right", "circle-filled") ) + scale_fill_manual( name = "Heat Waves", labels = c("No heat waves", "Initial heat wave only", "Final heat wave only ", "Initial & final heat wave"), values = c(NA, "yellow", "red", "black") )
内容的提问来源于stack exchange,提问作者Mossed
相关产品推荐
相关产品推荐

