R Functions
Functions
R has a large collection of built-in functions that are called like this:
function_name(arg1 = val1, arg2 = val2, ...)
Let’s try using sum(), which makes regular summary of numbers. Type su and hit Tab. A pop-up shows you possible completions. Specify sum() by typing more (a “m”) to disambiguate, or by using ↑/↓ arrows to select.
sum(1,2)
## [1] 3
How to create function
The default syntax for creating a function as follow:
variable <- function(arg1, arg2,...){
your expression/algorithm
}
Hello, World!
Now is the very cliché stuff...
If we did not include a "Hello, World!" this would not be a serious and sleepy programming class.
hello <- function(){
print("Hello, World!")
}
hello()
## [1] "Hello, World!"
Name masking
You should have no problem predicting the output.
f <- function() {
x <- 1
y <- 2
c(x, y)
}
f()
## [1] 1 2
If a name isn’t defined inside a function, R will look one level up.
x <- 2
g <- function() {
y <- 1
c(x, y)
}
g()
## [1] 2 1
Function inside another function
x <- 1
h <- function() {
y <- 2
i <- function() {
z <- 3
c(x, y, z)
}
i()
}
h()
## [1] 1 2 3
Functios created by other function
j <- function(x) {
y <- 2
function() {
c(x, y)
}
}
k <- j(1)
k()
## [1] 1 2
Functions vs Variables
The same principles apply regardless of the type of associated value — finding functions works exactly the same way as finding variables:
l <- function(x)
x + 1
m <- function() {
l <- function(x)
x * 2
l(10)
}
m()
## [1] 20
If you are using a name in a context where it’s obvious that you want a function (e.g.,f(3)
), R will ignore objects that are not functions while it is searching.
n <- function(x)
x / 2
o <- function() {
n <- 10
n(n)
}
o()
## [1] 5
Exercise
What does the following function return? Make a prediction before running the code yourself.
f <- function(x) { f <- function(x) { f <- function(x) { x ^ 2 } f(x) + 1 } f(x) * 2 } f(10)
Answer:
## [1] 202
Every operation is a function call
“To understand computations in R, two slogans are helpful:
- Everything that exists is an object.
- Everything that happens is a function call.”
- John Chambers
Say a +
operation between variable x
and y
, we can do this:
x <- 1
y <- 2
x+y
## [1] 3
Then if we set the +
operator into a regular funciton format:
`+`(x,y)
## [1] 3
Another example of for
for (i in 1:2) print(i)
## [1] 1
## [1] 2
Then again for
as a regular funciton:
`for`(i, 1:2, print(i))
## [1] 1
## [1] 2
Anonymous functions
Anonymouse functions shows you a side of functions that you might not have known about: you can use functions without giving them a name.
In R, functions are objects. They aren’t automatically bound to a name. Unlike other languages (e.g., Python), R doesn’t have a special syntax for creating a named function.
Generally, you use the regular assignment operator to give it a name when you create a function in R. If you choose not to give the function a name, you get an anonymous function.
The following code chunk does not call the function, but it only return the function itself.
function(x)3()
function(x)3()
You can call an anonymous function without giving it a name. You have to use the parenthesis in two ways: first, to call a function, and second to make it clear that you want to call the anonymous function itself, as opposed to calling a (possibly invalid) function inside the anonymous function:
With appropriate parenthesis, the function is called:
(function(x) 3)()
## [1] 3
Return Values
Functions are generally used for computing some value, so they need a mechanism to supply that value back to the caller.
# first build it without an explicit return
double.num <- function(x) {
x * 2
}
double.num(5)
## [1] 10
# now build it with an explicit return
double.num <- function(x) {
return(x * 2)
}
double.num(5)
## [1] 10
double.num <- function(x) {
x * 3
print("hello")
x * 2
}
double.num(5)
## [1] "hello"
## [1] 10
double.num <- function(x) {
return(x * 2)
print("hello")
return(3)
}
double.num(5)
## [1] 10
Control Statments
Control statements allow us to control the flow of our programming and cause different
things to happen, depending on the values of tests. The main control statements are
if
, else
, ifelse
and switch
.
Create one function with if and else
Let's try to create a function to demonstrate if the variable is equal to 1
if.one <- function(x){
if(x==1){
print("True")
}else{
print("False")
}
}
if.one(1)
if.one(2)
## [1] "True"
## [1] "False"
Create one function with ifelse
Let's try to create a function to demonstrate if the variable is equal to 1
if.one <- function(x){
ifelse(x==1, "TRUE","FALSE")
}
if.one(1)
if.one(2)
## [1] "TRUE"
## [1] "FALSE"
Create one function with switch
If we have multiple cases to check, writing else if repeatedly can be cumbersome and inefficient. This is where switch is most useful.
multipleCases <- function(x){
switch(x,
a="first",
b="second",
z="last",
c="third",
d="other")
}
multipleCases("a")
Special argument ...
There is a special argument called ... . This argument will match any arguments not otherwise matched, and can be easily passed on to other functions.
Example 1
f <- function(a,b,c) {
data.frame(a,b,c)
}
f(a = 1, b = 2, c = 3)
## a b c
## 1 1 2 3
Example 2
f <- function(...) {
data.frame(...)
}
f(a = 1, b = 2, c = 3, d = 5, e = 7)
## a b c d e
## 1 1 2 3 5 7
Tips
Try press Alt-Shift-K to see the shortcuts in R Studio.
for
Loops
The most commonly used loop is the for loop.
for(i in 1:3){
print(i)
}
## [1] 1
## [1] 2
## [1] 3
Let's build up a automatic for
loop to count the number of letters for fruit names.
# build a vector holding fruit names
fruit <- c("apple", "banana", "pomegranate")
# make a variable to hold their lengths, with all NA to start
fruitLength <- rep(NA, length(fruit))
fruitLength
## [1] NA NA NA
# give it names
names(fruitLength) <- fruit
fruitLength
## apple banana pomegranate
## NA NA NA
for (i in fruit){
fruitLength[i] <- nchar(i)
}
fruitLength
## apple banana pomegranate
## 5 6 11
Apply Family
Built into R is the apply
function and all of its common relatives such as lapply
, sapply
and mapply
. Each has its quirks and necessities and is best used in different situations.
apply
theMatrix <- matrix(1:9, nrow=3)
# sum the rows
apply(theMatrix, 1, sum)
## [1] 12 15 18
# sum the columns
apply(theMatrix, 2, sum)
## [1] 6 15 24
Notice that this could alternatively be accomplished using the built-in rowSums
and colSums
functions, yielding the same results.
Sum up the row values.
rowSums(theMatrix)
## [1] 12 15 18
Sum up the column values.
colSums(theMatrix)
## [1] 6 15 24
lapply
and sapply
Basic grammar:
lapply(x, FUN, ...)
sapply(x, FUN, ...)
lapply
lapply works by applying a function to each element of a list and returning the results as a list.
Hadley Wickham, Advanced R
lapply
works by applying a function to each element of a list and returning the results as a list.
theList <- list(A=1:3, B=1:5, C=-1:1, D=2)
lapply(theList, sum)
## $A
## [1] 6
##
## $B
## [1] 15
##
## $C
## [1] 0
##
## $D
## [1] 2
sapply
sapply
is a user-friendly version and wrapper of lapply
by default returning a vector.
theList <- list(A=1:3, B=1:5, C=-1:1, D=2)
sapply(theList, sum)
## A B C D
## 6 15 0 2
mapply
Perhaps the most-overlooked-when-so-useful member of the apply
family is mapply
, which applies a function to each element of multiple lists.
firstList <-
list(A = matrix(1:16, 4),
B = matrix(1:16, 2),
C = data.frame(1:5))
secondList <-
list(A = matrix(1:16, 4),
B = matrix(1:16, 8),
C = data.frame(15:1))
# test element-by-element if they are identical
mapply(identical, firstList, secondList)
## A B C
## TRUE FALSE FALSE
Lets create one small function with mapply
:
simpleFunc <- function(x, y) {
nrow(x) + nrow(y)
}
mapply(simpleFunc, firstList, secondList)
## A B C
## 8 10 20
References:
[1] Hadley Wickham, Advanced R
[2] Hadley Wickham, Garrett Grolemund. R For Data Science.
[3] Yihui Xie, J. J. Allaire, Garrett Grolemund. R Markdown.
[4] Jared P. Lander, Writing R functions. (NEU library)