You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

修改说明

  1. 让scale_pattern_manual的labels与scale_fill_manual完全一致,确保图例标签统一
  2. 使用guides()函数:
    • 通过guide_legend的override.aes参数,为每个图例项指定对应的纹理样式
    • 设置pattern = "none"隐藏单独的pattern图例,只保留合并后的fill图例
  3. 既实现了仅Total_1柱状图显示纹理,同时图例正确显示所有类别,且Total_1的图例项会带有对应纹理

内容的提问来源于stack exchange,提问作者Karthik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 20:08:11