如何在 ggplot2 中对齐不同年份的周数?

How to align weeks in different years in ggplot2?

提问人:Ahsk 提问时间:9/24/2023 最后编辑:M--Ahsk 更新时间:9/27/2023 访问量:116

问:

如何在 x 轴上对齐不同年份的周数,以便对齐同一月份(如 6 月)的周数?

请注意,数据不是在不同年份的同一周内收集的,因此有些周无法对齐,这没关系。我只想对齐不同年份相同月份发生的周数,以比较不同年份不同周数的时间模式。

这是我的代码:

p <- ggplot(df, aes(x = date_in, y = total_count, fill = Treatment)) +
  geom_col() +
  facet_grid(year ~ .) +  # Stacking years on top of each other
  theme_few(base_size = 10, base_family = "Arial") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, 
                                   vjust = 0.5, color = "black"),
        axis.text.y = element_text(vjust = 0.5, color = "black"),
        axis.title = element_text(color = "black"),  
        strip.text = element_text(face = "bold", color = "black")) + 
  facet_wrap(~ year, ncol = 1, scales = "free", 
                     strip.position = "right") + # Righty-axis
  theme(legend.position = "top") +
  scale_fill_manual(values = c("IP" = "blue", "LD" = "red"))  # Set custom colors

# Print the plot
print(p)

以下是可重现的示例数据集:

df <- structure(list(date_in = structure(c(7L, 11L, 11L, 13L, 13L, 
15L, 15L, 18L, 18L, 21L, 5L, 5L, 7L, 7L, 9L, 9L, 11L, 11L, 13L, 
13L, 17L, 17L, 20L, 20L, 23L, 23L, 26L, 26L, 28L, 28L, 1L, 1L, 
2L, 2L, 3L, 3L, 4L, 4L, 6L, 6L), levels = c("03/08", "04/05", 
"04/26", "05/17", "05/19", "05/24", "05/26", "05/31", "06/02", 
"06/07", "06/09", "06/14", "06/16", "06/21", "06/23", "06/28", 
"06/29", "06/30", "07/05", "07/06", "07/07", "07/12", "07/13", 
"07/14", "07/19", "07/21", "07/26", "07/28", "08/02", "08/04", 
"08/09", "08/11", "08/16", "08/17", "08/24", "08/25", "08/31", 
"09/01", "09/07", "09/08", "09/14", "09/15", "09/21", "09/22", 
"09/23", "09/28", "09/29", "09/30", "10/05", "10/06", "10/07", 
"10/12", "10/13", "10/14", "10/19", "10/20", "10/21", "10/26", 
"10/27", "10/29", "11/02", "11/03", "11/04", "11/09", "11/11", 
"11/17", "11/23", "12/01", "12/07"), class = "factor"), total_count = c(0L, 
0L, 0L, 0L, 4L, 3L, 13L, 0L, 0L, 16L, 2L, 2L, 18L, 362L, 135L, 
684L, 34L, 123L, 6L, 21L, 2L, 4L, 6L, 6L, 1L, 11L, 0L, 12L, 1L, 
2L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 11L, 0L, 0L), Treatment = structure(c(2L, 
2L, 4L, 2L, 4L, 2L, 4L, 2L, 4L, 2L, 2L, 4L, 2L, 4L, 2L, 4L, 2L, 
4L, 4L, 2L, 4L, 2L, 2L, 4L, 4L, 2L, 4L, 2L, 2L, 4L, 2L, 4L, 2L, 
4L, 2L, 4L, 2L, 4L, 2L, 4L), levels = c("between_row", "IP", 
"mulch", "LD"), class = "factor"), year = structure(c(1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 
4L, 4L, 4L, 4L, 4L, 4L), levels = c("2010", "2011", "2012", "2013"
), class = "factor")), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -40L))

我想要的输出如下所示

enter image description here

R 数据帧 ggplot2 绘图 dplyr

评论

0赞 Azor Ahai -him- 9/24/2023
获取周数并进行比较会起作用吗?还是日期超级重要?
0赞 M-- 9/24/2023
为什么将 2011 年 5 月 19 日与 2013 年 5 月 17 日保持一致?这是什么规则?前后几天?还是什么?您想要的输出甚至显示 6/02 和 6/29 对齐!!!
0赞 M-- 9/25/2023
您需要在每个图表的底部显示这些日期吗?这是可行的,但需要比我现在更多的时间。如果没有,我们可以根据周数来调整周数。
0赞 Ahsk 9/26/2023
谢谢。它看起来确实更干净,所有周都应该出现在 x 轴上,因为如果没有日期/周,则意味着在这些周内没有进行采样。而且我们不需要图表下方的数字 - 列中的日期就足够了。你不需要花时间在上面,因为我得到了答案,但只是把它扔在那里。

答:

1赞 Andy Baxter 9/24/2023 #1

这也许是一种使用 .从本质上讲,将每个以休息时间作为周数绘制,并重新应用开始周日期作为标签,以便图对齐:patchwork

library(tidyverse)
library(ggthemes)

dates_weeks <- df |>
  mutate(date = mdy(paste0(date_in, year)),
         week = week(date)) 

min_week <- min(dates_weeks$week)
max_week <- max(dates_weeks$week)

