PAYATHON 2026

R:如何调整柱状图中各月份柱子之间的间距?

技术老齐

要让各月份的柱子等距排列,可以把横轴改为按月份排序的因子,再用 width 调整柱间空白。width 越小,柱子越窄,空白越大;反之,空白越小。

保留日期轴时也能调整 width,但相邻月份的柱子中心距离仍由实际天数决定。

为什么 Jan 和 Feb 之间更宽?

原代码把日期类型的 month_day 映射到横轴,并使用 scale_x_date(),因此柱子按实际时间间隔排列。

假设每个月的数据都在月初,将日期统一到 2027 年后:

  • 1 月 1 日到 2 月 1 日相差 31 天。
  • 2 月 1 日到 3 月 1 日相差 28 天。

所以,Jan 和 Feb 的柱子中心距离比 Feb 和 Mar 更大。统一年份仍会保留月份天数的差异,"%b" 也只改变标签的显示格式。

推荐做法:使用离散月份轴

将月份转换为因子,并指定 1 至 12 月的顺序。这样,相邻月份在离散轴上的中心距离就相同了,柱间空白则由柱宽控制。ggplot2 离散位置刻度说明

同一个月有多条日期记录时,可以先按年月和类别汇总,再绘图。以下代码保留了按年份分面、按类别堆叠和柱内标签。Month 需已是 Date 或 lubridate 能处理的日期时间类型。

library(dplyr)
library(lubridate)
library(ggplot2)

my_group_sum <- my_group %>%
  mutate(
    year = year(Month),
    month_num = month(Month)
  ) %>%
  group_by(year, month_num, Category) %>%
  summarise(
    Incidents = sum(Incidents),
    .groups = "drop"
  ) %>%
  mutate(
    month_label = factor(
      month_num,
      levels = 1:12,
      labels = month.abb
    )
  )

ggplot(
  my_group_sum,
  aes(x = month_label, y = Incidents, fill = Category)
) +
  geom_col(
    width = 0.7,
    position = position_stack(reverse = TRUE)
  ) +
  geom_text(
    aes(label = Incidents, color = Category),
    position = position_stack(reverse = TRUE, vjust = 0.5),
    size = 5,
    fontface = "bold"
  ) +
  scale_x_discrete(drop = FALSE) +
  facet_wrap(~ year) +
  labs(x = NULL, y = "Incidents") +
  theme(
    axis.text.x = element_text(
      color = "black",
      size = 12,
      angle = 45,
      hjust = 1
    )
  )

geom_col() 直接绘制汇总后的 Incidents,与原来的 geom_bar(stat = "identity") 用途一致,两者都支持设置 width。ggplot2 柱状图说明

在离散轴上,可以尝试以下柱宽:

width = 0.5  # 柱子较窄,柱间空白较大
width = 0.9  # 柱子较宽,柱间空白较小
width = 1   # 相邻月份都有柱子时,柱体边缘相接

调整 width 时,月份中心的位置保持不变。原有的图例、字体和边框等 theme() 设置可以继续添加到示例末尾。

保留日期轴时如何调整?

如果需要表达真实的时间间隔,可以保留原来的 month_day 和日期轴,在柱形图层中增加 width:

geom_bar(
  stat = "identity",
  width = 20,
  alpha = 1,
  position = position_stack(reverse = TRUE)
)

对于 Date 横轴,width = 20 表示柱宽为 20 天。减小这个值会增大柱间空白,增大则会缩小空白。如果柱宽超过相邻日期的距离,柱子就会重叠。

日期刻度可以这样设置:

scale_x_date(
  date_breaks = "1 month",
  date_labels = "%b"
)

date_breaks 控制刻度间隔,date_labels 控制标签格式,两者都不会让月份等距排列。ggplot2 日期刻度说明

注意事项

  • 原代码中的 size = 2.5 不控制柱宽,调整柱间空白应使用 width。
  • scale_x_discrete(drop = FALSE) 会保留全部 12 个月的位置。缺少数据的月份留空,不会自动补成零。如果只想显示数据中出现的月份,可以去掉这一设置。
  • expand 控制横轴两端的留白,panel.spacing 控制年份分面之间的距离。调整同一面板内的柱间空白时,应优先设置 width。
  • 改用月份因子后,不再需要构造固定年份的 month_day,也应移除原来的 scale_x_date()。

备注:内容仅供参考。