Writing a function

Objective: write a for loop and understand it focuses on the iteration and not actions

  1. take 5 min to watch this video by Hadley Wickham, until 5 min.

being lazy, instead of you doing the work, rely on the work of someone else | Hadley Wickham

  1. write a R function that takes a vector as an argument and returns the number of odd numbers. For example, passing the vector c(1, 2, 3, 4) to the function odd_count should output:
odd_count(c(1, 2, 3, 4))
## [1] 2

1. The structure

  • Here we would like to introduce the use of different programming statements you learned, such as if, else if, else or a for loop.
  • Write, in human terms (or pseudocode), how the function could be constructed
  1. initialize a counter to zero
  2. for each element of the argument
  3. test if the element is an odd number
    • if yes -> increment the counter by 1
    • if no -> do nothing
  4. return the counter

2. Do it using R

Version 0, not a function

start by writing a piece of code that does the job, following the steps of your pseudocode

# test vector named a
a <- c(1, 2, 3, 4)
# initialized a scalar at 0
counter <- 0
# iterate through the values of a
for (i in a) {
  # if integer division by is not zero
  if (i %% 2 != 0) {
    # increment the counter by 1
    counter <- counter + 1
  }
}
# how much is counter?
counter
## [1] 2

Version 1

Convert your functional piece of code as a function named odd_count()

Tip

  • remenber that function arguments are then local to the function’s environmen
  • remember the modulo operation is a %% b in R and returns the reminder of the division of a/b. If b = 2 and the result is different from zero, then a is an odd number.
  • remember to test if a equals b using == and not =. a = b is an assignment leading a to take the value of b!
  • returning the expected value, should be placed outside the for loop
a <- c(1, 2, 3, 4)

odd_count <- function(x) {
  counter <- 0
  for (i in x) {
    if (i %% 2 != 0) {
      counter <- counter + 1
    }
  }
  return(counter)
}

odd_count(c(a))
## [1] 2

Version 2: Making a better function, sanity check.

What would happen if we provide some characters, or a matrix, as an input to odd_count()? Is your function able to deal with such an input?

No, but it should. Add a test before doing anything else to check if numbers are provided to the function.

Tip

the stop("Warning! some conditions are not fulfilled to continue bla bla \n") should help. It stops the execution and print the chosen message
a <- c(1, 2, 3, 4)

odd_count <- function(x) {
  # if the input is not numeric, we stop the code execution
  # of note, the call. = FALSE is to avoid printing the code line, makes the error more explicit
  if (!is.numeric(x)) stop("input not numeric!", call. = FALSE)
  counter <- 0
  for (i in x) {
    if (i %% 2 != 0) {
      counter <- counter + 1
    }
  }
  return(counter)
}

odd_count(c(a, "a"))
## Error: input not numeric!

Version 3: Customize the function, multiple arguments.

Now we would like to count only unique numbers that are odd. Change the function so that it takes a second argument uniq changing this behaviour: a boolean (TRUE/FALSE).

When set to TRUE, only unique numbers should be considered (see the function unique()). When set to FALSE (which we would like to be the default value), the result should be equal to version 2.

Chart of outcome

Please check that your 3 versions have the expected behaviour using the following table:

input version 1 version 2 version 3 (unique = TRUE)
c(0, 1) 1 1 1
c(0, 1, 2, ‘A’) nasty error input not numeric! input not numeric!
c(0, 1, 2, 3, 5) 3 3 3
c(0, 1, 2, 3, 5, 3) 4 4 3
a <- c(1, 2, 3, 4)

odd_count <- function(x, uniq = TRUE) {
  if (!is.numeric(x)) stop("input not numeric!", call. = FALSE)
  counter <- 0
  if (uniq) x <- unique(x)
  for (i in x) {
    if (i %% 2 != 0) {
      counter <- counter + 1
    }
  }
  return(counter)
}

odd_count(c(a, 3), uniq = FALSE)
## [1] 3
odd_count(c(a, 3), uniq = TRUE)
## [1] 2
odd_count(c(a, 3))
## [1] 2

Version 4, vectorized

Now that the for loop was a pain to set-up, we can use what R is good at: vectorization

  • replace all the for loop code that one line of code using vectorization

Tip

remenber that TRUE is 1 and FALSE is 0, so summing a logical vector truly helps here.
a <- c(1, 2, 3, 4)

odd_count <- function(x, uniq = TRUE) {
  if (!is.numeric(x)) stop("input not numeric!", call. = FALSE)
  if (uniq) x <- unique(x)
  # the vector is integer divided by 2, the iteration is transparent
  # then logical test: is different from 0
  # finally sum up the logical vector, TRUE is 1, FALSE is 0
  sum(x %% 2 != 0)
}

odd_count(c(a, 3), uniq = FALSE)
## [1] 3
odd_count(c(a, 3))
## [1] 2