如何消除仅含每年1-3月的时间序列ggplot柱状图间隙?
问题
需要用R语言ggplot2绘制时间序列柱状图,数据包含40余年的每年1、2、3月数据,部分年份存在缺月情况(如仅含1月和3月数据)。要求:
- 每个数据月份对应一个独立柱状
- 柱状紧密排列无间隙
- x轴仅显示年份
尝试过以下方法但未解决:
- 以
date为x轴绘制:柱状间因预留4-12月位置出现间隙,调整scale_x_date参数无效 - 自定义
YearMonth_Label因子作x轴:柱状无间隙但轴标签未按时间顺序排列 - 按年份因子绘制:效果接近但未实现每月柱状紧密排列
附示例数据及尝试代码:
# 示例数据 example <- structure(list(season = c("winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter" ), date = c("19-Mar-90", "20-Feb-91", "12-Feb-12", "07-Jan-94", "28-Mar-85", "26-Feb-92", "11-Feb-92", "24-Mar-10", "30-Mar-13", "18-Feb-83", "11-Mar-94", "02-Feb-10", "29-Jan-88", "04-Feb-83", "10-Jan-89", "06-Jan-93", "13-Feb-77", "02-Feb-11", "03-Mar-79", "27-Mar-81", "28-Jan-85", "13-Mar-08", "15-Feb-17", "14-Jan-90", "31-Mar-82"), day = c(19L, 20L, 12L, 7L, 28L, 26L, 11L, 24L, 30L, 18L, 11L, 2L, 29L, 4L, 10L, 6L, 13L, 2L, 3L, 27L, 28L, 13L, 15L, 14L, 31L), month = c("Mar", "Feb", "Feb", "Jan", "Mar", "Feb", "Feb", "Mar", "Mar", "Feb", "Mar", "Feb", "Jan", "Feb", "Jan", "Jan", "Feb", "Feb", "Mar", "Mar", "Jan", "Mar", "Feb", "Jan", "Mar"), monthNum = c(3L, 2L, 2L, 1L, 3L, 2L, 2L, 3L, 3L, 2L, 3L, 2L, 1L, 2L, 1L, 1L, 2L, 2L, 3L, 3L, 1L, 3L, 2L, 1L, 3L ), year = c(1990L, 1991L, 2012L, 1994L, 1985L, 1992L, 1992L, 2010L, 2013L, 1983L, 1994L, 2010L, 1988L, 1983L, 1989L, 1993L, 1977L, 2011L, 1979L, 1981L, 1985L, 2008L, 2017L, 1990L, 1982L ), Anomaly_yr = c(-0.0735902041331981, -0.59089540108907, 0, 0.614707415070896, -0.707106781186548, -0.5, -0.5, -0.707106781186547, -0.707106781186547, 0.694734791305552, -0.892574080787689, 0.707106781186548, -0.707106781186547, -0.537781361281464, 0.225223907763323, 1.15470053837925, -0.707106781186548, -0.574952563436588, 0.357882806098879, -0.707106781186547, 0.707106781186548, 1.15470053837925, 0.707106781186548, 0.810170077754531, -0.707106781186547)), class = c("grouped_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, -25L), groups = structure(list( season = c("winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter", "winter"), year = c(1977L, 1979L, 1981L, 1982L, 1983L, 1985L, 1988L, 1989L, 1990L, 1991L, 1992L, 1993L, 1994L, 2008L, 2010L, 2011L, 2012L, 2013L, 2017L), .rows = structure(list( 17L, 19L, 20L, 25L, c(10L, 14L), c(5L, 21L), 13L, 15L, c(1L, 24L), 2L, 6:7, 16L, c(4L, 11L), 22L, c(8L, 12L), 18L, 3L, 9L, 23L), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame" ), row.names = c(NA, -19L), .drop = TRUE)) # 格式化日期 example$date <- as.Date(example$date, format = "%d-%b-%y") # 颜色分组 library(dplyr) example <- example %>% mutate(col = factor(ifelse(Anomaly_yr > 0, 2, 1)))
尝试代码1:
ggplot(data = example, mapping = aes(x = date, y = Anomaly_yr, fill = col, color = col )) + geom_bar(stat = "identity", position = "dodge", show.legend=FALSE)
尝试代码2:
ggplot(data = example, aes(x = date, y = Anomaly_yr, fill = col, color = col)) + geom_bar(stat = "identity", position = "dodge", show.legend=FALSE) + scale_x_date(date_labels = "%Y - %b", date_breaks = "1 month")
尝试代码3:
example$YearMonth <- as.Date(paste(example$year, example$monthNum, "1", sep = "-")) example$YearMonth <- as.Date(paste0(format(example$YearMonth, "%Y-%m"), "-01")) example$YearMonth_Label <- format(example$YearMonth, "%Y %b") month_order <- c("Jan", "Feb", "Mar") example$YearMonth_Label <- factor(example$YearMonth_Label, levels = unique(example$YearMonth_Label, "%Y %b")) ggplot(data = example, aes(x = YearMonth_Label, y = Anomaly_yr, fill = col)) + geom_bar(stat = "identity", position = "dodge", show.legend=FALSE) + scale_x_discrete(labels = function(x) str_wrap(x, width = 8)) + theme(axis.text.x = element_text(angle = 90))
尝试代码4:
ggplot(data = example, aes(x = as.factor(year), y = Anomaly_yr, fill = col)) + geom_bar(stat = "identity", position = "dodge", show.legend=FALSE)
解决方案
核心思路是生成按时间顺序排序的年份-月份因子作为x轴,同时自定义x轴标签仅显示年份,确保同一年的月份柱状紧密排列,不同年份的柱状按时间顺序排列。
步骤1:数据处理,生成有序的x轴因子
先按年份和月份序号排序数据,然后创建包含年份和月份的标签,并将其设为有序因子,保证时间顺序:
# 加载必要包 library(ggplot2) library(dplyr) library(stringr) # 按年份和月份序号排序数据,确保时间顺序 example_sorted <- example %>% arrange(year, monthNum) %>% # 创建年份-月份标签 mutate(YearMonth_Label = paste(year, month, sep = " ")) %>% # 将标签转换为有序因子,levels按排序后的顺序设置 mutate(YearMonth_Label = factor(YearMonth_Label, levels = unique(YearMonth_Label)))
步骤2:绘制符合要求的柱状图
使用生成的有序因子作为x轴,关闭position="dodge"(因为每个因子水平对应一个柱子,无需偏移),然后自定义x轴标签只提取年份,同时调整标签样式避免重叠:
ggplot(data = example_sorted, aes(x = YearMonth_Label, y = Anomaly_yr, fill = col, color = col)) + # 绘制柱状图,无间隙紧密排列 geom_bar(stat = "identity", show.legend = FALSE) + # 自定义x轴标签,仅显示年份 scale_x_discrete( labels = function(x) str_extract(x, "^\\d{4}"), # 可选:设置标签间隔,避免年份重复显示(例如每2年显示一次) breaks = function(x) x[seq(1, length(x), by = 3)] # 按实际数据密度调整by值 ) + # 调整x轴文本样式,避免重叠 theme(axis.text.x = element_text(angle = 45, hjust = 1)) + labs(x = "年份", y = "异常值")
关键说明
- 有序因子
YearMonth_Label确保所有柱状按时间顺序排列,不会出现乱序问题 geom_bar默认position="stack",这里因为每个x水平对应一个柱子,所以用position="identity"即可实现紧密无间隙排列scale_x_discrete的labels参数通过正则提取年份,实现x轴仅显示年份的需求;breaks参数可以控制年份标签的显示间隔,避免过多标签重叠
内容的提问来源于stack exchange,提问作者FishyFishies
相关产品推荐
相关产品推荐

