Homework

Functions using dates and times

In this section you’ll be asked to create functions that use dates and times. We’ll be using the lubridate package to parse and manipulate date objects. You can read more about the package here.

  1. Write a function that takes a single argument (username, a person’s name) and prints a greeting “Good morning, username” or “Good afternoon, username” depending on the time of day.

    Before midday, the function should print “morning”; after midday it should print “afternoon”.

  • You’ll need to load the lubridate package.
  • You can get the current date and time with now().
  • You can extract the ‘hour’ part of a datetime object with the hour function.
  • You can check if an object is contained within a set using %in%. (For example: 5 %in% 1:10).
greeting <- function(username) {
  salutation <- if_else(hour(now()) %in% 0:11,
                        "Good morning,",
                        "Good afternoon,")
  paste(salutation, username)
}
  1. Update your function to ensure that the username argument a character. If not, stop the function and return an informative error message.

    Hint: see stop.

greeting <- function(username) {
  if (!is.character(username)) {
    stop("The provided username must be a string.")
  }

  salutation <- if_else(hour(now()) %in% 0:11,
                        "Good morning,",
                        "Good afternoon,")
  paste(salutation, username)
}
  1. Update your function so that it returns “Good evening, NAME” if the time between 6pm or and midnight.
# Option 1: multiple if-else statements:

greeting <- function(username) {
  if (!is.character(username)) {
    stop("The provided username must be a string.")
  }

  the_hour <- hour(now())
  if (the_hour %in% 0:11) {
    salutation <- "Good morning,"
  } else if (the_hour %in% 12:17) {
    salutation <- "Good afternoon"
  } else {
    salutation <- "Good evening"
  }

  paste(salutation, username)
}


# Option 2, better: using case_when
greeting <- function(username) {
  if (!is.character(username)) {
    stop("The provided username must be a string.")
  }
  the_hour <- hour(now())
  salutation <- case_when(the_hour %in% 0:11 ~ "Good morning,",
                          the_hour %in% 12:17 ~ "Good afternoon,",
                          TRUE ~ "Good evening,")
  paste(salutation, username)
}

Iteration

We often need to do things repeatedly, but we don’t want to type the same code over and over. In R, there are several ways to achieve this. But a key distinction can be made between looping and functional programming.

This exercise will ask you to complete a task using both approaches. If you’re unsure, have a look at the answers and check that you understand what’s going on – then have a go yourself.

The exercise is in two parts. In the first you’re asked to write a function that counts the number of days in a month, based on the traditional verse below:

Thirty days has September, April, June, and November, All the rest have thirty-one, Save February at twenty-eight, But leap year, coming once in four, February then has one day more.

You’ll then use this function (i) in a loop and (ii) using functional programming.

Write the function

Write a function that takes a month of the year as a string (e.g, “October”) and returns a vector containing the days of the month (e.g., 1, 2, 3, 4, ..., 31 for October). (Don’t worry about leap years!).

This is just one way of approaching this – many other solutions are possible.

enumerate_days <- function(month) {
  month <- tolower(month)
  if (!(month %in% tolower(month.name))) {
    stop("Month not recognised")
  }
  n_days <- dplyr::case_when(
    month %in% c("september", "april", "june", "november") ~ 30,
    month == "february" ~ 28,
    .default = 31
  )
  return(1:n_days)
}
  • Ensure that your function is case insensitive (i.e., it can handle upper and lower case months.
  • Using the stop function, add a check at the start of your function to confirm that the input is a valid month.

Iteration using loops

Using a loop, apply your function to each element of month.name. Store the result in a list. This list should contain 12 elements, each one a numeric vector containing the days of the month (1, 2, …, N).

year_loop <- list()
for (i in seq_along(month.name)) {
  year_loop[[i]] <- enumerate_days(month.name[i])
}

year_loop
[[1]]
 [1]  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] 26 27 28 29 30 31

[[2]]
 [1]  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] 26 27 28

[[3]]
 [1]  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] 26 27 28 29 30 31

[[4]]
 [1]  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] 26 27 28 29 30

[[5]]
 [1]  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] 26 27 28 29 30 31

[[6]]
 [1]  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] 26 27 28 29 30

[[7]]
 [1]  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] 26 27 28 29 30 31

[[8]]
 [1]  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] 26 27 28 29 30 31

[[9]]
 [1]  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] 26 27 28 29 30

[[10]]
 [1]  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] 26 27 28 29 30 31

[[11]]
 [1]  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] 26 27 28 29 30

[[12]]
 [1]  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] 26 27 28 29 30 31

Iteration using functional programming

Use lapply to apply your function to each element of month.name:

year_lapply <- lapply(month.name, enumerate_days)

You can also use map from the purrr package for this:

library(tidyverse)
year_map <- map(month.name, enumerate_days)

Use identical() to confirm that the two approaches (loops and functional programming) give you identical results.

identical(year_loop, year_lapply)
[1] TRUE
identical(year_loop, year_map)
[1] TRUE

We’ll learn more about purrr in Session 4.