Functions: Answers

Warning

Make sure that you try the exercises yourself first before looking at the answers

Function Arguments

Look at the help file for the function mean().

How many arguments does the function have?

What types of vectors are accepted?

What is the default setting for dealing with NA values?

Three arguments (plus further arguments).

Numerical and logical vectors are accepted.

The default setting is to NOT remove NA (missing) values.

Use the function mean() to calculate the mean of the following values:

Note

note the NA and use named argument matching

c(1, 2, NA, 6)
mean(x = c(1, 2, NA, 6), na.rm = TRUE)
[1] 3

Do Q2 again but rearrange the arguments.

mean(na.rm = TRUE, x = c(1, 2, NA, 6))
[1] 3

Do Q2 again using positional matching.

mean(c(1, 2, NA, 6), 0, TRUE)
[1] 3

Determine the class of mean() using class().

class(mean)
[1] "function"

The class is function.

Determine the class of mean() using str().

str(mean)
function (x, ...)  

The class is function.

Determine the class of the value output in Q4 using class().

class(mean(c(1, 2, NA, 6), 0, TRUE))
[1] "numeric"

The class is numeric

Determine the class of the value output in Q4 using str().

str(mean(c(1, 2, NA, 6), 0, TRUE))
 num 3

The num means the class is numeric.

Function environment and scoping

For each of the following sets of commands, give the value that will be returned by the last command. Try to answer without using R.

w <- 5
f <- function(y) {
  return(w + y)
}
f(y = 2)
w <- 5
f <- function(y) {
  w <- 4
  return(w + y)
}
f(y = 2)
  1. This will return 7 because w is 5 and we are evaluating the function at y = 2

  2. This will return 6 because w is reassigned as 4 inside the function and we are evaluating the function at y = 2.

Among the variables w, d, and y, which are global to f() and which are local? What is the value of z when executing f(w)

w <- 2
f <- function(y) {
  d <- 3
  h <- function(z) {
    return(z + d)
  }
  return(y * h(y))
}

The object w is global to f() while d and y are local to f().

z is 2, because it takes the value of y when executing h(y) in function f(), which takes the value of global variable w when executing f(w)

Do the following in R:

  1. Try:
myFun1 <- function(a) {
  b <- 3
  myFun2(a)
}

myFun2 <- function(y) {
  return(y + a + b)
}

myFun1(10)

What happens?

  1. Now try:
a <- 1
b <- 2
myFun1(10)

What happens?

  1. We get an error message because a and b are local to myFun1 so the function myFun2 can’t find them in the global environment.

  2. We get get the value 13 because the values a and b are global so myFun2 can find them and use them in its commands.

if(), else, and ifelse() and Vectorization

Write a function called ‘evenOrOdd’ involving if and else that takes an argument x and returns “Even” or “Odd” depending on whether or not x is divisible by 2. (Do not use the ifelse() function).

evenOrOdd <- function(x) {
  if(x %% 2 == 0) {
    return("Even")
  } else {
    return("Odd")
  }
}

Is your function ‘evenOrOdd’ vectorized? Check by passing it the vector:

w <- c(3, 6, 6, 4, 7, 9, 11, 6)
evenOrOdd(w)
Error in if (x%%2 == 0) {: the condition has length > 1

An error is given, because x in if(x %% 2 == 0) is longer than 1.

Another way to determine if each element of a vector is even or odd is to use the ifelse() function, which serves as a vectorized version if and else. Use ifelse() to obtain “Even” or “Odd” for each element of w.

ifelse(w %% 2 == 0, "Even", "Odd")
[1] "Odd"  "Even" "Even" "Even" "Odd"  "Odd"  "Odd"  "Even"

Terminating a function with returns, errors, and warnings

The functions warning() and stop() are used to print a warning message and to stop the execution of the function call and print an error message. For example:

noNegMean <- function(x) {
  if(all(x < 0)) {
    stop("All values in x are negative")
  }
  
  if(any(x < 0)) {
    x[x < 0] <- 0
    warning("Negative values in x replaced by zero")
  }
  
  return(mean(x))
}

Copy the above code and then pass noNegMean() a vector containing some negative and some positive values. What happens?

noNegMean(c(-1,0,1))
Warning in noNegMean(c(-1, 0, 1)): Negative values in x replaced by zero
[1] 0.3333333

We get the warning message and it returned 0.3333, which is the average of c(0, 0, 1).

What happens when you pass noNegMean() a vector containing all negative values?

noNegMean(c(-1,-1,-1))
Error in noNegMean(c(-1, -1, -1)): All values in x are negative

We get the error message and nothing is returned.

Write a function ratio() that takes two arguments, x and y, and attempts to compute the ratio x/y.

If both x == 0 & y == 0, the function should stop and print an error message about dividing 0 by 0.

If y == 0 (but not x), the function should print a warning message about dividing by 0, and then return x/y (which will be Inf).

In all other cases, it should return x/y.

Test your ratio() function first using two nonzero values for x and y, then using a nonzero x but y = 0, and finally using x = 0 and y = 0.

ratio <- function(x,y) {
  if(x == 0 & y == 0) {
    stop("Cannot divide zero by zero.")
  }

  if(y == 0) {
    warning("Cannot divide by zero.")
  }

  ratio <- x/y
  return(ratio)
}
ratio(2,3)
[1] 0.6666667
ratio(0,0)
Error in ratio(0, 0): Cannot divide zero by zero.
ratio(1,0)
Warning in ratio(1, 0): Cannot divide by zero.
[1] Inf

looping using for() loops and the apply functions

Copy this is a function to determine if a number is a prime number:

isPrime <- function(num){
  if (num == 2) {
    return(TRUE)
  }
  if(num > 1) {
    for(i in 2:(num-1)) {
      if ((num %% i) == 0) {
        return(FALSE)
      }
    }
  } else {
    return(FALSE)
  }
  
  return(TRUE)
}

Copy this matrix for which we would like to check if a number is a prime number:

mat <- matrix(1:100, nrow=10)

Use the apply() function to calculate the prime number for each number in the matrix.

What numbers from 1 until 100 are prime numbers?

apply(mat, c(1,2), isPrime)
       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
 [1,] FALSE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE
 [2,]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [3,]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE
 [4,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [5,]  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [6,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [7,]  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE
 [8,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [9,] FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE FALSE
[10,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
mat[apply(mat, c(1,2), isPrime)]
 [1]  2  3  5  7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

Copy the following command to create a list containing two generations of the famous Kennedy family:

Kennedys <- list(
    JosephJr = character(0),
    John = c("Caroline", "JohnJr", "Patrick"),
    Rosemary = character(0),
    Kathleen = character(0),
    Eunice = c("RobertIII", "Maria", "Timothy", "Mark", "Anthony"),
    Patricia = c("Christopher", "Sydney", "Victoria", "Robin"),
    Robert = c("Kathleen", "JosephII", "RobertJr", "David", 
               "MaryC", "Michael", "MaryK", "Christopher", 
               "Matthew", "Douglas", "Rory"),
    Jean = c("Stephen", "William", "Amanda", "Kym"),
    Edward = c("Kara", "EdwardJr", "Patrick")
)

Use a for() loop to loop over the list of the first generation of Kennedys, keeping track of how many children each one has in a vector.

children <- NULL
for(i in Kennedys){
  children <- c(children, length(i))
}
children
[1]  0  3  0  0  5  4 11  4  3

Now, using the lapply() function, loop over the list of the first generation of Kennedys and keep track of how many children each Kennedy has. What is the class of the output?

result <- lapply(Kennedys, length)
result
$JosephJr
[1] 0

$John
[1] 3

$Rosemary
[1] 0

$Kathleen
[1] 0

$Eunice
[1] 5

$Patricia
[1] 4

$Robert
[1] 11

$Jean
[1] 4

$Edward
[1] 3
class(result)
[1] "list"

Answer Question 20 again using the sapply() function. What is the class of the output?

result <- sapply(Kennedys, length)
result
JosephJr     John Rosemary Kathleen   Eunice Patricia   Robert     Jean 
       0        3        0        0        5        4       11        4 
  Edward 
       3 
class(result)
[1] "integer"

Load the “diamonds” dataset from the ggplot2 package by running library(gglot2) and calculate the average price of diamonds by color and clarity using the tapply() function.

library(ggplot2)
tapply(diamonds$price, list(diamonds$color, diamonds$clarity), mean)
        I1      SI2      SI1      VS2      VS1     VVS2     VVS1       IF
D 3863.024 3931.101 2976.146 2587.226 3030.159 3351.128 2947.913 8307.370
E 3488.422 4173.826 3161.838 2750.942 2856.294 2499.674 2219.820 3668.506
F 3342.182 4472.625 3714.226 3756.795 3796.718 3475.513 2804.277 2750.836
G 3545.693 5021.684 3774.787 4416.256 4131.362 3845.283 2866.821 2558.034
H 4453.414 6099.895 5032.415 4722.414 3780.689 2649.067 1845.658 2287.870
I 4302.185 7002.649 5355.020 5690.506 4633.184 2968.233 2034.862 1994.937
J 5254.060 6520.958 5186.048 5311.059 4884.461 5142.397 4034.176 3363.882