Writing Vector Functions

New Seating Chart

Tuesday, November 2

Today we will…

  • Reminder: Midterm Portfolio Meetings
  • New Material
    • Function Basics
    • Variable Scope + Environment
  • PA 7: Writing Functions

Midterm Portfolio Meetings

Midterm Portfolio Meetings

  • No in-person class on Thursday
  • Meetings scheduled between 8am and 11am will be in Dr. T’s office (25-105).
  • Meetings scheduled between 12pm and 3pm will be in our classroom (this room).

Functions

Why write functions?

Functions allow you to automate common tasks!

  • We’ve been using functions since Day 1, but when we write our own, we can customize them!
  • Have you found yourself copy-pasting code and only changing small parts?

Why write functions?

Writing functions has three big advantages over copy-paste:

  1. Your code is easier to read.
  2. To change your analysis, simply change one function.
  3. You avoid mistakes.

Function Basics

Function Syntax


Basic syntax of a function in R. The function 'func_name' is assigned using '<-' to 'function(func_arg1, func_arg2)'. The body of the function is enclosed in curly brackets. Inside the brackets, there is a placeholder comment labeled '# FUNCTION_BODY' and a 'return(func_value)' statement indicating the output of the function.

Function Syntax

Illustration of R function syntax. The image explains the parts of a function in R using labeled arrows and colors. At the top, the name 'func_name' is assigned using '<-' to a function. An arrow points to 'func_name' with the label 'assign the function a NAME.' The keyword 'function' is highlighted, with an arrow labeled 'indicate we are creating a function.' The parentheses contain 'func_arg1, func_arg2,' which are labeled as 'specify ARGUMENTS of the function.' The body of the function is placed between curly brackets and labeled 'write the BODY of the function between curly brackets.' Finally, the 'return(func_value)' statement is labeled 'return a value as the OUTPUT of the function.

A (very) Simple Function

Let’s define a function.

  • You run the code to define the function just once.
add_two <- function(x){
  x + 2
}


Let’s call the function!

add_two(5)
[1] 7

Naming: add_two

The name of the function is chosen by the author.

add_two <- function(x){
  x + 2
}

Function names have no inherent meaning.

The name you give to a function does not affect what the function does.

add_three <- function(x){
  x + 7
}


add_three(5)
[1] 12

Arguments

The argument(s) of the function are chosen by the author.

  • Arguments are how we pass external values into the function.
  • They are temporary variables that only exist inside the function body.
  • We give them general names:
    • x, y, z – vectors
    • df – data frame
    • i, j – indices


add_two <- function(x){
  x + 2
}

Arguments

If we supply a default value when defining the function, the argument is optional when calling the function.

add_something <- function(x, something = 2){
  return(x + something)
}

If a value is not supplied, something defaults to 2.

add_something(x = 5)
[1] 7
add_something(x = 5, something = 6)
[1] 11

If we do not supply a default value when defining the function, the argument is required when calling the function.

add_something <- function(x, something){
  x + something
}

add_something(x = 2)
Error in add_something(x = 2): argument "something" is missing, with no default

Body: { }

The body of the function is where the action happens.

  • The body must be specified within a set of curly brackets.
  • The code in the body will be executed (in order) whenever the function is called.
add_two <- function(x){
  x + 2
}

Output: return()

Your function will give back what would normally print out

add_two <- function(x){
  x + 2
}


7 + 2
[1] 9
add_two(7)
[1] 9


…but it’s better to be explicit and use return()*.

add_two <- function(x){
  return(x + 2)
}

Function Style – Using return()s


# Good
find_abs <- function(x) {
  if (x > 0) {
    return(x)
  }
  x * -1
}
# Bad
add_two <- function(x, y) {
  return(x + y)
}

I tend to disagree…

I prefer to use return() statements because it saves me from accidentally writing a function that outputs nothing.

Output: return()

If you need to return more than one object from a function, wrap those objects in a list.

