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

如何在多个ggplot柱状图中保持统一柱宽、柱间距并适配图片高度

统一柱宽、柱间距并适配图片高度的独立柱状图生成方案

需求背景

需要生成多个独立柱状图,要求:

  • 所有图表的柱宽完全一致,柱间距统一
  • 每个图表单独保存,图片高度自动适配柱子数量(柱子越少,图片高度越小)
  • 仅使用geom_bar()结合ggsave(),不使用分面/网格布局

可复现示例

依赖库

library(tidyverse)
library(ggplot2)

模拟数据

实际场景含数百变量,部分因子最多15个水平:

df <- structure(list(Animal = structure(c(1L, 2L, 2L, 2L, 1L, 1L, 1L, 
                                          1L, 3L, 3L), levels = c("Cat", "Dog", "Horse"), class = "factor"), 
                     Sex = structure(c(2L, 1L, 1L, 1L, 2L, 2L, 1L, 2L, 2L, 2L), levels = c("Female", 
                                                                                           "Male"), class = "factor")), row.names = c(NA, 10L), class = "data.frame")

当前代码问题

现有代码通过width = 0.35*n_levels/3实现了柱宽统一,但柱间距不一致;且所有图片高度相同,未适配柱子数量。


解决方案

问题1:统一柱宽与柱间距

核心思路是固定柱子的绝对宽度,同时通过调整离散坐标轴的刻度间距来控制柱间距,而非依赖geom_bar()的相对宽度参数:

  1. 给geom_bar()设置固定的width值(比如0.8),确保所有柱子的绝对宽度一致
  2. 利用scale_y_discrete()(因coord_flip()后原x轴转为y轴)的expand参数,统一控制柱子上下的空白间距,保证不同图表的柱间距基准一致

问题2:自动适配图片高度

根据柱子数量计算ggsave()的height参数:给每个柱子分配固定高度,再加上标题、副标题所需的额外固定高度,实现图片高度随柱子数量动态调整。


完整修改后的函数

# 创建计数器
counter <- 0

# 定义函数
make_bar_plots <- function(df){ 
  # 定义固定参数:柱子绝对宽度、单柱子高度、标题区域额外高度
  bar_fixed_width <- 0.8
  per_bar_height <- 0.8
  extra_height <- 1.5

  # 遍历数据框列
  for (col in colnames(df)){ 
    # (1) 准备工作
    N <- sum(!is.na(df[col]))
    n_levels <- length(unique(df[!is.na(df[col]), col]))[1]
    counter <<- counter + 1
    
    if (is.factor(df[[col]])) {
      # 处理数据:过滤NA并按频率排序因子
      plot_data <- df[col] %>% 
        filter(!is.na(!!sym(col))) %>% 
        mutate({{col}} := fct_rev(fct_infreq(!!sym(col))))
      
      # (2) 基础柱状图绘制
      p <- ggplot(plot_data, aes(x = !!sym(col))) +
        geom_bar(aes(y = (..count..)/sum(..count..)),
                 width = bar_fixed_width, # 固定柱宽
                 fill = 'black',
                 size = 1.5) +
        # 统一控制离散刻度的上下间距
        scale_y_discrete(expand = expansion(add = c(0.5, 0.5))) +
        coord_flip(clip = 'off')
      
      # (3) 标注信息
      p <- p + geom_text(aes(label = scales::percent((..count..)/sum(..count..),
                                                     accuracy = 0.1L),
                             y = ((..count..)/sum(..count..))),
                         stat="count", hjust = -0.1, size=4.2) +
        labs(title = col,
             subtitle = paste0('N = ', format(N, big.mark = ',')))
      
      # (4) 外观设置
      p <- p + theme_minimal() +
        theme(axis.title.y = element_blank(),
              axis.title.x = element_blank(),
              axis.text.x = element_blank(),
              axis.text.y = element_text(colour='black', size = 12.5),
              plot.margin = margin(r=25, l=1, b=0, t=1),
              plot.title = element_text(size = 16.5),
              plot.subtitle = element_text(size = 12.5),
              panel.grid = element_blank())
      
      # (5) 计算动态高度并保存图片
      plot_height <- per_bar_height * n_levels + extra_height
      ggsave(paste(counter, '_', col, '.png'), 
             path = 'plots/', 
             bg = 'white',
             height = plot_height,
             width = 8) # 固定宽度保证横向空间一致
      print(p)
    } 
  }
}

运行验证

make_bar_plots(df)

修改后效果:

  • 所有图表的柱子宽度完全一致,柱间距统一
  • Sex(2个柱子)的图片高度为0.8*2+1.5=3.1,Animal(3个柱子)为0.8*3+1.5=3.9,自动适配柱子数量

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:40:15