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

ggplot2:按年份分面并按月份对齐X轴日期的实现方案

日历热力图:实现分面周对齐与月份标签显示

需求说明

需要绘制每日数据的日历热力图,满足以下要求:

  • Y轴为周一至周日(按周一开始排序)
  • X轴为年周数,但显示月份标签
  • 按年份分面(每行一个年份)
  • 各分面的年周完全对齐
  • 每个热力图方块(tile)为完美方形

测试数据集

先修正原数据中的变量名错误(VectorofDates未定义,改为Date):

library(tidyverse)
library(lubridate)

my_data <- tibble(
  Date = seq(
    as.Date("1/11/2013", "%d/%m/%Y"), 
    as.Date("31/12/2014", "%d/%m/%Y"), 
    "days"
  ),
  Value = runif(length(Date))
)

现有尝试的问题

尝试1:用week()标记周数(周对齐但无月份标签)

该方法能保证分面周对齐,但X轴是纯周数,无法直接替换为月份标签:

my_data %>% 
  mutate(Week = week(Date)) %>% 
  mutate(Weekday = wday(Date, label = TRUE, week_start = 1)) %>% 
  mutate(Year = year(Date)) %>% 
ggplot(aes(fill = Value, x = Week, y = Weekday)) +
  geom_tile() +
  theme_bw() +
  facet_grid(Year ~ .) +
  coord_fixed()

尝试2:用日期型周起始(标签正确但周不对齐)

该方法X轴能显示月份标签,但跨年份的周无法对齐,且方块难以做到完美方形:

my_data %>% 
  mutate(Week = floor_date(Date, week_start = 1),
         Week = round_date(Week, "week", week_start = 1)) %>% 
  mutate(Weekday = wday(Date, label = TRUE, week_start = 1)) %>% 
  mutate(Year = year(Date)) %>% 
  ggplot(aes(fill = Value, x = Week, y = Weekday)) +
  geom_tile() +
  theme_bw() +
  facet_grid(Year ~ .) +
  scale_x_datetime(name = NULL, labels = label_date("%b")) +
  coord_fixed(7e5)

解决方案:手动映射月份到周数位置

核心想法:用周序号作为x轴数值保证对齐,同时计算每个月份对应的周数位置,手动替换X轴标签,结合coord_fixed()实现方形方块。

步骤1:数据预处理

添加周数、星期几、年份,同时计算每个周对应的月份,以及每个年份的周范围和月份标签位置:

data_processed <- my_data %>%
  mutate(
    Year = year(Date),
    Week = week(Date),
    Weekday = wday(Date, label = TRUE, week_start = 1),
    Month = month(Date, label = TRUE)
  ) %>%
  # 按年份+月份分组,计算对应周数的中间值(用于放置月份标签)
  group_by(Year, Month) %>%
  mutate(month_week_mid = median(Week)) %>%
  ungroup() %>%
  # 按年份获取周数的最小/最大值,保证分面x轴范围匹配
  group_by(Year) %>%
  mutate(
    min_week = min(Week),
    max_week = max(Week)
  ) %>%
  ungroup()

步骤2:绘制热力图

使用scale_x_continuous手动设置X轴刻度和标签,结合coord_fixed()确保方块方形:

ggplot(data_processed, aes(x = Week, y = Weekday, fill = Value)) +
  geom_tile(color = "white", size = 0.5) + # 白色边框区分方块
  # 分面:自由调整X轴长度但保持周数对齐
  facet_grid(Year ~ ., scales = "free_x", space = "free_x") +
  # 手动替换X轴:用月份对应周数的中间值做刻度位置,显示月份名称
  scale_x_continuous(
    name = NULL,
    breaks = unique(data_processed$month_week_mid),
    labels = unique(data_processed$Month),
    expand = c(0, 0) # 去掉X轴两端留白
  ) +
  scale_fill_viridis_c(option = "plasma") + # 可选:替换配色方案
  theme_bw() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1), # 月份标签倾斜显示
    panel.grid = element_blank(), # 去掉网格线
    strip.background = element_rect(fill = "#f0f0f0") # 优化分面标题背景
  ) +
  coord_fixed(ratio = 1) # 强制方块为完美正方形

关键说明

  • 周对齐:用week(Date)生成的周序号作为x轴数值,不同年份的周数直接对应,分面时自然对齐。
  • 月份标签:通过计算每个月份对应的周数中位数,将其作为X轴刻度位置,替换为月份名称,实现“X轴是周数但显示月份标签”的效果。
  • 方形方块:coord_fixed(ratio = 1)保证x轴和y轴单位长度一致,配合expand = c(0,0)去掉留白,让每个方块成为正方形。
  • 分面适配:scales="free_x"和space="free_x"让不同年份的X轴长度根据实际周数调整,同时保持周数的对齐关系。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 18:37:15