In this post, we will see how to make a calendar from scratch using R in ggplot2. I have been wanting to do this for a while. There is no better time to do this as we are at the end of the year 2022. The goal is to make a simple calender for the year 2022, with weekends and holidays marked clearly.
Let us get started. Calender is like a heatmap with tiles for each month. ggplot2’s geom_tile() is going to be the key function for making a calendar.
First, let us load the packages needed.
library(tidyverse)
Create data for Calendar
We need to create a dataframe containing 2023 dates, days, months, weeks, and schedules for filling the calender. Let us start with dates for the whole year 2023 using lubridate’s function
start_day <- as.Date("2023-01-01") end_day <- as.Date("2023-12-31") df <- tibble(date = seq(start_day, end_day, by="1 day"))
Checking the heads and tails of the dataframe to make sure we have the right data.
df %>% head() A tibble: 6 × 1 date <date> 1 2023-01-01 2 2023-01-02 3 2023-01-03 4 2023-01-04 5 2023-01-05 6 2023-01-06
df %>% tail() # A tibble: 6 × 1 date <date> 1 2023-12-26 2 2023-12-27 3 2023-12-28 4 2023-12-29 5 2023-12-30 6 2023-12-31
To add som holidays, let us specify the days for some common holidays in USA.
holidays <- c(as.Date("2023-01-02"), as.Date("2023-01-16"), as.Date("2023-06-19"), as.Date("2023-07-04"), as.Date("2023-09-04"), as.Date("2023-11-23"), as.Date("2023-11-24"), as.Date("2023-12-25"))
In the calendar we make, we will have days on x-axis, week number of a month on y-axis and dates of a month on the calendar. With the R package lubridate, we get most of what we want except for month of week. We need to calculate month of week accounting for the days. And thanks to Stack Overflow, we can compute month of week.
df %>% mutate(Year=lubridate::year(date), Month = lubridate::month(date, label=TRUE, abbr=FALSE), Day = lubridate::wday(date, label=TRUE), mday = lubridate::mday(date), Month_week = (5 + day(date) + wday(floor_date(date, 'month'))) %/% 7) %>% head() # A tibble: 6 × 6 date Year Month Day mday Month_week <date> <dbl> <ord> <ord> <int> <dbl> 1 2023-01-01 2023 January Sun 1 1 2 2023-01-02 2023 January Mon 2 1 3 2023-01-03 2023 January Tue 3 1 4 2023-01-04 2023 January Wed 4 1 5 2023-01-05 2023 January Thu 5 1 6 2023-01-06 2023 January Fri 6 1
Let us add holiday and weekend schedule to the dataframe as a column. Here we use case_when() function to create the column.
df %>% mutate(Year=lubridate::year(date), Month = lubridate::month(date, label=TRUE, abbr=FALSE), Day = lubridate::wday(date, label=TRUE), mday = lubridate::mday(date), Month_week = (5 + day(date) + wday(floor_date(date, 'month'))) %/% 7) %>% mutate(schedule = case_when( Day %in% c("Sat", "Sun") ~ "Week end", date %in% holidays ~ "Holiday", TRUE~ "Available")) %>% head() # A tibble: 6 × 7 date Year Month Day mday Month_week schedule <date> <dbl> <ord> <ord> <int> <dbl> <chr> 1 2023-01-01 2023 January Sun 1 1 Week end 2 2023-01-02 2023 January Mon 2 1 Holiday 3 2023-01-03 2023 January Tue 3 1 Available 4 2023-01-04 2023 January Wed 4 1 Available 5 2023-01-05 2023 January Thu 5 1 Available 6 2023-01-06 2023 January Fri 6 1 Available
Now we have all the data needed to make 2023 calendar.
calendar_df <- df %>% mutate(Year=lubridate::year(date), Month = lubridate::month(date, label=TRUE, abbr=FALSE), Day = lubridate::wday(date, label=TRUE), mday = lubridate::mday(date), Month_week = (5 + day(date) + wday(floor_date(date, 'month'))) %/% 7) %>% mutate(schedule = case_when( Day %in% c("Sat", "Sun") ~ "Week end", date %in% holidays ~ "Holiday", TRUE~ "Available")) calendar_df %>% head() # A tibble: 6 × 7 date Year Month Day mday Month_week schedule <date> <dbl> <ord> <ord> <int> <dbl> <chr> 1 2023-01-01 2023 January Sun 1 1 Week end 2 2023-01-02 2023 January Mon 2 1 Holiday 3 2023-01-03 2023 January Tue 3 1 Available 4 2023-01-04 2023 January Wed 4 1 Available 5 2023-01-05 2023 January Thu 5 1 Available 6 2023-01-06 2023 January Fri 6 1 Available
A basic calendar with geom_tile() in ggplot2
Here we start with a basic calendar using geom_tile() function in ggplot2. We used facet_wrap() with Month variable so we get a monthly calendar.
calendar_df %>% ggplot(aes(y=Month_week, x=Day))+ geom_tile(color="black")+ facet_wrap(~Month) ggsave("basic_calendar_with_ggplot2.png", width=12, height=8)
Highlighting holidays and weekend in calendar with ggplot2
Now we can use fill fill argument to add colors to differentiate weekend, weekday and holidays. We also specify the number of months in each row to be 3 with ncols argument to facet_wrap()
calendar_df %>% ggplot(aes(y=Month_week, x=Day, fill=schedule))+ geom_tile(color="black")+ facet_wrap(~Month, ncol=3) ggsave("basic_calendar_weekend_holidays_with_ggplot2.png", width=12, height=8)
Customising the calendar with geom_text() in ggplot2
Another useful feature of the calendar is to add the dates on the calendar. Here we use geom_text() function to annotate the calendar with dates.
calendar_df %>% ggplot(aes(y=Month_week, x=Day, fill=schedule))+ geom_tile(color="black")+ geom_text(aes(label=mday))+ facet_wrap(~Month, ncol=3) ggsave("basic_calendar_with_dates_ggplot2.png", width=12, height=8)
We can further customize the calendar by using classic ggplot2 theme.
calendar_df %>% ggplot(aes(y=Month_week, x=Day, fill=schedule))+ geom_tile(color="black")+ geom_text(aes(label=mday))+ facet_wrap(~Month, ncol=3)+ theme_classic(16) ggsave("basic_calendar_classic_theme_ggplot2.png", width=12, height=8)
A closer look at the week on the calendar shows it is in reverse order. We can change that y-axis ticks order using scale_y_reverse() function.
calendar_df %>% ggplot(aes(y=Month_week, x=Day, fill=schedule))+ geom_tile(color="black")+ geom_text(aes(label=mday))+ facet_wrap(~Month, ncol=3)+ theme_classic(16)+ scale_y_continuous(breaks = scales::pretty_breaks())+ scale_y_reverse()+ theme(legend.position="bottom") ggsave("customizing_calendar_ggplot2.png", width=9, height=12)
Manually specifying fill colors for calendar with ggplot2
Finally, let us manually change colors to fill the days of calendar and also add title and customize x and y-axis labels.
# The palette with grey: cal_palette <- c("#90EE90", "#56B4E9","#FF0000") calendar_df %>% ggplot(aes(y=Month_week, x=Day, fill=schedule))+ geom_tile(color="black")+ geom_text(aes(label=mday))+ facet_wrap(~Month, ncol=3)+ theme_classic(16)+ scale_y_continuous(breaks = scales::breaks_pretty(n=5))+ scale_y_reverse()+ theme(legend.position="bottom")+ scale_fill_discrete(name=NULL)+ scale_fill_manual(values=cal_palette)+ labs(title="2023 Calendar",y="Week", x=NULL) ggsave("make_calender_with_ggplot2.png", width=9, height=12)
1 comment
Comments are closed.