Functions

greet <- function(name) {
  paste0("Hello ", name, "! Welcome to class.")
}

name_vec <- c("Clara", "Zhiwei", "Calvin", "Gaston", "Jayson", "Garv", "Isabel", "Laksha", "Hibah", "Asim", "Rabia", "Branden", "David", "Derek", "Karuna", "Claire", "Ash", "Terrell", "Colin", "Clarie", "Brian", "Ethan", "Daniel", "Wendy", "Jeremy", "Nicolas", "Sacha", "Alyssa")

for (name in name_vec) {
  print(greet(name))
  Sys.sleep(8)
}

While you’re waiting…

Question: What will the following code return?

x <- 10
y <- 20
g02 <- function() {
  x <- 1
  y <- 2
  c(x, y)
}
g02()

Defining a Function

Ex. colSums()

mat <- matrix(1:4, nrow = 2)
mat
     [,1] [,2]
[1,]    1    3
[2,]    2    4
colSums(mat)
[1] 3 7

How could I compute this without colSums()?

c(sum(mat[, 1]), sum(mat[, 2]))
[1] 3 7

I could generalize with a for-loop.

colsum_vec <- rep(0, ncol(mat))

for (coln in 1:ncol(mat)) {
  colsum_vec[coln] <- sum(mat[, coln])
}

colsum_vec
[1] 3 7

Why use colSums() instead?

  1. Make your code more readable
  2. Don’t Repeat Yourself (DRY)
  3. Focus on the variables, not the constants
  4. Easier to test, update

Many individual functions = wrench set








Custom function = socket wrench set.

Building a function

  (9/5) * temp_c + 32

Building a function

c_to_f <- function(temp_c) {
  (9/5) * temp_c + 32
}

Building a function

c_to_f <- function(temp_c) {
  (9/5) * temp_c + 32
}
  • Create a new function with function()
  • Define the function name, arguments, and expression
  • Creates a new object, c_to_f() in your environment
  • Last line will be returned (or use return())

Scoping

Question: What will the following code return?

x <- 10
y <- 20
g02 <- function() {
  x <- 1
  y <- 2
  c(x, y)
}
g02()
[1] 1 2

x <- 2
g03 <- function() {
  y <- 1
  c(x, y)
}
g03()
[1] 2 1
  • The environment within the function includes the environment “one level up”
  • Objects defined in the function environment mask those one level up
  • Best to encapsulate functions (not make them dependent on objects defined outside their environment / arguments)

Building a Die Roller

Example 1: Roll a Die

Task: return a numeric vector with the result of rolling a fair six-sided die.

  sample(x = 1:6, size = 1)

Example 1: Roll a Die

Task: return a numeric vector with the result of rolling a fair six-sided die.

roll_die <- function() {
  sample(x = 1:6, size = 1)
}

Question: Modify this function so it can

  1. work for arbitrary n-sided dice
  2. return more than 1 roll of the die
02:00

Example 1: Roll a Die

Task: return a numeric vector with the result of rolling a fair six-sided die.

roll_die <- function(n_sides = 6) {
  sample(x = 1:n_sides, size = 1)
}

Question: Modify this function so it can

  1. work for arbitrary n-sided dice
  2. return more than 1 roll of the die

Example 1: Roll a Die

Task: return a numeric vector with the result of rolling a fair six-sided die.

roll_die <- function(n_sides = 6, n_rolls = 1) {
  sample(x = 1:n_sides, size = n_rolls)
}

Question: Modify this function so it can

  1. work for arbitrary n-sided dice
  2. return more than 1 roll of the die

Building a Grade Calculator

Question

Write a function called course_grade() that takes assignment scores as inputs as outputs your current grade in the course.

02:30

course_grade <- function(ps, projects, quizzes, final_exam) {
  grade <- 0.05 * mean(ps) + 0.15 * mean(projects) + 
    0.5 * mean(quizzes) + 0.3 * final_exam
  return(grade)
}
ps <- c(1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1)
projects <- c(1, 1, 1, 1, 1, 1)
quizzes <- c(.8, .91, .98, .79, .85, .92)
final_exam <- .85
course_grade(ps, projects, quizzes, final_exam)
[1] 0.8889286

What if the weighting scheme were different?

Add flexibility through arguments.

course_grade <- function(ps, projects, quizzes, final_exam,
                         ps_w, projects_w, quizzes_w, final_exam_w) {
  grade <- ps_w * mean(ps) + projects_w * mean(projects) + 
    quizzes_w * mean(quizzes) + final_exam_w * final_exam
  return(grade)
}
course_grade(ps, projects, quizzes, final_exam,
             .05, .15, .5, .3)
[1] 0.8889286

What if we have no final_exam grade yet?

Save typing and head off errors by setting defaults.

course_grade <- function(ps = 0, projects = 0, quizzes = 0, final_exam = 0,
                         ps_w = .05, projects_w = .15, quizzes_w = .5, final_exam_w = .3) {
  grade <- ps_w * mean(ps) + projects_w * mean(projects) + 
    quizzes_w * mean(quizzes) + final_exam_w * final_exam
  return(grade)
}


How else can we improve this function?

Why write functions?

  1. Make your code more readable
  2. Don’t Repeat Yourself (DRY)
  3. Focus on the variables, not the constants
  4. Easier to test, update