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'
  )

Map: Alma Pond
Map: Dobbins Memorial State Forest
Map: Little John Wildlife Management Area
Map: Otter Lake
Map: South Hill State Forest (Oneida 23)
Map: Summer Hill State Forest
Map: West Parishville State Forest
SVGZ Graphic: albany-snow-depth
SVGZ Graphic: college-rate
SVGZ Graphic: december-holidays
SVGZ Graphic: ht2025
SVGZ Graphic: lt2025
SVGZ Graphic: Places Named Bethlehem
SVGZ Graphic: Towns with Most Similiar Land Cover to the Town of Bethlehem
Terrain Map: Happy World Milk Day!
Photo: Oil Creek
Photo: Kenka Mills Ruins
Photo: Kueka Outlet
Photo: Pitch Pine Forest
Photo: Can see some blue sky through the clouds
Photo: Ridges In The Distance
Photo: West
Photo: Low head dam
Photo: 2.5 Miles to Vanderwhacker Firetower
Photo: Evening at Catharine Creek Marsh

Leave a Reply

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