year_breaks <- 2010:2013 |>
  set_names() |>
  map(\(year_n) {
    
    year_dat <- dates_weeks |> 
      filter(year == year_n) |> 
      select(date_in, week) |> 
      unique()
    
    week_map <- character(max_week - min_week + 1)
    names(week_map) <- seq(min_week, max_week)
    
    week_map[as.character(year_dat$week)] <- as.character(year_dat$date_in)
    
    
    week_map
    
  })

library(patchwork)

dates_weeks |>
  mutate(week = factor(week), Treatment = factor(Treatment)) |> 
  nest(data = -year) |>
  mutate(pl = map2(data, year, \(data, year) {
    # browser()
    
    ggplot(data, aes(x = week, y = total_count, fill = Treatment)) +
      geom_col() +
      scale_x_discrete(
        labels = year_breaks[[as.character(year)]],
        drop = FALSE
      ) +
      facet_wrap(year, strip.position = "right") +
      scale_fill_manual(values = c("IP" = "blue", "LD" = "red"), drop = FALSE) +
      theme_few(base_size = 10)
    
  })) |>
  pull(pl) |>
  wrap_plots(ncol = 1) +
  plot_layout(guides = "collect") &
  theme(
    legend.position = "top",
    axis.text.x = element_text(
      angle = 90,
      hjust = 1,
      vjust = 0.5,
      color = "black"
    ),
    axis.text.y = element_text(vjust = 0.5, color = "black"),
    axis.title = element_text(color = "black"),
    strip.text = element_text(face = "bold", color = "black")
  )

评论

0赞 Andy Baxter 9/25/2023
编辑后的代码看起来如何?首先过滤掉 0 个观察周。
0赞 Andy Baxter 9/25/2023
哦,我明白!您的意思是,在计数为零的几周内,您期望该图表上有一个空条,但其他图表的同一周下方没有标签?所以应该出现在最低的图表上,但在它们上方应该是没有标签的空白区域?03/0804/05
0赞 Andy Baxter 9/25/2023
上面的代码怎么样?我为每个方面提供了一组唯一的命名值作为周间休息时间,以排列跨年份的周数。上图中的关键是,即使日期不同,它也会将 和 作为同一周和同一周。我认为你想要的是什么,而其他方法正在努力管理?2010/07/072012/07/062011/05/192013/05/17
0赞 Andy Baxter 9/25/2023
对不起,忘记取出过滤语句。已在上面更新 - 目前每年每周都排队。我想的最后一个问题是,在顶部图中应该与第二张图(一年中的同一周)或在第二张图和第三张图中(每年所有第一个六月的观测值)对齐?06/0906/0906/0206/29
1赞 M-- 9/24/2023 #2

更新:

如果您需要根据日期编号对齐日期,那么我们需要获取周数并将其用于 .ggplot

我不知道有一种方法可以在分面中拥有自定义轴刻度标签,所以我只使用了轴上的周数并为实际日期添加了标签。不过,您可以创建多个绘图并将它们拼接在一起。

library(dplyr)
library(tidyr)
library(ggplot2)
library(ggthemes)
  
df %>% 
  mutate(across(where(is.factor), as.character),
         week_no = strftime(paste(year, date_in, sep = "/"), format = "%V")) %>% 
  filter(total_count != 0) %>%
  pivot_wider(id_cols = c(week_no, year, date_in), 
              names_from = c(Treatment), values_from = c(total_count)) %>% 
  full_join(expand.grid(week_no = unique(.$week_no), year = unique(.$year)), .) %>% 
  replace_na(., list(date_in = "", LD = 0, IP = 0)) %>% 
  pivot_longer(LD:IP, names_to = "Treatment", values_to = "total_count") %>% 
  mutate(date_in = ifelse(Treatment == "IP", date_in, "")) %>% 
 ggplot(., aes(x = week_no, y = total_count, fill = Treatment)) +
  geom_col() +
  geom_text(aes(y=0, label=date_in), vjust=-0.5, color = "darkgray") +
  facet_wrap(~ year, ncol = 1, scales = "free_y", strip.position = "right") + 
  scale_fill_manual(values = c("IP" = "navyblue", "LD" = "darkred")) +
  xlab("Week no.") + ylab("Total Count") +
  theme_few(base_size = 10, base_family = "Arial") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, 
                                   vjust = 0.5, color = "black"),
        axis.ticks.x = element_blank(),
        axis.text.y = element_text(vjust = 0.5, color = "black"),
        strip.text = element_text(face = "bold", color = "black"),
        legend.position = "top")

创建于 2023-09-25 使用 reprex v2.0.2

原文答案:

您可以过滤掉空行,并仅显示具有 .free_xdate_intotal_count

library(ggplot2)
library(ggthemes)

ggplot(df[df$total_count != 0,], aes(x = date_in, y = total_count,
                                     fill = Treatment)) +
  geom_col() +
  facet_wrap(~ year, ncol = 1, scales = "free_y", strip.position = "right") + 
  scale_fill_manual(values = c("IP" = "blue", "LD" = "red")) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5, 
                                   color = "black"),
        axis.text.y = element_text(vjust = 0.5, color = "black"),
        axis.title = element_text(color = "black"),
        strip.text = element_text(face = "bold", color = "black"),
        legend.position = "top") +
  theme_few(base_size = 10, base_family = "Arial")

创建于 2023-09-23 使用 reprex v2.0.2