如何在ggplot中为两个geom_point系列分别手动定义shape美学?
这个问题我太熟悉了!在ggplot2里,每个美学属性(比如shape)只能对应一个比例尺(scale),你重复调用scale_shape_manual()会直接覆盖掉之前的设置,这就是报错的原因,同时也导致箭头形状没法正常显示。下面给你两个可行的解决方案:
方法1:统一管理所有shape映射(无需额外包)
我们可以把两组shape的取值都整合到同一个scale_shape_manual里,再通过guides()分别控制两个图例的显示逻辑:
- 先确保你的形状映射向量包含所有需要的取值:
sh_pal_all <- c( "Up" = "\U2191", "No Change" = "\U2194", "Down" = "\U2193", "A1" = "circle", "B1" = "square", "C1" = "diamond", "D1" = "triangle", "E1" = "circle" )
- 修改后的完整代码:
ggplot(data = meltdf2_all, aes(x = Month, y = variable, group = variable)) + geom_line(linetype = "dashed", colour = "grey") + # 第一个点层:映射住宿类型到shape geom_point(aes(colour = colour, shape = accommodation), size = 3) + # 第二个点层:映射风险变化到shape geom_point(data = melt_change_all, aes(shape = value), size = 3) + scale_colour_manual(values = pal_all, limits = names(pal_all), guide = guide_legend("Risk Level")) + # 统一的shape比例尺,包含所有映射关系 scale_shape_manual(values = sh_pal_all) + scale_x_date(date_breaks = "1 month" , date_labels = "%b-%y") + theme( axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank(), axis.line.x = element_line(colour = "darkgrey"), axis.title.x=element_blank(), panel.background = element_rect(fill = "white"), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), legend.position = c(0.2,0.70), legend.key.size = unit(1.5,"line"), legend.box = "horizontal" ) + guides( colour = guide_legend(override.aes = list(size = 3)), # 配置住宿类型的图例 shape = guide_legend( override.aes = list(size = 5), breaks = names(sh_pal_all[4:8]), title = "Accommodation Type" ), # 配置风险变化的次级图例 shape.sec = guide_legend( override.aes = list(size = 5), breaks = names(sh_pal_all[1:3]), title = "Latest Change" ) ) + scale_size(guide = "none")
注意:shape.sec是ggplot2 3.3.0及以上版本才支持的功能,如果你的版本较低,建议用下面的方法2。
方法2:使用ggnewscale添加独立的shape比例尺(推荐)
ggnewscale包就是专门解决「同一个美学属性需要多个独立比例尺」的场景,步骤很简单:
- 先安装并加载包:
install.packages("ggnewscale") library(ggnewscale)
- 修改后的完整代码:
sh_pal_all <- c( "Up" = "\U2191", "No Change" = "\U2194", "Down" = "\U2193", "A1" = "circle", "B1" = "square", "C1" = "diamond", "D1" = "triangle", "E1" = "circle" ) ggplot(data = meltdf2_all, aes(x = Month, y = variable, group = variable)) + geom_line(linetype = "dashed", colour = "grey") + # 第一个点层:住宿类型的shape映射 geom_point(aes(colour = colour, shape = accommodation), size = 3) + # 第一个shape比例尺,对应住宿类型 scale_shape_manual(values = sh_pal_all[4:8], limits = names(sh_pal_all[4:8]), guide = guide_legend("Accommodation Type")) + # 关键:新增一个shape比例尺的上下文,让后续图层使用独立的shape设置 new_scale("shape") + # 第二个点层:风险变化的shape映射 geom_point(data = melt_change_all, aes(shape = value), size = 3) + # 第二个shape比例尺,对应风险变化 scale_shape_manual(values = sh_pal_all[1:3], limits = names(sh_pal_all[1:3]), guide = guide_legend("Latest Change")) + # 颜色比例尺和其他设置 scale_colour_manual(values = pal_all, limits = names(pal_all), guide = guide_legend("Risk Level")) + scale_x_date(date_breaks = "1 month" , date_labels = "%b-%y") + theme( axis.title.y=element_blank(), axis.text.y=element_blank(), axis.ticks.y=element_blank(), axis.line.x = element_line(colour = "darkgrey"), axis.title.x=element_blank(), panel.background = element_rect(fill = "white"), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), legend.position = c(0.2,0.70), legend.key.size = unit(1.5,"line"), legend.box = "horizontal" ) + guides( colour = guide_legend(override.aes = list(size = 3)) ) + scale_size(guide = "none")
这个方法兼容性更好,而且逻辑更清晰,两个shape比例尺完全独立,不会互相干扰,箭头形状也能正常显示。
内容的提问来源于stack exchange,提问作者Ant
相关产品推荐
相关产品推荐

