Functions and conditionals

Practical Computing and Data Science Tools

Annoucements

  • Midterm 1 is next week, Oct 3, during lab time.
  • Lab 1 and 2 grades are posted on D2L.
  • Lab 3 grades will be released by the end of the week on D2L.

Agenda

  • More functions
  • Informative error messages
  • Conditional statements

Functions

Recall the pow() function from last time

pow <- function(x, v) {
  result <- x^v
  return(result)
}

pow() test

pow(5, 2)
[1] 25

pow() test

pow(5, 2)
[1] 25
pow(v = 5, x = 2)
[1] 32

pow() test

pow(5, 2)
[1] 25
pow(v = 5, x = 2)
[1] 32
pow("ten", "two")
Error in x^v: non-numeric argument to binary operator

pow() test

pow(5, 2)
[1] 25
pow(v = 5, x = 2)
[1] 32
pow("ten", "two")
Error in x^v: non-numeric argument to binary operator
pow(x = 5, z = 2)
Error in pow(x = 5, z = 2): unused argument (z = 2)

Errors

Consider the following errors:

pow("ten", "two")
Error in x^v: non-numeric argument to binary operator
pow(x = 5, z = 2)
Error in pow(x = 5, z = 2): unused argument (z = 2)
  • What do/don’t you like about each error?

  • Which error message is better? Why?

02:00

Informative Error Messages

Informative Error Messages

new_pow <- function(x, v) {
  stopifnot(
    "The x argument value should be numeric." = is.numeric(x),
    "The v argument value should be numeric." = is.numeric(v)
  )
  
  result <- x^v
  return(result)
}

Informative Error Messages

new_pow <- function(x, v) {
  stopifnot(
    "The x argument value should be numeric." = is.numeric(x),
    "The v argument value should be numeric." = is.numeric(v)
  )
  
  result <- x^v
  return(result)
}

Testing pow() vs. new_pow()

pow("ten", "two")
Error in x^v: non-numeric argument to binary operator
new_pow("ten", "two")
Error in new_pow("ten", "two"): The x argument value should be numeric.

Testing pow() vs. new_pow()

pow("ten", "two")
Error in x^v: non-numeric argument to binary operator
new_pow("ten", "two")
Error in new_pow("ten", "two"): The x argument value should be numeric.
new_pow(10, "two")
Error in new_pow(10, "two"): The v argument value should be numeric.

Testing pow() vs. new_pow()

pow("ten", "two")
Error in x^v: non-numeric argument to binary operator
new_pow("ten", "two")
Error in new_pow("ten", "two"): The x argument value should be numeric.
new_pow(10, "two")
Error in new_pow(10, "two"): The v argument value should be numeric.
new_pow(10, 2)
[1] 100

Difference function

Write a function, called diff(), that takes the difference (subtracts) its first and second arguments. If you have time, try to include any necessary informative error messages.

05:00

Difference function

A possible solution:

diff <- function(a, b) {
  result <- a - b
  return(result)
}

Difference function

A possible solution with informative error messages:

diff <- function(a, b) {
  stopifnot(
    "The a argument value should be numeric." = is.numeric(a),
    "The b argument value should be numeric." = is.numeric(b)
  )
  result <- a - b
  return(result)
}

Difference function

Testing:

diff(5, 2)
[1] 3
diff(2, 5)
[1] -3
diff("two", 5)
Error in diff("two", 5): The a argument value should be numeric.

Conditional statements

Conditional statements

  • Conditional statements allow for code to run based on whether a statement is true or false:
if (condition here is true) {
  run any code within these curly braces 
}

Conditional statements

  • Conditional statements allow for code to run based on whether a statement is true or false:
if (condition here is true) {
  run any code within these curly braces 
} else {
  run this code
}

Conditional statements

  • We can also use the else if condition to check multiple conditions:
if (condition 1 is true) {
  run any code within these curly braces 
} else if (some other condition is true) {
  run this code
} else {
  this code will run
}

Conditional statement example

What will this print?

species <- "doug-fir"

if (species == "doug-fir") {
  print("evergreen!")
} else {
  print(":-(")
}
00:30

Conditional statement example

What will this print?

species <- "doug-fir"

if (species == "doug-fir") {
  print("evergreen!")
} else {
  print(":-(")
}
[1] "evergreen!"