min_max <- function(x){
  lowest <- min(x)
  highest <- max(x)
  return(list(lowest, highest))
}

vec <- c(346,
         547,
         865, 
         112, 
         58)

min_max(vec)
[[1]]
[1] 58

[[2]]
[1] 865

Input Validation

When a function requires an input of a specific data type, check that the supplied argument is valid.

add_something <- function(x, something){
  stopifnot(is.numeric(x))
  return(x + something)
}

add_something(x = "statistics", something = 5)
Error in add_something(x = "statistics", something = 5): is.numeric(x) is not TRUE
add_something <- function(x, something){
  if(!is.numeric(x)){
    stop("Please provide a numeric input for the x argument.")
  }
  return(x + something)
}

add_something(x = "statistics", something = 5)
Error in add_something(x = "statistics", something = 5): Please provide a numeric input for the x argument.

How would you modify the previous code to validate both x and something?

Meaning, the function should check if both x and something are numeric.

Multiple Validations

add_something <- function(x, something){
  if(!is.numeric(x) | !is.numeric(something)){
    stop("Please provide numeric inputs for both arguments.")
  }
  return(x + something)
}

add_something(x = 2, something = "R")
Error in add_something(x = 2, something = "R"): Please provide numeric inputs for both arguments.
add_something <- function(x, something){
  stopifnot(is.numeric(x), is.numeric(something))
  return(x + something)
}

add_something(x = 2, something = "R")
Error in add_something(x = 2, something = "R"): is.numeric(something) is not TRUE

Variable Scope + Environment

Variable Scope

The location (environment) in which we can find and access a variable is called its scope.

  • We need to think about the scope of variables when we write functions.
  • What variables can we access inside a function?
  • What variables can we access outside a function?

Global Environment

  • The top right pane of Rstudio shows you the global environment.
    • This is the current state of all objects you have created.
    • These objects can be accessed anywhere.

A screenshot of the Environment tab in the RStudio environment, which displays the set of objects created by the user that are stored in the global environment and can be used for analysis. This is where we've see the datasets we read in stored!

Function Environment

  • The code inside a function executes in the function environment.
    • Function arguments and any variables created inside the function only exist inside the function.
      • They disappear when the function code is complete.
  • What happens in the function environment does not affect things in the global environment.

Function Environment

We cannot access variables created inside a function outside of the function.

add_two <- function(x) {
  my_result <- x + 2
  return(my_result)
}


add_two(9)
[1] 11
my_result
Error in eval(expr, envir, enclos): object 'my_result' not found

Name Masking

Name masking occurs when an object in the function environment has the same name as an object in the global environment.

add_two <- function(x) {
  my_result <- x + 2
  return(my_result)
}
my_result <- 2000


The my_result created inside the function is different from the my_result created outside.

add_two(5)
[1] 7
my_result
[1] 2000

Dynamic Lookup

Functions look for objects FIRST in the function environment and SECOND in the global environment.

  • If the object doesn’t exist in either, the code will give an error.
add_two <- function() {
  return(x + 2)
}

add_two()
Error in add_two(): object 'x' not found
  • If it doesn’t exist in the function environment, then it will look in the global environment
x <- 10

add_two()
[1] 12

It is not good practice to rely on global environment objects inside a function!

Debugging

A cartoon of a fuzzy round monster face showing 10 different emotions experienced during the process of debugging code. The progression goes from (1) 'I got this' - looking determined and optimistic; (2) 'Huh. Really thought that was it.' - looking a bit baffled; (3) '...' - looking up at the ceiling in thought; (4) 'Fine. Restarting.' - looking a bit annoyed; (5) 'OH WTF.' Looking very frazzled and frustrated; (6) 'Zombie meltdown.' - looking like a full meltdown; (7) (blank) - sleeping; (8) 'A NEW HOPE!' - a happy looking monster with a lightbulb above; (9) 'insert awesome theme song' - looking determined and typing away; (10) 'I love coding' - arms raised in victory with a big smile, with confetti falling.

