Day 5 - Functions

Spring 2023

Smith College

Overview

Timeline

  • Function Fundamentals
  • Function Creation

Goal

Review functions and how they work.

Function Fundamentals

Function Examples

You’ve probably used several functions so far:

  • mean()
  • sum()
  • table()
  • read.csv()
  • skim()
  • ifelse()
  • ggplot()
  • pivot_wider()

Anatomy of a Function





object <- function(arguments)

Anatomy of Function Creation

function_name <- function(arguments) {

    Step 1

    Step 2

    Step 3

    Etc…

}

ifelse

```{r}
ifelse
```
function (test, yes, no) 
{
    if (is.atomic(test)) {
        if (typeof(test) != "logical") 
            storage.mode(test) <- "logical"
        if (length(test) == 1 && is.null(attributes(test))) {
            if (is.na(test)) 
                return(NA)
            else if (test) {
                if (length(yes) == 1) {
                  yat <- attributes(yes)
                  if (is.null(yat) || (is.function(yes) && identical(names(yat), 
                    "srcref"))) 
                    return(yes)
                }
            }
            else if (length(no) == 1) {
                nat <- attributes(no)
                if (is.null(nat) || (is.function(no) && identical(names(nat), 
                  "srcref"))) 
                  return(no)
            }
        }
    }
    else test <- if (isS4(test)) 
        methods::as(test, "logical")
    else as.logical(test)
    ans <- test
    len <- length(ans)
    ypos <- which(test)
    npos <- which(!test)
    if (length(ypos) > 0L) 
        ans[ypos] <- rep(yes, length.out = len)[ypos]
    if (length(npos) > 0L) 
        ans[npos] <- rep(no, length.out = len)[npos]
    ans
}
<bytecode: 0x00000186231467d8>
<environment: namespace:base>

Function Creation

Anatomy of Function Creation

function_name <- function(arguments) {

    Step 1

    Step 2

    Step 3

    Etc…

}

Universe in a Box

You should treat the inside of a function like a mini R universe.


You should only work on data passed to the inside of a function through the arguments.


You can call on objects in your global environment, but this is a very bad idea.

arguments

function(arguments) {

}

Our First Function

```{r}
#| output-location: column-fragment

say_me <- function(character_to_say) {
  
  # Step 1
  print("You told me to say:")
  
  # Step 2
  print(character_to_say)
  
  # Step 3
  print("I did it!")
}

say_me(character_to_say = "Hello World!")
```
[1] "You told me to say:"
[1] "Hello World!"
[1] "I did it!"

Functions with Outputs

```{r}
#| output-location: column-fragment

add_me <- function(x, y) {
  
  # Step 1
  result = x + y
  
  # Return Results
  return(result)
}

add_me(1, 5)
```
[1] 6

Argument Defaults

```{r}
increase_me <- function(x, y = 1) {
  
  # Step 1
  result = x + y
  
  # Return Results
  return(result)
}
```
```{r}
#| output-location: column-fragment

# Using default
print("Using default")
increase_me(1)
increase_me(6)
```
[1] "Using default"
[1] 2
[1] 7
```{r}
#| output-location: column-fragment

# Overwrite default
print("Overwrite default")
increase_me(2)
increase_me(2, 10)
```
[1] "Overwrite default"
[1] 3
[1] 12

Help Files

Function Names Matter

You can overwrite base R (or other package) functions if you aren’t careful.

```{r}
#| output-location: column-fragment

num_vec = c(1, 21, 6, 8, 34, 57, 2, 67)

mean(num_vec)
```
[1] 24.5
```{r}
#| output-location: column-fragment

mean = function(x){
  
  print("Your code is broken.")
  return(emo::ji("devil"))
  
}

mean(num_vec)
```
[1] "Your code is broken."
😈 

Be concise but descriptive.

Searching for Functions

R looks for functions in a specific order

```{r}
library("ggplot2")

sessionInfo()
```
R version 4.2.2 (2022-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.utf8 
[2] LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggplot2_3.3.6

loaded via a namespace (and not attached):
 [1] compiler_4.2.2   pillar_1.8.1     tools_4.2.2      digest_0.6.29   
 [5] lubridate_1.8.0  jsonlite_1.8.4   evaluate_0.16    lifecycle_1.0.1 
 [9] tibble_3.1.8     gtable_0.3.0     pkgconfig_2.0.3  rlang_1.0.4     
[13] DBI_1.1.3        cli_3.3.0        rstudioapi_0.13  yaml_2.3.5      
[17] xfun_0.36        emo_0.0.0.9000   fastmap_1.1.0    withr_2.5.0     
[21] stringr_1.4.1    dplyr_1.0.10     knitr_1.39       generics_0.1.3  
[25] vctrs_0.4.1      grid_4.2.2       tidyselect_1.1.2 glue_1.6.2      
[29] R6_2.5.1         fansi_1.0.3      rmarkdown_2.19   purrr_0.3.4     
[33] magrittr_2.0.3   scales_1.2.1     htmltools_0.5.3  assertthat_0.2.1
[37] colorspace_2.0-3 utf8_1.2.2       stringi_1.7.8    munsell_0.5.0   
[41] crayon_1.5.1    

Call Package Functions Explicitly

We can ask for functions from specific packages for clarity (or if we don’t want to load the whole package).

```{r}
#| output-location: column-fragment

mean(num_vec)
```
[1] "Your code is broken."
👿 

We do so using ::

```{r}
#| output-location: column-fragment

base::mean(num_vec)
```
[1] 24.5

You should pretty much always use :: when loading function from other packages when creating your own package.

The …

The dots allow you to pass anything down through a function.

```{r}
#| output-location: column-fragment

top5_cars = function(data, ...){
  
  top5 = data[order(data$disp, decreasing = TRUE), ]
  top5 = top5[1:5, ]
  
  plot(top5$mpg, top5$disp, ...)
  
}

top5_cars(mtcars, main = "Big Engines, Lower MPG")
```

main isn’t used in our function, but it is used by plot()

Capture the …

You can capture the … and pull out specific elements.

```{r}
#| output-location: column-fragment

top5_cars = function(data, ...){
  
  the_dots = list(...)
  
  top5 = data[order(data$disp, decreasing = TRUE), ]
  top5 = top5[1:5, ]
  
  plot(top5$mpg,
       top5$disp,
       ...,
       xlab = the_dots$labs[1],
       ylab = the_dots$labs[2])
  
}

top5_cars(mtcars, main = "Big Engines, Lower MPG",
          labs = c(
            "Engine Displacement",
            "Miles Per Gallon"
            ))
```

… and Named Arguments

Any argument after ... must be named.

data.frame(mtcars[1:26,], letters)
                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb letters
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4       a
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4       b
Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1       c
Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1       d
Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2       e
Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1       f
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4       g
Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2       h
Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2       i
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4       j
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4       k
Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3       l
Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3       m
Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3       n
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4       o
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4       p
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4       q
Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1       r
Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2       s
Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1       t
Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1       u
Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2       v
AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2       w
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4       x
Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2       y
Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1       z
data.frame(mtcars[1:26,], row.names = letters)
   mpg cyl  disp  hp drat    wt  qsec vs am gear carb
a 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
b 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
c 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
d 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
e 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
f 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
g 14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
h 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
i 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
j 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
k 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
l 16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
m 17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
n 15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
o 10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
p 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
q 14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
r 32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
s 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
t 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
u 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
v 15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
w 15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
x 13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
y 19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
z 27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1

Code Along

For Next Time