# 16R Practice: Writing Functions and Packages

## Learning Objectives

• Practice creating functions
• Practice testing functions
• Practice adding error messages to functions
• Practice deploying a package and organizing custom functions within it
Acknowledgements

These exercises are adapted from Allison Horst’s EDS 221: Scientific Programming Essentials Course for the Bren School’s Master of Environmental Data Science program.

## 16.1 Exercise: R Functions

Setup
1. Make sure you’re in the right project (`training_{USERNAME}`) and use the Git workflow by `Pull`ing to check for any changes in the remote repository (aka repository on GitHub).

2. Create a new Quarto Document.

1. Title it “R Practice: Functions”.
2. Save the file and name it “r-practice-functions”.
3. Organize your Quarto Document and folders in a meaningful way. Organization is personal - so this is up to you! Consider the different ways we’ve organized previous files using: headers, bold text, naming code chunks, comments in code chunks. Consider what directories (folders) we’ve used or talked about to organize these files. What is most important is organizing and documenting the file so that your future self (or if you share this file with others!) understands it as well as your current self does right now.

4. Use the Git workflow. After you’ve set up your project and uploaded your data go through the workflow: `Stage (add) -> Commit -> Pull -> Push`

### 16.1.1 R Functions Warm Up

We’re going to start by creating some simple functions. Recall that the anatomy of a function is the same for all functions and each one contains:

• a function name,
• arguments that allow a user to specify inputs,
• and body of commands and outputs enclosed within a set of curly braces `{}`
Question 1

Create a function called `double_it()` that doubles any value input value. Then try out the function, are the values returned what you expect?

``````# create function #
double_it <- function(x) {
print(2 * x)
}

# try it out #
# explicit notation
double_it(x = 24)

# non explicit notation
double_it(24)``````
Question 2

Write a function called `exclaim_age()` that returns the statement “I am ___ years old!”, where the blank is entered by the user as argument age.

Then try out the function, are the values returned what you expect?

``````# write function #
exclaim_age <- function(age) {
print(paste("I am", age, "years old!"))
}

# try it out #
# explicit notation
exclaim_age(age = 12)

# non explicit notation
exclaim_age(12)``````

### 16.1.2 Functions with Conditionals

Question 3

Consider the function called `find_max()`:

1. Talk to your neighbor about what this function does and what you expect the output would be.
2. Run the function with some values. Is it running how you expect?
3. Run the function again, but this time use the function in an arithmetic expression. Is the output what you expect?
``````# "find_max() function" #
find_max <- function(value_1, value_2) {

if (value_1 > value_2) {
return(value_1)
}
else if (value_2 > value_1) {
return(value_2)
}
}``````
``````# example using `find_max()` in an arithmetic expression #
# we expect the answer to be 20
5 * find_max(4, 2)``````

### 16.1.3 Adding Error or Warning Messages

Question 4 Setup

Let’s continue to test the `find_max()` function and make sure it runs appropriately in the following function calls in Questions 4a, 4b, and 4c.

Question 4a

Run `find_max(4, 2, 5)` in the Console.

What happens? What kind of message appears? Is it sufficient? If not, consider adding a warning or error message using `warning()` or `stop()`. Remember, use `?function` to access the Help page. Add additional logic to the function, as needed.

When you run `find_max(4, 2, 5)`, the following error message appears:

``Error in find_max(4, 2, 5) : unused argument (5)``

This is an error message that is automatically created by R since our function only requires two parameters. This is a sufficient error message.

Question 4b

Run `find_max(4, 4)` in the Console.

What happens? What kind of message appears? Is it sufficient? If not, consider adding a warning or error message using `warning()` or `stop()`. Remember, use `?function` to access the Help page. Add additional logic to the function, as needed.

Hint 4b

When you run `find_max(4, 4)`, no message appears and the function is sent to the Console, but no value is either returned or printed.

To account for this scenario, add an `if()` statement to the beginning of the function, and then use either `warning()` or `stop()`.

``````# `find_max()` function with error message
find_max <- function(value_1, value_2) {

if (value_1 == value_2) {
stop("Values must be different from each other.")
}

if (value_1 > value_2) {
return(value_1)
}
else if (value_2 > value_1) {
return(value_2)
}
}

# try it out #
# does the message appear as you expected?
find_max(4, 4)``````
Question 4c

Run `find_max(4, "cow")` in the Console.

What happens? What kind of message appears? Is it sufficient? If not, consider adding a warning or error message using `warning()` or `stop()`. Remember, use `?function` to access the Help page. Add additional logic to the function, as needed.

Hint 4c

When you run `find_max(4, "cow")`, the function runs as is and returns the value “cow”. This is not expected because these two values aren’t necessarily comparable since they’re different data types.

However, `find_max()` doesn’t know any better since this scenario hasn’t been defined in the body of the function yet.

To account for this scenario, add additional logic that checks the class of each argument before the function continues to execute.

The logical operator for OR is `|`. The not-equal-to operator is `!=`.

``````# `find_max()` function with error messages and checks
find_max <- function(value_1, value_2) {

# `|` is the logical OR operator
# `!=` is the not-equal-to operator
if (is.numeric(value_1) != TRUE | is.numeric(value_2) != TRUE) {
# alt expression: is.numeric(value_1) == FALSE | is.numeric(value_2) == FALSE
stop("Value must be a numeric type.")
}

if (value_1 == value_2) {
stop("Values must be different from each other.")
}

if (value_1 > value_2) {
return(value_1)
}
else if (value_2 > value_1) {
return(value_2)
}
}

# try it out #
# does the message appear as you expected?
find_max(4, "cow")
find_max("cow", 4)``````

## 16.2 Exercise: R Packages

Setup
1. Create a new project using the R Package template by selecting: `File -> New Project -> New Directory -> R Package -> Create Project`. In the R Package dialog box, type “aboutMe” in the “Package Name:” field. Or you can use `usethis::create_package("~/aboutMe")`.

2. Delete the files: `hello.R` in the `R` directory and `hello.Rd` in the `man` directory.

3. Add a license by running in the Console: `usethis::use_apache_license()`.

4. Turn the project into a Git repository. Recall the steps from Ch 6 Exercise 3: Setting up Git on an existing project. Follow the steps either using the `usethis` package or Command Line.

### 16.2.1 Add Functions to the `aboutMe` Package

Question 1

Create the following two functions for the `aboutMe` package:

2. Function calculates your age for the current year.
1. Hint: You can access the current year using `format(Sys.Date(), "%Y")`.
2. `Sys.Date()` returns the current date, and `format()` and `"%Y"` formats the date to just the year in the format `YYYY` (as opposed to `YY`).

Write each of these functions in their own scripts and save them to the `R` directory.

Note: If any of your functions have dependencies, make sure to add it to the `DESCRIPTION` file.

Question 2

Go back to each function script and add a Roxygen skeleton to document each function.

### 16.2.3 Test Age Function

Question 3

Use `usethis::use_testthat()` and `usethis::use_test("test-file-name")` to test the age function.

### 16.2.4 Test, Check, and Install `aboutMe`

Question 4

Use either the Build Tab or run the `devtools` functions `test()`, `check()`, and `install()` in the Console.

After installing, go to the Packages Tab in the Files Pane, search for the `aboutMe` package, and check that the documentation for your package looks as you expected. Try loading the package using `library()` and call some of the functions you created.

Save and Use Git

Save your work and use the Git workflow: `Stage (add) -> Commit -> Pull -> Push`