Image by Allison Horst

Debugging

You will make mistakes (create bugs) when coding.

  • Unfortunately, it becomes more and more complicated to debug your code as your code gets more sophisticated.
  • This is especially true with functions!

Debugging Strategies

When you have a concept that you want to turn into a function…

  1. Write a simple example of the code without the function framework.

  2. Generalize the example by assigning variables.

  3. Write the code into a function.

  4. Call the function on the desired arguments

This structure allows you to address issues as you go.

An Example

Write a function called find_car_make() that takes in the name of a car and returns the “make” of the car (the company that created it).

  • find_car_make("Toyota Camry") should return “Toyota”.
  • find_car_make("Ford Anglica") should return “Ford”.

An Example

make <- word(string = "Toyota Camry",
             start = 1, 
             end = 1)
make
[1] "Toyota"
make <- word(string = "Ford Anglica",
             start = 1, 
             end = 1)
make
[1] "Ford"
car_name <- "Toyota Camry"

make <- word(string = car_name, 
             start = 1, 
             end = 1)
make
[1] "Toyota"

Taking these examples, write a find_car_make() function that will output the maker / manufacturer of any car.

Once you’ve got a function written, can you add input validation to your function?

find_car_make()

find_car_make <- function(car_name){
  # Check if there is a space in the string (so there are separate parts)
  stopifnot(
    stringr::str_detect(car_name, pattern = "\\w")
            )
  # Grab the first word of the car name (assuming Toyota Camry style input)
  make <- word(string = car_name, 
               start = 1, 
               end = 1)
  
  return(make)
}


find_car_make("Toyota Camry")
[1] "Toyota"
find_car_make("Ford Anglica")
[1] "Ford"

PA 7: Writing Functions

PA 7

You will write several small functions, then use them to unscramble a message. Many of the functions have been started for you, but none of them are complete as is.

This activity will require knowledge of:

  • Summary functions
  • Function documentation (for optional arguments)
  • Function syntax
  • modulus division (and remainders)
  • if () & else if() statements
  • Using [] and logical values to extract elements of a vector
  • Negating logical statements



None of us have all these abilities. Each of us has some of these abilities.

Base R Resources

Every group should have a base R cheatsheet!

On the front middle:

  • Selecting Vectors can refresh your memory on using [] to extract elements of a vector

On the front right:

  • If Statements can help you write conditional checks / returns for your functions
  • Functions can help you remember the syntax for creating a function

A Base R Cheat Sheet with various R programming syntax and examples. It covers topics such as reading and writing data (e.g., read.csv, write.csv), accessing help with R functions (e.g., ?mean), using control structures like for loops and if statements, defining functions, vector operations, using libraries, and working with different data types like lists, matrices, and data frames. There are examples of matrix operations, conditions, statistical functions (e.g., mean, sd), and plotting basics (e.g., plot, hist). The cheat sheet includes tips on environment management and converting between data types.

Getting Started

The partner who lives the furthest from SLO starts as the Developer (typing and listening to instructions from the Coder)!

  • The Coder does not type.
    • The collaborative editing feature should allow you to track what is being typed.
  • The Developer only types what they are told to type.

Submission

Submit the name of the television show the six numbers are asssociated with.

  • Each person will input the full name of the TV show into the PA 7 quiz.
  • The person who last occupied the role of Developer will download and submit the PA-7-functions.html file for the group.
    • Only one submission per group!

Lab 7 & Challenge 7: Functions + Fish

A serene scene of the Blackfoot River in Montana, with a small raft carrying two people navigating the gentle current. The river winds through a landscape of rugged, rocky shores and lush, green pine forests. Rolling hills and distant mountains frame the background under a lightly clouded sky.

To do…

  • PA 7: Functions
    • Due Thursday, 11/4 by 12:10pm
  • Attend Your Midterm Portfolio Meeting
  • Lab 7: Functions + Fish
    • Due Sunday, 11/10 at 11:59pm.