In R, everything is either an object or a function. We have experimented with data simulation and data creation, so we have created our own objects. However, we have not yet created our own functions. In this session, we will explore the basics of creating our own functions in R.
To create a function in R, you create a new function by assigning a
name to a function() call, and then defining the function
arguments and telling the function what to do.
The basic structure of a function is this:
function_name <- function(){
# stuff the function does goes in here
}
Note the two curly braces: “{” and “}” - these define the start and the end of the function and what it will do. You can include anything you want inside the function, including creating objects and using other functions.
Using the framework above, create a function named
hello.world that will print “Hello World” to the console
when run.
hello.world <- function(){
print("Hello World")
}
To run your function, you need to include the brackets at the end, just like any other function.
hello.world()
## [1] "Hello World"
The hello.world function above does not have any
arguments, but we can change that. When you define your function, you
can also define the number of arguments it takes, their names, and
whether they have default values.
function_name <- function(argument1 = default, argument2){
# stuff the function does goes in here
}
Make a function named printer which takes one argument
named input with the default value “need input”. Inside
your function, ask it to print input.
printer <- function(words = "need input"){
print(words)
}
Call your printer function without supplying an
argument. What happens?
printer()
## [1] "need input"
Now call printer, but this time give it some input for
the words argument.
printer(words = 'Hello World')
## [1] "Hello World"
printer(1234)
## [1] 1234
Our printer function is not very special or even very smart to make,
because the print() command already exists - we are just
reinventing the wheel. However, we can use functions to define
conditional logic suited to our needs.
For instance, let’s say we only wanted to print something if a
certain condition is met. We can do that by using if and
else statements inside of a function.
For example, the following function takes two arguments, checks to see if they are equal, and if they are equal, sends a message to the console indicating a match was found.
match.finder <- function(argument1, argument2){
if(argument1 == argument2){
message('match found')
}
}
Let’s try it out:
match.finder(1,2)
match.finder(1,1)
## match found
Note that the if statement inside
match.finder included its own set of curly brackets, which
define the scope of that particular if statement. At this
point it is crucial to note that the brackets must always match (open
and close) where you want them to match, or else your conditionals may
not work (or more likely, you function will just not run properly).
match.finderAdd a second if statement to match.finder
which will send a message to the console if a match is not found. Then
run the function with a match and without a match. You can put the
second if statement below the first, but make sure the
second if is not inside the curly brackets of the first
if.
match.finder <- function(argument1, argument2){
if(argument1 == argument2){
message('match found')
}
if (argument1 != argument2){
message('match not found')
}
}
Let’s try it out:
match.finder(1,2)
## match not found
match.finder(1,1)
## match found
The function above works but is pretty lazy and prone to breaking if
we expand it. Our function only has a binary decision to make (do the
arguments match, yes or no) so it is not necessary to have two separate
if statements. We can do the same thing above using
else. The benefit is that using else() means
we don’t need to specify the condition to be met - else()
will automatically trigger if the first if returns
FALSE. This is exactly how if_else() and
ifelse() worked, from our prior sessions.
# a much more elegant version of match.finder()
match.finder2 <- function(argument1, argument2){
if(argument1 == argument2){
message('match found')
} else {
message('match not found')
}
}
Let’s try it out:
match.finder2(1,2)
## match not found
match.finder2(1,1)
## match found
We can put if statements within if
statements to have conditional logic trigger based on other conditional
logic. For example, we could have a function first check if two numbers
match. If they match, the function then checks to see if their sum is
greater than 10, if so, it prints a message. (Note again how the curly
brackets must be nested.)
useless.function <- function(num1, num2) {
if(num1 == num2){
if(num1 + num2 > 10){
message('numbers match and sum is greater than ten')
}
}
}
Try it
useless.function(1,2)
useless.function(6,6)
## numbers match and sum is greater than ten
useless.function(100,200)
Our useless.function is pretty useless, and it only
tells us something if the numbers match! Add a new else
statement to useless.function which prints a message when
the numbers do not match. you should put your else
statement after the second last bracket.
useless.function <- function(num1, num2) {
if(num1 == num2){
if(num1 + num2 > 10) {
message('numbers match and sum is greater than ten')
}
} else {
message('numbers do not match')
}
}
useless.function(6,6)
## numbers match and sum is greater than ten
useless.function(4,5)
## numbers do not match
Now add an else statement so that the user is informed
when the numbers match but are not greater than ten. You will want to
add your else statement after the bracket that comes after
your message which triggers when the numbers are greater than ten.
useless.function <- function(num1, num2) {
if(num1 == num2){
if(num1 + num2 > 10) {
message('numbers match and sum is greater than ten')
} else {
message('numbers match but sum is not greater than ten')
}
} else {
message('numbers do not match')
}
}
useless.function(6,6)
## numbers match and sum is greater than ten
useless.function(4,4)
## numbers match but sum is not greater than ten
useless.function(4,5)
## numbers do not match
Finally, modify useless.function so that it also checks
whether a number is greater than ten when the numbers do not match. You
should have four possible messages:
# don't show.
useless.function <- function(num1, num2) {
if(num1 == num2){
if(num1 + num2 > 10) {
message('numbers match and sum is greater than ten')
} else {
message('numbers match but sum is not greater than ten')
}
} else {
if(num1 + num2 > 10) {
message('numbers do not match and sum is greater than ten')
} else {
message('numbers do not match and sum is not greater than ten')
}
}
}
useless.function(6,6)
## numbers match and sum is greater than ten
useless.function(4,4)
## numbers match but sum is not greater than ten
useless.function(100,120)
## numbers do not match and sum is greater than ten
useless.function(1,2)
## numbers do not match and sum is not greater than ten
Maybe we could perform the tests first, and then use those results in subsequent conditions? We can save local variables within our function, check it out:
# the space in message is just for readability
more.useless.function <- function(num1, num2){
check.match <- num1 == num2
check.sum <- ((num1 + num2) > 10)
message(check.match, ' ', check.sum)
}
more.useless.function(6,6)
## TRUE TRUE
more.useless.function(7,5)
## FALSE TRUE
Modify more.useless.function to output the same messages
as useless.function, but use the saved variables
check.match and check.sum within the
more.useless.function
# do not show
# the space in message is just for readability
more.useless.function <- function(num1, num2){
check.match <- num1 == num2
check.sum <- ((num1 + num2) > 10)
if(check.match == TRUE) {
if(check.sum == TRUE){
message('numbers match and sum is greater than ten')
} else {
message('numbers match and sum is not greater than ten')
}
} else {
if(check.sum == TRUE){
message('numbers do not match and sum is greater than ten')
} else {
message('numbers do not match and sum is not greater than ten')
}
}
}
test it:
more.useless.function(1,1)
## numbers match and sum is not greater than ten
more.useless.function(6,6)
## numbers match and sum is greater than ten
more.useless.function(7,9)
## numbers do not match and sum is greater than ten
more.useless.function(1,2)
## numbers do not match and sum is not greater than ten
Let’s add one more final thing here: else if. We have
been doing that above, although poorly. The else if allows
us to continue checking multiple conditions through a function, whereas
else only fires if one other condition is not met, and
anything afterwards does not work unless we nest a bunch more if
statements..
See below, we could add an many else if conditions as we
wanted to check for a variety of conditions.
else.if.example <- function(x){
if(x < 2){
message('x is less than 2')
} else if(x > 2){
message('x is greater than 2')
} else {
message('x must be equal to 2')
}
}
else.if.example(2)
## x must be equal to 2
Create a function named check.string.length with a
single argument input. Your function should produce three
different messages, depending on the input:
I want you to use length() to check the size, so that
you can check numbers and strings. To save us a headache, you should
include this as the first line within the function:
effect <- sapply(strsplit(as.character(input), ''), length)
The above line will allow us to count the length of the word when
split into separate characters - otherwise we would always get a length
of 1 for any word we put into the function. In the function, use
effect to control your conditional statements.
# don't show
check.length <- function(input){
effect <- sapply(strsplit(as.character(input), ''), length)
if(effect <= 5) {
message(effect, ': small effect')
} else if(effect > 5 & effect < 11){
message(effect, ': medium effect')
} else if(effect > 10){
message(effect, ': large effect')
}
}
Now, I want you to figure out how to get check.length to
output a small effect, a medium effect, and a small effect.
check.length(1)
## 1: small effect
check.length('cat')
## 3: small effect
check.length('banana')
## 6: medium effect
check.length(12345678987654321)
## 17: large effect
check.length('New Zealand')
## 11: large effect
check.length('United States of America')
## 24: large effect