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

如何消除仅含每年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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:30:55