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

ggplot2双X轴柱状图问题:自动添加数据集外的日历周

问题:ggplot2双X轴柱状图出现不存在的日历周

使用ggplot2绘制多月份qty数据的柱状图,添加双X轴后,X轴自动生成数据集中未包含的日历周(如CW29/CW30、CW19/CW20)。若直接用year_week作为X轴,该问题消失,但无法通过sec.axis添加双X轴。

数据集

sample <- data.frame(service_type = c("A","B","C","A","B","A","B","C","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","A","B","C","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C","A","B","C"),
                     qty = c(-20,-74,248,-32,-39,-17,-173,234,225,-22,-85,182,-41,-65,216,-35,-129,177,-29,-60,260,-29,-165,215,-16,-143,157,-17,-131,174,-25,-110,211,-11,-109,198,-19,-78,148,-17,-62,155,-31,-103,173,-17,-101,177,-16,-95,164,-14,-56,-32,-93,225,264,-27,-196,168,-25,-136,124,-17,-79,108,-33,-129,195,-16,-80,169,-12,-72,212,-20,-147,223,-14,-155,146,-24,-140,225,-28,-92,288,-31,-159,295,-28,-131,215,-22,-68,274,-20,-150,249,-17,-85,229,-28,-143,222,-32,-137,262,-19,-109,287,-33,-115,231,-23,-85,227,-27,-96,169),
                     xlabel2 = c("Sep-22","Sep-22","Sep-22","Oct-22","Oct-22","Mar-23","Mar-23","Mar-23","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Oct-22","Nov-22","Nov-22","Nov-22","Mar-23","Mar-23","Mar-23","Nov-22","Nov-22","Nov-22","Mar-23","Mar-23","Mar-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","Apr-23","May-23","May-23","May-23","Sep-22","Sep-22","Nov-22","Nov-22","Nov-22","Sep-22","Aug-22","Aug-22","Aug-22","Aug-22","Aug-22","Aug-22","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Jan-23","Feb-23","Feb-23","Feb-23","Feb-23","Feb-23","Feb-23","Feb-23","Feb-23","Feb-23","Aug-22","Aug-22","Aug-22","Aug-22","Aug-22","Aug-22","Feb-23","Feb-23","Feb-23","Sep-22","Sep-22","Sep-22","Mar-23","Mar-23","Mar-23","Sep-22","Sep-22","Sep-22","Nov-22","Nov-22","Nov-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22","Dec-22"),
                     year_week = c("2022 - CW38","2022 - CW38","2022 - CW38","2022 - CW39","2022 - CW39","2023 - CW10","2023 - CW10","2023 - CW10","2022 - CW39","2022 - CW40","2022 - CW40","2022 - CW40","2022 - CW41","2022 - CW41","2022 - CW41","2022 - CW42","2022 - CW42","2022 - CW42","2022 - CW43","2022 - CW43","2022 - CW43","2022 - CW44","2022 - CW44","2022 - CW44","2023 - CW11","2023 - CW11","2023 - CW11","2022 - CW45","2022 - CW45","2022 - CW45","2023 - CW12","2023 - CW12","2023 - CW12","2023 - CW13","2023 - CW13","2023 - CW13","2023 - CW15","2023 - CW15","2023 - CW15","2023 - CW14","2023 - CW14","2023 - CW14","2023 - CW16","2023 - CW16","2023 - CW16","2023 - CW17","2023 - CW17","2023 - CW17","2023 - CW18","2023 - CW18","2023 - CW18","2022 - CW36","2022 - CW36","2022 - CW46","2022 - CW46","2022 - CW46","2022 - CW36","2022 - CW31","2022 - CW31","2022 - CW31","2022 - CW32","2022 - CW32","2022 - CW32","2023 - CW01","2023 - CW01","2023 - CW01","2023 - CW02","2023 - CW02","2023 - CW02","2023 - CW03","2023 - CW03","2023 - CW03","2023 - CW04","2023 - CW04","2023 - CW04","2023 - CW05","2023 - CW05","2023 - CW05","2023 - CW06","2023 - CW06","2023 - CW06","2023 - CW07","2023 - CW07","2023 - CW07","2022 - CW33","2022 - CW33","2022 - CW33","2022 - CW34","2022 - CW34","2022 - CW34","2023 - CW08","2023 - CW08","2023 - CW08","2022 - CW35","2022 - CW35","2022 - CW35","2023 - CW09","2023 - CW09","2023 - CW09","2022 - CW37","2022 - CW37","2022 - CW37","2022 - CW47","2022 - CW47","2022 - CW47","2022 - CW48","2022 - CW48","2022 - CW48","2022 - CW49","2022 - CW49","2022 - CW49","2022 - CW50","2022 - CW50","2022 - CW50","2022 - CW51","2022 - CW51","2022 - CW51","2022 - CW52","2022 - CW52","2022 - CW52") )

原始代码

sample <- sample %>%
  mutate(date = as.Date(paste0(year_week, 1), "%Y - CW%U%u")) 

sample %>%
  ggplot() +
  aes(x = date, fill = service_type, weight = qty) +
  geom_bar(position = "dodge") +
  scale_fill_hue(direction = 1) +
  scale_x_date(
    date_labels = "%Y - CW%U",
    date_breaks = "1 week",
    sec.axis = dup_axis(
      breaks = as.Date(paste0("5-", unique(sample$xlabel2)), "%d-%b-%y"),
      labels = \(x) format(x, "%b-%y")
    )
  ) +
  theme_minimal() +
  theme(legend.position = "left", axis.text.x.bottom = element_text(angle=90, hjust=1))
解决方案

问题核心是scale_x_date基于连续日期轴生成刻度,会自动填充缺失的周间隔。解决思路是将year_week转为有序因子,同时手动构建双X轴的映射关系:

修改后代码

library(dplyr)
library(ggplot2)

# 预处理数据集:将year_week转为有序因子,映射月份到对应周位置
sample_processed <- sample %>%
  distinct(year_week, xlabel2) %>%
  arrange(year_week) %>%
  mutate(week_idx = row_number()) %>%
  right_join(sample, by = c("year_week", "xlabel2")) %>%
  mutate(year_week = factor(year_week, levels = unique(year_week[order(week_idx)]), ordered = TRUE))

# 计算每个月份对应的中间周位置,用于次轴刻度定位
month_positions <- sample_processed %>%
  distinct(xlabel2, week_idx) %>%
  group_by(xlabel2) %>%
  summarise(pos = mean(week_idx))

# 绘制双X轴柱状图
sample_processed %>%
  ggplot() +
  aes(x = year_week, fill = service_type, weight = qty) +
  geom_bar(position = "dodge") +
  scale_fill_hue(direction = 1) +
  scale_x_discrete(
    labels = levels(sample_processed$year_week),
    sec.axis = dup_axis(
      breaks = month_positions$pos,
      labels = month_positions$xlabel2,
      name = "Month"
    )
  ) +
  theme_minimal() +
  theme(
    legend.position = "left",
    axis.text.x.bottom = element_text(angle = 90, hjust = 1),
    axis.text.x.top = element_text(margin = margin(t = 10))
  )

代码说明

  1. 有序因子处理:将year_week转为有序因子后,X轴仅显示数据中存在的周,不会自动填充缺失间隔。
  2. 次轴刻度定位:通过计算每个月份对应周的索引平均值,让次轴月份标签对齐该月所有周的中间位置,视觉更合理。
  3. 双X轴实现:使用scale_x_discrete配合sec.axis,既保留双轴功能,又解决了无效日历周的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 14:07:03