如何在ggplot中仅为单个柱状图添加纹理且修复图例异常?
在ggplot中为单个柱状图添加纹理并修复图例异常
问题描述
想要仅为Total_1对应的柱状图添加纹理,但使用geom_bar_pattern与scale_fill_manual编写的代码出现图例显示错误,需要在不破坏图例的前提下实现需求。
数据准备
Run3VsRun8_Savings <- data.frame( Category = factor(c("4", "5", "6"), levels = c("4", "5", "6")), Cooling = c(3,0.5,4), NG_Heating = c(-39,-106,-30), Total =c(-26,-66,-19), Total_1=c(-23,-63,-17)) Run3VsRun8_Savings_long <- tidyr::gather(Run3VsRun8_Savings, key = "Variable", value = "Value", -Category)
注:需确保已加载
tidyr包以使用gather函数
原问题代码
ggplot(data = Run3VsRun8_Savings_long, aes(x = Category, y = Value, fill = Variable, pattern = Variable)) + geom_bar_pattern(position = position_dodge2(padding = 0), stat = "identity", color = 'black', pattern_fill = "white", pattern_angle = 55, pattern_density = 0.5, pattern_spacing = 0.025, pattern_key_scale_factor = 0.6) + geom_text(aes(label = paste0(Value, "%")), position = position_dodge(width = 1), vjust = -0.6, size = 2.8) + geom_vline(xintercept = seq(0.5, length(unique(Run3VsRun8_Savings_long$Category)) - 0.5), linetype = "dashed", color = "black") + scale_fill_manual( values = c("Cooling" = "skyblue", "NG_Heating" = "pink", "Total" = "gold", "Total_1" = "gold"), labels = c("Cooling" = "Cooling", "NG_Heating" = "Heating", "Total" = "Total_ZERH+HP", "Total_1" = "Total_ZERH+HP+Tight Envelope") ) + scale_pattern_manual(values = c('Cooling' = 'none', 'NG_Heating' = 'none', 'Total' = 'none', 'Total_1' = 'stripe'), labels = c('Cooling' = '', 'NG_Heating' = '', 'Total' = '', 'Total_1' = '')) + labs(x = "STUDY REGION", y = "COST SAVINGS (%)", title = 'Single-Wide Homes') + ylim(-120, 10) + theme_minimal() + theme( legend.position = "bottom", legend.direction = "horizontal", legend.box = "horizontal", legend.margin = margin(t = 0, r = 0, b = 0, l = 0), legend.title = element_blank(), legend.text = element_text(size = 10), plot.title = element_text(hjust = 0.5, margin = margin(b = 25), face = 'bold') )
问题分析
原代码同时将fill和pattern映射到Variable,但scale_pattern_manual设置了空标签,导致fill与pattern的图例冲突,出现重复或异常显示。需要让两个图例合并,保持标签一致,同时仅为Total_1显示纹理。
解决方案代码
library(ggplot2) library(ggpattern) library(tidyr) ggplot(data = Run3VsRun8_Savings_long, aes(x = Category, y = Value, fill = Variable, pattern = Variable)) + geom_bar_pattern(position = position_dodge2(padding = 0), stat = "identity", color = 'black', pattern_fill = "white", pattern_angle = 55, pattern_density = 0.5, pattern_spacing = 0.025, pattern_key_scale_factor = 0.6) + geom_text(aes(label = paste0(Value, "%")), position = position_dodge(width = 1), vjust = -0.6, size = 2.8) + geom_vline(xintercept = seq(0.5, length(unique(Run3VsRun8_Savings_long$Category)) - 0.5), linetype = "dashed", color = "black") + scale_fill_manual( values = c("Cooling" = "skyblue", "NG_Heating" = "pink", "Total" = "gold", "Total_1" = "gold"), labels = c("Cooling" = "Cooling", "NG_Heating" = "Heating", "Total" = "Total_ZERH+HP", "Total_1" = "Total_ZERH+HP+Tight Envelope") ) + scale_pattern_manual( values = c('Cooling' = 'none', 'NG_Heating' = 'none', 'Total' = 'none', 'Total_1' = 'stripe'), # 保持labels与scale_fill_manual一致,避免图例分裂 labels = c("Cooling" = "Cooling", "NG_Heating" = "Heating", "Total" = "Total_ZERH+HP", "Total_1" = "Total_ZERH+HP+Tight Envelope") ) + # 合并fill和pattern的图例,共用同一套图例项 guides( fill = guide_legend(override.aes = list(pattern = c('none', 'none', 'none', 'stripe'))), pattern = "none" # 隐藏单独的pattern图例 ) + labs(x = "STUDY REGION", y = "COST SAVINGS (%)", title = 'Single-Wide Homes') + ylim(-120, 10) + theme_minimal() + theme( legend.position = "bottom", legend.direction = "horizontal", legend.box = "horizontal", legend.margin = margin(t = 0, r = 0, b = 0, l = 0), legend.title = element_blank(), legend.text = element_text(size = 10), plot.title = element_text(hjust = 0.5, margin = margin(b = 25), face = 'bold') )
修改说明
- 让
scale_pattern_manual的labels与scale_fill_manual完全一致,确保图例标签统一 - 使用
guides()函数:- 通过
guide_legend的override.aes参数,为每个图例项指定对应的纹理样式 - 设置
pattern = "none"隐藏单独的pattern图例,只保留合并后的fill图例
- 通过
- 既实现了仅
Total_1柱状图显示纹理,同时图例正确显示所有类别,且Total_1的图例项会带有对应纹理
内容的提问来源于stack exchange,提问作者Karthik
相关产品推荐
相关产品推荐

