How to make a Calender in R with ggplot2

How to make a calendar with ggplot2 in R
How to make a calendar with ggplot2 in R

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)
How to make a calendar with ggplot2

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)
Basic Calendar with holidays and weekend using geom_tile() in ggplot2

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)
Basic Calendars with dates in ggplot2

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)
Calendar with classic theme in ggplot2

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)

How to customize a calendar made with ggplot2

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)
How to make a calendar with ggplot2 in R

1 comment

Comments are closed.