```{r, echo = FALSE, message = FALSE}
library(dplyr)
library(dasehr)
library(knitr)
library(stringr)
library(tidyr)
library(emo)
library(readr)
opts_chunk$set(comment = "")
```


## Writing your own functions

So far we've seen many functions, like `c()`, `class()`, `filter()`, `dim()` ...

**Why create your own functions?**

- Cut down on repetitive code (easier to fix things!)
- Organize code into manageable chunks
- Avoid running code unintentionally
- Use names that make sense to you


## Writing your own functions

The general syntax for a function is: 

```
function_name <- function(arg1, arg2, ...) {
 <function body>
}
```


## Writing your own functions

Here we will write a function that multiplies some number `x` by 2:

```{r}
div_100 <- function(x) x / 100
```

When you run the line of code above, you make it ready to use (no output yet!). Let's test it!

```{r comment=""}
div_100(x = 600)
```


## Writing your own functions: `{ }`

Adding the curly brackets - `{}` - allows you to use functions spanning multiple lines:

```{r comment=""}
div_100 <- function(x) {
  x / 100
}
div_100(x = 10)
```


## Writing your own functions: `return`

If we want something specific for the function's output, we use `return()`:

```{r comment=""}
div_100_plus_4 <- function(x) {
  output_int <- x / 100
  output <- output_int + 4
  return(output)
}
div_100_plus_4(x = 10)
```

<!-- ## Writing your own functions: print intermediate steps -->

<!--  - printed results do not stay around but can show what a function is doing -->
<!--  - returned results stay around -->
<!--  - can only return one result but can print many -->
<!--  - if `return` not called, last evaluated expression is returned -->
<!--  - `return` should be the last step (steps after may be skipped) -->

<!-- ## Adding print -->

<!-- ```{r comment=""} -->
<!-- div_100_plus_4 <- function(x) { -->
<!--   output_int <- x / 100 -->
<!--   output <- output_int + 4 -->
<!--   print(paste("div_100 result = ", output_int)) -->
<!--   return(output) -->
<!-- } -->

<!-- result <- div_100_plus_4(x = 10) -->
<!-- result -->
<!-- ``` -->


## Writing your own functions: multiple inputs

Functions can take multiple inputs:

```{r comment=""}
div_100_plus_y <- function(x, y) x / 100 + y
div_100_plus_y(x = 10, y = 3)
```


## Writing your own functions: multiple outputs

Functions can return a vector (or other object) with multiple outputs.

```{r comment=""}
x_and_y_plus_2 <- function(x, y) {
  output1 <- x + 2
  output2 <- y + 2

  return(c(output1, output2))
}
result <- x_and_y_plus_2(x = 10, y = 3)
result
```


## Writing your own functions: defaults

Functions can have "default" arguments. This lets us use the function without using an argument later:

```{r comment=""}
div_100_plus_y <- function(x = 10, y = 3) x / 100 + y
div_100_plus_y()
div_100_plus_y(x = 11, y = 4)
```


## Writing another simple function

Let's write a function, `sqdif`, that:

1. takes two numbers `x` and `y` with default values of 2 and 3.
2. takes the difference
3. squares this difference
4. then returns the final value 


## Writing another simple function

```{r comment=""}
sqdif <- function(x = 2, y = 3) (x - y)^2

sqdif()
sqdif(x = 10, y = 5)
sqdif(10, 5)
sqdif(11, 4)
```


## Writing your own functions: characters

Functions can have any kind of input. Here is a function with characters:

```{r comment=""}
loud <- function(word) {
  output <- rep(toupper(word), 5)
  return(output)
}
loud(word = "hooray!")
```


<!-- ## Functions for tibbles -->

<!-- We can use `filter(row_number() == n)` to extract a row of a tibble: -->

<!-- ```{r message=FALSE} -->
<!-- get_row <- function(dat, row) dat %>% filter(row_number() == row) -->

<!-- ces <- calenviroscreen -->
<!-- ces_1_8 <- ces %>% select(1:8) -->
<!-- ``` -->

