How to make calendars with ggplot and lubridate

How to make calendars with ggplot and lubridate

You don’t need any additional packages to make beautiful calendars beyond the tidyverse and lubridate. These calendars can be faceted by month using facet_wrap(~mo) for multiple months. You can set aesthetics to make the dates different colors to represent data or multiple geom_text to plot various text information on each box. This is a basic example to get you started, but you can build upon it to make advanced calendars as shown on my blog and elsewhere.

library(tidyverse)
library(lubridate)
rm(list=ls())
tibble(date = seq(as.Date('2023-03-01'), as.Date('2023-03-31'), 'days')) %>%
  # manually setting the weekday using factor with the order of weekdays,
  # make monday first in levels if so desired. Use abbreviations if 
  # desired, but also you'll need to rename the levels
 mutate(wkdy = factor(weekdays(date, abbreviate=T), levels=c('Sun','Mon','Tue','Wed','Thu','Fri','Sat')), 
         wkn = week(date), 
         mo = month(date, label=T, abbr=F),
         day = day(date)) %>%
  # the group_by and dense rank creates
  # a column which represents week of the month
 group_by(mo) %>% 
  mutate(wkn.mo = dense_rank(wkn)) %>%
  ungroup() %>%
  # basically each calendar day is ploted as a geom tile
  # the weekday number and week month number define the location
  ggplot(aes(x=wkdy, y=wkn.mo)) +
  # you can use aesthetics to colorize the tiles
  geom_tile(alpha=0.8, fill='gray', width=0.9, height=0.9) + 
  # use additional geom text to add text of different sizes
  geom_text(aes(label=day), size=20, hjust=0.5) +
  # for multiple months you will want to use facet_wrap
  # set for each month. Scales = "free" ensures the 
  # day of week labels are repeated for each month's calendar
 # but if you use scales = "free" below you have to switch the 
 # coord_fixed to coord_cartersian which will make the grids on the 
 # calendar non-square.
  #   facet_wrap(~mo) +
  # puts the day of week labels up top
  scale_x_discrete(position = "top") +
  # remove any default theme position
  theme_void() +
  # This reverses the axis and adds margin. Need to
  # change for months with six weeks like April 2023 to
  # c(6.6, 0.4). You could also use scale_y_reverse but
  # I'd rather set manual limits so all months have the
  # same height when using facet wrap
  coord_fixed(expand=F, ylim=c(5.6, 0.4)) + 
  # set labels
  labs(title="March 2023", 
       x="", y=""
  ) +
  theme(
    plot.background = element_rect(fill='white', color='white'),
    # strip.placement outside puts month name above the x axis 
    # date lables
    strip.placement = 'outside',
    text = element_text(family = 'Arial', size=40),
    plot.title = element_text(face='bold', size=60, hjust=0.5, margin=margin(0,0,10,0)),
    axis.text.x = element_text(size=20),
    plot.margin = margin(30,30,30,30),
    legend.position = 'none'
  )

Leave a Reply

Your email address will not be published. Required fields are marked *