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.finder
Add 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