Plot weekly data with monthly axis labels

I am trying to make a line plot where each point is a weekly count with two lines, one for 2019 and one for 2020. Because the week numbers in different years don't have the same start date, I've created new variable for year and week number.

This is all fine, but what I'm having trouble with is changing the x-axis labels or scales so that they are months instead of week numbers. Would it be possible to have axis labels and grid lines that indicate the first day of each month (in 2020), rather than the week number? I'm not sure how to do this because by x-axis data I am plotting is a week number rather than date.

Data
df <- structure(list(year_week = structure(c(17896, 17903, 17910, 17917, 
17924, 17931, 17938, 17945, 17952, 17959, 17966, 17973, 17980, 
17987, 17994, 18001, 18008, 18015, 18022, 18029, 18036, 18043, 
18050, 18057, 18064, 18071, 18078, 18085, 18092, 18099, 18106, 
18113, 18120, 18127, 18134, 18141, 18148, 18155, 18162, 18169, 
18176, 18183, 18190, 18197, 18204, 18211, 18218, 18225, 18232, 
18239, 18246, 18253, 18260, 18267, 18274, 18281, 18288, 18295, 
18302, 18309, 18316, 18323, 18330, 18337, 18344, 18351, 18358, 
18365), class = "Date"), year = c(2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2020, 2020, 2020, 2020, 2020, 
2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020
), week = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 
48, 49, 50, 51, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 
14, 15, 16), n = c(91L, 114L, 93L, 83L, 95L, 94L, 114L, 97L, 
82L, 80L, 91L, 85L, 109L, 93L, 98L, 96L, 78L, 84L, 88L, 82L, 
95L, 103L, 108L, 110L, 94L, 94L, 80L, 85L, 85L, 102L, 74L, 92L, 
61L, 96L, 89L, 90L, 95L, 80L, 82L, 85L, 77L, 97L, 110L, 108L, 
90L, 103L, 89L, 73L, 69L, 104L, 109L, 82L, 81L, 105L, 101L, 109L, 
96L, 100L, 84L, 88L, 83L, 81L, 95L, 72L, 65L, 49L, 57L, 58L)), class = "data.frame", 
row.names = c(NA, -68L))
head(df)
#>    year_week year week   n
#> 1 2018-12-31 2019    1  91
#> 2 2019-01-07 2019    2 114
#> 3 2019-01-14 2019    3  93
#> 4 2019-01-21 2019    4  83
#> 5 2019-01-28 2019    5  95
#> 6 2019-02-04 2019    6  94

library(ggplot2)

ggplot(df, aes(week, n, color = factor(year))) +
  geom_line()

Created on 2020-04-22 by the reprex package (v0.3.0)

I think you're looking for the scale_x_date function, which will give you finer control over how you scale and label the date-related ticks on your x-axis. See here for the help page.

Perhaps something along these lines:

month <- seq(as.Date("2020-01-01"), 
             as.Date("2020-12-01"), 
             by = "1 month")

month_numeric <- as.numeric(format(month, format = "%U"))
month_label <- format(month, format = "%b")

and then add the following to your plot

scale_x_continuous(breaks = month_numeric, 
                   labels = month_label)
1 Like

And right on cue, here's a new blog post that goes through some of these strategies:

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

Aha that's great. I modified your code just a bit to calculate the first of each month as a fraction of the number of weeks in a year so that the axis-tick actually represents the first of the month rather than the first whole week in that month. And I think this give me what I was after. Thanks again @nutterb!

month <- seq(as.Date("2020-01-01"), 
             as.Date("2020-12-01"), 
             by = "1 month")

month_numeric <- lubridate::yday(month) / 365 * 52 + 1
month_label <- lubridate::month(month, label = TRUE)

ggplot(df, aes(week, n, color = factor(year))) +
 geom_line() +
 scale_x_continuous(breaks = month_numeric, 
                    labels = month_label)

Created on 2020-04-22 by the reprex package (v0.3.0)

This is simpler and good enough for visualization purposes

library(tidyverse)

df %>% 
    group_by(week) %>% 
    mutate(year_week = first(year_week)) %>% 
    ggplot(aes(year_week, n, color = factor(year))) +
    geom_line() +
    scale_x_date(date_breaks = "1 month", date_labels = "%b")

2 Likes

This is simpler, thank you! As you say it doesn't really matter for plotting, but one tiny difference here is that the month grid lines will be aligned to the 2019 dates and not 2020.