<!-- ```{r} -->
<!-- get_row(dat = ces, row = 10) -->
<!-- get_row(dat = ces, row = 4) -->
<!-- ``` -->


## Functions for tibbles - curly braces{.codesmall}

```{r}
# get means and missing for a specific column
get_summary <- function(dataset, col_name) {
    dataset %>%  
    summarise(mean = mean(, na.rm = TRUE),
              na_count = sum(is.na()))
}
```

Examples:

```{r}
get_summary(calenviroscreen, CES4.0Score)
get_summary(haa5, perc_pop_exposed_to_exceedances)
```

## Summary

- Simple functions take the form:
  - `NEW_FUNCTION <- function(x, y){x + y}`
  - Can specify defaults like `function(x = 1, y = 2){x + y}`
  -`return` will provide a value as output
  - `print` will simply print the value on the screen but not save it


## Lab Part 1

🏠 [Class Website](https://daseh.org/)  

💻 [Lab](https://daseh.org/modules/Functions/lab/Functions_Lab.Rmd)


# Functions on multiple columns

## Using your custom functions: `sapply()`- a base R function

Now that you've made a function... You can "apply" functions easily with `sapply()`!

These functions take the form:

```   
sapply(<a vector, list, data frame>, some_function)
```

## CalEnviroScreen

This dataset was gathered by the California Office of Environmental Health Hazard Assessment. CalEnviroScreen ranks census tracts in California based on potential exposures to pollutants, adverse environmental conditions, socioeconomic factors and the prevalence of certain health conditions. Read more at https://calenviroscreen-oehha.hub.arcgis.com/.

```{r}
head(calenviroscreen)
```


## Using your custom functions: `sapply()`

`r emo::ji("rotating_light")` There are no parentheses on the functions! `r emo::ji("rotating_light")`

You can also pipe into your function.

```{r comment=""}
sapply(calenviroscreen, class) # also: calenviroscreen %>% sapply(class)
```


## Using your custom functions: `sapply()`

Use the `div_100` function we created earlier to convert 0-100 percentiles to proportions.

```{r}
calenviroscreen %>%
  select(ends_with("Pctl")) %>%
  sapply(div_100) %>%
  head()
```


## Using your custom functions "on the fly" to iterate

Also called "anonymous function".

```{r comment=""}
calenviroscreen %>%
  select(ends_with("Pctl")) %>%
  sapply(function(x) x / 100) %>%
  head()
```


## Anonymous functions: alternative syntax

```{r comment=""}
calenviroscreen %>%
  select(ends_with("Pctl")) %>%
  sapply(\(x) x / 100) %>%
  head()
```


# across

## Using functions in `mutate()` and `summarize()`

Already know how to use functions to modify columns using `mutate()` or calculate summary statistics using `summarize()`.

- Pesticides - pounds of selected active pesticide / square mile  
- Poverty - percent of population living below two times the federal poverty level  
- LowBirthWeight - Percent low birth weight

```{r}
calenviroscreen %>%
  summarize(max_pest = max(Pesticides, na.rm = T),
            max_pov = max(Poverty, na.rm = T),
            low_bw = max(LowBirthWeight, na.rm = T))
```


## Applying functions with `across` from `dplyr`

`across()` makes it easy to apply the same transformation to multiple columns. Usually used with `summarize()` or `mutate()`.

```
summarize(across(<columns>,function)) 
```
or
```
mutate(across(<columns>,function))
```

- List columns first : `.cols = `
- List function next: `.fns = `
- If there are arguments to a function (e.g., `na.rm = TRUE`), use an anonymous function.


## Applying functions with `across` from `dplyr`

Combining with `summarize()`

```{r warning=FALSE}
calenviroscreen %>%
  summarize(across(
    c(Pesticides, Poverty, LowBirthWeight),
    mean # no parentheses
  ))
```

## Applying functions with `across` from `dplyr`

Add anonymous function to include additional arguments (e.g., `na.rm = T`).

```{r warning=FALSE}
calenviroscreen %>%
  summarize(across(
    c(Pesticides, Poverty, LowBirthWeight),
    function(x) mean(x, na.rm = T)
  ))
```

## Applying functions with `across` from `dplyr`

Can use with other tidyverse functions like `group_by`!

```{r}
calenviroscreen %>%
  group_by(CaliforniaCounty) %>% 
  summarize(across(
    c(Pesticides, Poverty, LowBirthWeight),
    function(x) mean(x, na.rm = T)
  ))
```


## Applying functions with `across` from `dplyr`

Using different `tidyselect()` options (e.g., `starts_with()`, `ends_with()`, `contains()`)

```{r warning=FALSE}
calenviroscreen %>% 
  group_by(CaliforniaCounty) %>%
  summarize(across(contains("PM"), mean))
```


<!-- ## Applying functions with `across` from `dplyr`{.codesmall} -->

<!-- `mutate()` across to round across many columns at once! -->

<!-- ```{r} -->
<!-- calenviroscreen %>% -->
<!--   mutate(across( -->
<!--     where(is.numeric),  -->
<!--     function(x) round(x, digits = 0) -->
<!--   )) %>% select(7:13) -->
<!-- ``` -->


## Applying functions with `across` from `dplyr` {.smaller}

Combining with `mutate()` - the `replace_na` function

Here we will use the `yearly_co2_emissions` data from `dasehr`

```replace_na({data frame}, {list of values})```
or
```replace_na({vector}, {single value})```

```{r warning=FALSE, message=FALSE}
yearly_co2_emissions %>%
  select(country, starts_with("194")) %>%
  mutate(across(
    c(`1943`, `1944`, `1945`),
    function(x) replace_na(x, replace = 0)
  ))
```


<!-- ## Use custom functions within `mutate` and `across` -->

<!-- If your function needs to span more than one line, better to define it first before using inside `mutate()` and `across()`. -->

<!-- ```{r} -->
<!-- times1000 <- function(x) x * 1000 -->

<!-- airquality %>% -->
<!--   mutate(across( -->
<!--     everything(), -->
<!--     .fns  = times1000 -->
<!--   )) %>% -->
<!--   head(n = 2) -->

<!-- airquality %>% -->
<!--   mutate(across( -->
<!--     everything(), -->
<!--     .fns  = function(x) x * 1000 -->
<!--   )) %>% -->
<!--   head(n = 2) -->
<!-- ``` -->


## `purrr` package

Similar to across, `purrr` is a package that allows you to apply a function to multiple columns in a data frame or multiple data objects in a list.

While we won't get into `purrr` too much in this class, its a handy package for you to know about should you get into a situation where you have an irregular list you need to handle!

# Multiple Data Frames

## Multiple data frames {.smaller}

Lists help us work with multiple data frames

```{r}
AQ_list <- list(AQ1 = airquality, AQ2 = airquality, AQ3 = airquality)
str(AQ_list)
```


## Multiple data frames: `sapply`

```{r}
AQ_list %>% sapply(class)
AQ_list %>% sapply(nrow)
AQ_list %>% sapply(colMeans, na.rm = TRUE)
```


## Summary

- Apply your functions with `sapply(<a vector or list>, some_function)`
- Use `across()` to apply functions across multiple columns of data 
- Need to use `across` within `summarize()` or `mutate()`
- Can use `sapply` or `purrr` to work with multiple data frames within lists simultaneously


## Lab Part 2

🏠 [Class Website](https://daseh.org/)  

💻 [Lab](https://daseh.org/modules/Functions/lab/Functions_Lab.Rmd)

```{r, fig.alt="The End", out.width = "50%", echo = FALSE, fig.align='center'}
knitr::include_graphics(here::here("images/the-end-g23b994289_1280.jpg"))
```

Image by <a href="https://pixabay.com/users/geralt-9301/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=812226">Gerd Altmann</a> from <a href="https://pixabay.com//?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=812226">Pixabay</a>
