TL;DR

Functions allow us to write code that is more readable and easier to debug. This post will cover how to write your own functions and how to use them.

Introduction

We have seen in our previous posts that there are a wide range of functions within R that can perform an enormous range of tasks for us. However, as we begin to conduct our own analysis, we may find ourselves using the same blocks of code several times to perform similar tasks. Whilst we could copy and paste the code each time we want to use it (I will explain why this is not ideal in a minute), we could instead wrap our code in a function allowing us to run our code many times throughout our analysis, and any future analysis. Lets take a look at how we can do that!

Why functions?

Functions form a fundamental part of programming. They help our code to follow the DRY principle (Don’t Repeat Yourself). This principle aims to reduce code repetition making programs more succinct, easier to read and easier to debug. Functions do this by extracting a chunk of code, that performs a specific role, out of your main code which can then be used several times throughout your code. Lets look at an example below.

The basic syntax of functions

To write a function in R we follow the same syntax as when we create a normal object in R alongside the function keyword.

print_ten <- function() { # initialise the function
  print(10) # write the code you wish the function to execute
}

The function above is called print_ten and we use the function keyword, followed by opening curly brackets, and then enter the code we want to run within these brackets. In our code we ask R to print the number 10 to the console. You will notice, when we create the function nothing is returned, for this to happen we need to run the function as follows.

#run the print_ten code
print_ten() 
## [1] 10

You can see that by using the function name, and a set of open brackets, we have called our function and the code within it has been executed and the result has been printed. However, this function is clearly not much use to anyone (and can be achieved using the print function in R anyway!) so lets look at a more useful function.

Extending functions to take arguments

Functions become increasingly useful when they can take arguments. Arguments are passed into a function each time the function is run and allows the code within the function to be adapted. This becomes particularly useful as the function can be written to be more generalisable and used in a greater variety of situations. Below we will create a basic addition function.

addition <- function(number1, number2) { # defining the variables to pass into the function
  total <- number1 + number2 # defining the variable total within the function 
  return(total) # returning the value of total
}

You can see above we use the function keyword but in rather than a pair of empty brackets, we put two arguments into the function: number1 and number2. Within our code, we can then use these terms along with the + symbol to add them together and assign them to the variable total. We then use the return function to return the value of total. It is good practice to use the return function in your function to make it clear what the function will return when run.

Below we run the addition function. You can see that we can provide any two numbers as arguements into the function and the function returns the sum of these.

#running the addition function with 1 and 3 as the variables
addition(1, 3) 
## [1] 4
#running the addition function with 6 and 8 as the variables
addition(6, 8) 
## [1] 14

This is really all you need to begin writing functions. The code inside the function can become as complex as necessary (see our post on batch glucose processing for an example of a moderately complex function) but these basics will get you a long way.

Loading a function

Whilst writing a function as we have above works well, when the code inside it becomes longer it can take up a large amount of space. Therefore, it is often useful to store large functions in a separate file and then load this file into your R workspace. This can be done as follows.

source("~/health_stack/content/post/program_making-functions/example_function.R/") 
#load source file stored at the location provided

Running this code, with the correct file location, will load the function into your global environment and it can then be used as above. Storing functions in this way can lead to your final analysis looking significantly neater. In a later post, we will cover how you can store multiple functions in a package, and the benefits of this.

Conclusions

Converting your code into functions can increase the readability, reduce repeatability and allow you to perform common applications with greater ease. Functions can be written inline with the code, or stored in separate files, to pull them out of your code all together.

Complete Code

print_ten <- function() { # initialise the function
  print(10) # write the code you wish the function to execute
}

print_ten() 
#run the print_ten code

addition <- function(number1, number2) { # defining the variables to pass into the function
  total <- number1 + number2 # defining the variable total within the function 
  return(total) # returning the value of total
}

addition(1, 3) 
#running the addition function with 1 and 3 as the variables
addition(6, 8) 
#running the addition function with 6 and 8 as the variables

source("~/health_stack/content/post/program_making-functions/example_function.R/") 
#load source file stored at the location provided