Conditional statement example

What will this print?

species <- "doug-fir"
dbh <- 22

if (species == "doug-fir") {
  print("evergreen!")
} else if (dbh > 20) {
  print("big tree!")
} else {
  print(":-(")
}
00:30

Conditional statement example

What will this print?

species <- "doug-fir"
dbh <- 22

if (species == "doug-fir") {
  print("evergreen!")
} else if (dbh > 20) {
  print("big tree!")
} else {
  print(":-(")
}
[1] "evergreen!"

Conditional statement example

What will this print?

species <- "doug-fir"
dbh <- 22

if (species == "doug-fir") {
  print("evergreen!")
}  
if (dbh > 20) {
  print("big tree!")
} else {
  print(":-(")
}
00:30

Conditional statement example

What will this print?

species <- "doug-fir"
dbh <- 22

if (species == "doug-fir") {
  print("evergreen!")
}  
[1] "evergreen!"
if (dbh > 20) {
  print("big tree!")
} else {
  print(":-(")
}
[1] "big tree!"

Conditional statements in a function

  • Let’s say you’re tired of R defaulting na.rm = FALSE when you take the mean of a variable. You could write a function with a different default!

Conditional statements in a function

  • Let’s say you’re tired of R defaulting na.rm = FALSE when you take the mean of a variable. You could write a function with a different default!

  • It might look something like this:

my_mean <- function(x, na.rm = TRUE) {
  if (na.rm) {
    x <- x[!is.na(x)]
  }
  
  average <- sum(x) / length(x)
  return(average)
}

Conditional statements in a function

  • Let’s say you’re tired of R defaulting na.rm = FALSE when you take the mean of a variable. You could write a function with a different default!

  • It might look something like this:

my_mean <- function(x, na.rm = TRUE) {
  if (na.rm) {
    x <- x[!is.na(x)]
  }
  
  average <- sum(x) / length(x)
  return(average)
}

Conditional statements in a function

nums <- c(1, 2, 3, NA, 5, 6)
my_mean(x = nums)
[1] 3.4
mean(x = nums)
[1] NA
mean(x = nums, na.rm = TRUE)
[1] 3.4

A cautious mean

my_mean <- function(x, na.rm = TRUE) {
  if (na.rm) {
    if (any(is.na(x))) {
      print("Watch out, you are removing NAs")
    } else {
      print("We didn't remove any NAs")
    }
    x <- x[!is.na(x)]
  } 
  
  average <- sum(x) / length(x)
  return(average)
}

A cautious mean

nums <- c(1, 2, 3, NA, 5, 6)
primes <- c(2, 3, 5, 7, 11, 13, 17, 19, 23)

What will

my_mean(x = nums)
my_mean(x = primes)

output?

01:00

A cautious mean

my_mean(x = nums)
[1] "Watch out, you are removing NAs"
[1] 3.4
my_mean(x = primes)
[1] "We didn't remove any NAs"
[1] 11.11111

A cautious mean

my_mean(x = nums)
[1] "Watch out, you are removing NAs"
[1] 3.4
my_mean(x = primes)
[1] "We didn't remove any NAs"
[1] 11.11111
# What about `prime_mean`? 
prime_mean <- my_mean(x = primes)
[1] "We didn't remove any NAs"

A cautious mean

my_mean(x = nums)
[1] "Watch out, you are removing NAs"
[1] 3.4
my_mean(x = primes)
[1] "We didn't remove any NAs"
[1] 11.11111
# What about `prime_mean`? 
prime_mean <- my_mean(x = primes)
[1] "We didn't remove any NAs"
prime_mean
[1] 11.11111

Activity

Write a function that takes two numbers as arguments (inputs) and does the following:

if the input’s sum is positive or 0, take the square root of their sum (recall the sqrt()) function; otherwise take the absolute value (recall abs()) of their sum. Throughout the function, print() some informative messages. Name this function abs_sqrt_sum().

05:00

Activity: possible solution

abs_sqrt_sum <- function(a, b) {
  if (a + b >= 0) {
    print("positive sum")
    return(sqrt(a + b))
  } else {
    print("negative sum")
    return(sqrt(abs(a + b)))
  }
}

Next time

  • Loops
  • More advanced functions
  • Review Quarto