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)) )
代码说明
- 有序因子处理:将
year_week转为有序因子后,X轴仅显示数据中存在的周,不会自动填充缺失间隔。 - 次轴刻度定位:通过计算每个月份对应周的索引平均值,让次轴月份标签对齐该月所有周的中间位置,视觉更合理。
- 双X轴实现:使用
scale_x_discrete配合sec.axis,既保留双轴功能,又解决了无效日历周的问题。
内容的提问来源于stack exchange,提问作者romina
相关产品推荐
相关产品推荐

